Add files via upload

This commit is contained in:
Babyhamsta 2022-07-30 22:29:23 -05:00 committed by GitHub
parent 05d9edddb6
commit 752d09c8da
Signed by: DevComp
GPG key ID: 4AEE18F83AFDEB23
86 changed files with 8028 additions and 3170 deletions

View file

@ -0,0 +1,78 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Ast.h"
#include "lluz/Documentation.h"
#include <memory>
namespace lluz
{
struct Binding;
struct SourceModule;
struct Module;
struct TypeVar;
using TypeId = const TypeVar*;
using ScopePtr = std::shared_ptr<struct Scope>;
struct ExprOrLocal
{
AstExpr* getExpr()
{
return expr;
}
AstLocal* getLocal()
{
return local;
}
void setExpr(AstExpr* newExpr)
{
expr = newExpr;
local = nullptr;
}
void setLocal(AstLocal* newLocal)
{
local = newLocal;
expr = nullptr;
}
std::optional<Location> getLocation()
{
return expr ? expr->location : (local ? local->location : std::optional<Location>{});
}
std::optional<AstName> getName()
{
if (expr)
{
if (AstName name = getIdentifier(expr); name.value)
{
return name;
}
}
else if (local)
{
return local->name;
}
return std::nullopt;
}
private:
AstExpr* expr = nullptr;
AstLocal* local = nullptr;
};
std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Position pos);
AstNode* findNodeAtPosition(const SourceModule& source, Position pos);
AstExpr* findExprAtPosition(const SourceModule& source, Position pos);
ScopePtr findScopeAtPosition(const Module& module, Position pos);
std::optional<Binding> findBindingAtPosition(const Module& module, const SourceModule& source, Position pos);
ExprOrLocal findExprOrLocalAtPosition(const SourceModule& source, Position pos);
std::optional<TypeId> findTypeAtPosition(const Module& module, const SourceModule& sourceModule, Position pos);
std::optional<TypeId> findExpectedTypeAtPosition(const Module& module, const SourceModule& sourceModule, Position pos);
std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const SourceModule& source, const Module& module, Position position);
} // namespace lluz

View file

@ -0,0 +1,93 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Location.h"
#include "lluz/TypeVar.h"
#include <unordered_map>
#include <string>
#include <memory>
#include <optional>
namespace lluz
{
struct Frontend;
struct SourceModule;
struct Module;
struct TypeChecker;
using ModulePtr = std::shared_ptr<Module>;
enum class AutocompleteEntryKind
{
Property,
Binding,
Keyword,
String,
Type,
Module,
};
enum class ParenthesesRecommendation
{
None,
CursorAfter,
CursorInside,
};
enum class TypeCorrectKind
{
None,
Correct,
CorrectFunctionResult,
};
struct AutocompleteEntry
{
AutocompleteEntryKind kind = AutocompleteEntryKind::Property;
// Nullopt if kind is Keyword
std::optional<TypeId> type = std::nullopt;
bool deprecated = false;
// Only meaningful if kind is Property.
bool wrongIndexType = false;
// Set if this suggestion matches the type expected in the context
TypeCorrectKind typeCorrect = TypeCorrectKind::None;
std::optional<const ClassTypeVar*> containingClass = std::nullopt;
std::optional<const Property*> prop = std::nullopt;
std::optional<std::string> documentationSymbol = std::nullopt;
Tags tags;
ParenthesesRecommendation parens = ParenthesesRecommendation::None;
};
using AutocompleteEntryMap = std::unordered_map<std::string, AutocompleteEntry>;
struct AutocompleteResult
{
AutocompleteEntryMap entryMap;
std::vector<AstNode*> ancestry;
AutocompleteResult() = default;
AutocompleteResult(AutocompleteEntryMap entryMap, std::vector<AstNode*> ancestry)
: entryMap(std::move(entryMap))
, ancestry(std::move(ancestry))
{
}
};
using ModuleName = std::string;
using StringCompletionCallback = std::function<std::optional<AutocompleteEntryMap>(std::string tag, std::optional<const ClassTypeVar*> ctx)>;
struct OwningAutocompleteResult
{
AutocompleteResult result;
ModulePtr module;
std::unique_ptr<SourceModule> sourceModule;
};
AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName, Position position, StringCompletionCallback callback);
// Deprecated, do not use in new work.
OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view source, Position position, StringCompletionCallback callback);
} // namespace lluz

View file

@ -0,0 +1,51 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Scope.h"
#include "lluz/TypeInfer.h"
namespace lluz
{
void registerBuiltinTypes(TypeChecker& typeChecker);
TypeId makeUnion(TypeArena& arena, std::vector<TypeId>&& types);
TypeId makeIntersection(TypeArena& arena, std::vector<TypeId>&& types);
/** Build an optional 't'
*/
TypeId makeOption(TypeChecker& typeChecker, TypeArena& arena, TypeId t);
/** Small utility function for building up type definitions from C++.
*/
TypeId makeFunction( // Monomorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes);
TypeId makeFunction( // Polymorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics, std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes, std::initializer_list<TypeId> retTypes);
TypeId makeFunction( // Monomorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames,
std::initializer_list<TypeId> retTypes);
TypeId makeFunction( // Polymorphic
TypeArena& arena, std::optional<TypeId> selfType, std::initializer_list<TypeId> generics, std::initializer_list<TypePackId> genericPacks,
std::initializer_list<TypeId> paramTypes, std::initializer_list<std::string> paramNames, std::initializer_list<TypeId> retTypes);
void attachMagicFunction(TypeId ty, MagicFunction fn);
Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol = std::nullopt);
void assignPropDocumentationSymbols(TableTypeVar::Props& props, const std::string& baseName);
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 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);
Binding* tryGetGlobalBindingRef(TypeChecker& typeChecker, const std::string& name);
TypeId getGlobalBinding(TypeChecker& typeChecker, const std::string& name);
} // namespace lluz

View file

@ -0,0 +1,30 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/TypeArena.h"
#include "lluz/TypeVar.h"
#include <unordered_map>
namespace lluz
{
// Only exposed so they can be unit tested.
using SeenTypes = std::unordered_map<TypeId, TypeId>;
using SeenTypePacks = std::unordered_map<TypePackId, TypePackId>;
struct CloneState
{
SeenTypes seenTypes;
SeenTypePacks seenTypePacks;
int recursionCount = 0;
};
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState);
TypeId clone(TypeId tp, TypeArena& dest, CloneState& cloneState);
TypeFun clone(const TypeFun& typeFun, TypeArena& dest, CloneState& cloneState);
TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log);
} // namespace lluz

View file

@ -0,0 +1,58 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Linter.h"
#include "lluz/ParseOptions.h"
#include <string>
#include <optional>
#include <vector>
namespace lluz
{
using ModuleName = std::string;
constexpr const char* kConfigName = ".lluzrc";
struct Config
{
Config()
{
enabledLint.setDefaults();
}
Mode mode = Mode::NoCheck;
ParseOptions parseOptions;
LintOptions enabledLint;
LintOptions fatalLint;
bool lintErrors = false;
bool typeErrors = true;
std::vector<std::string> globals;
};
struct ConfigResolver
{
virtual ~ConfigResolver() {}
virtual const Config& getConfig(const ModuleName& name) const = 0;
};
struct NullConfigResolver : ConfigResolver
{
Config defaultConfig;
virtual const Config& getConfig(const ModuleName& name) const override;
};
std::optional<std::string> parseModeString(Mode& mode, const std::string& modeString, bool compat = false);
std::optional<std::string> parseLintRuleString(
LintOptions& enabledLints, LintOptions& fatalLints, const std::string& warningName, const std::string& value, bool compat = false);
std::optional<std::string> parseConfig(const std::string& contents, Config& config, bool compat = false);
} // namespace lluz

View file

@ -0,0 +1,105 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Ast.h" // Used for some of the enumerations
#include "lluz/NotNull.h"
#include "lluz/Variant.h"
#include <string>
#include <memory>
#include <vector>
namespace lluz
{
struct Scope2;
struct TypeVar;
using TypeId = const TypeVar*;
struct TypePackVar;
using TypePackId = const TypePackVar*;
// subType <: superType
struct SubtypeConstraint
{
TypeId subType;
TypeId superType;
};
// subPack <: superPack
struct PackSubtypeConstraint
{
TypePackId subPack;
TypePackId superPack;
};
// subType ~ gen superType
struct GeneralizationConstraint
{
TypeId generalizedType;
TypeId sourceType;
Scope2* scope;
};
// subType ~ inst superType
struct InstantiationConstraint
{
TypeId subType;
TypeId superType;
};
struct UnaryConstraint
{
AstExprUnary::Op op;
TypeId operandType;
TypeId resultType;
};
struct BinaryConstraint
{
AstExprBinary::Op op;
TypeId leftType;
TypeId rightType;
TypeId resultType;
};
// name(namedType) = name
struct NameConstraint
{
TypeId namedType;
std::string name;
};
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint,
BinaryConstraint, NameConstraint>;
using ConstraintPtr = std::unique_ptr<struct Constraint>;
struct Constraint
{
explicit Constraint(ConstraintV&& c);
Constraint(const Constraint&) = delete;
Constraint& operator=(const Constraint&) = delete;
ConstraintV c;
std::vector<NotNull<Constraint>> dependencies;
};
inline Constraint& asMutable(const Constraint& c)
{
return const_cast<Constraint&>(c);
}
template<typename T>
T* getMutable(Constraint& c)
{
return ::lluz::get_if<T>(&c.c);
}
template<typename T>
const T* get(const Constraint& c)
{
return getMutable<T>(asMutable(c));
}
} // namespace lluz

View file

@ -0,0 +1,197 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <memory>
#include <vector>
#include <unordered_map>
#include "lluz/Ast.h"
#include "lluz/Constraint.h"
#include "lluz/Module.h"
#include "lluz/NotNull.h"
#include "lluz/Symbol.h"
#include "lluz/TypeVar.h"
#include "lluz/Variant.h"
namespace lluz
{
struct Scope2;
struct ConstraintGraphBuilder
{
// A list of all the scopes in the module. This vector holds ownership of the
// scope pointers; the scopes themselves borrow pointers to other scopes to
// define the scope hierarchy.
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scopes;
ModuleName moduleName;
SingletonTypes& singletonTypes;
const NotNull<TypeArena> arena;
// The root scope of the module we're generating constraints for.
// This is null when the CGB is initially constructed.
Scope2* rootScope;
// A mapping of AST node to TypeId.
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
// A mapping of AST node to TypePackId.
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
// Types resolved from type annotations. Analogous to astTypes.
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
// Type packs resolved from type annotations. Analogous to astTypePacks.
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
int recursionCount = 0;
// It is pretty uncommon for constraint generation to itself produce errors, but it can happen.
std::vector<TypeError> errors;
// Occasionally constraint generation needs to produce an ICE.
const NotNull<InternalErrorReporter> ice;
NotNull<Scope2> globalScope;
ConstraintGraphBuilder(const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope2> globalScope);
/**
* Fabricates a new free type belonging to a given scope.
* @param scope the scope the free type belongs to.
*/
TypeId freshType(NotNull<Scope2> scope);
/**
* Fabricates a new free type pack belonging to a given scope.
* @param scope the scope the free type pack belongs to.
*/
TypePackId freshTypePack(NotNull<Scope2> scope);
/**
* Fabricates a scope that is a child of another scope.
* @param location the lexical extent of the scope in the source code.
* @param parent the parent scope of the new scope. Must not be null.
*/
NotNull<Scope2> childScope(Location location, NotNull<Scope2> parent);
/**
* Adds a new constraint with no dependencies to a given scope.
* @param scope the scope to add the constraint to.
* @param cv the constraint variant to add.
*/
void addConstraint(NotNull<Scope2> scope, ConstraintV cv);
/**
* Adds a constraint to a given scope.
* @param scope the scope to add the constraint to. Must not be null.
* @param c the constraint to add.
*/
void addConstraint(NotNull<Scope2> scope, std::unique_ptr<Constraint> c);
/**
* The entry point to the ConstraintGraphBuilder. This will construct a set
* of scopes, constraints, and free types that can be solved later.
* @param block the root block to generate constraints for.
*/
void visit(AstStatBlock* block);
void visitBlockWithoutChildScope(NotNull<Scope2> scope, AstStatBlock* block);
void visit(NotNull<Scope2> scope, AstStat* stat);
void visit(NotNull<Scope2> scope, AstStatBlock* block);
void visit(NotNull<Scope2> scope, AstStatLocal* local);
void visit(NotNull<Scope2> scope, AstStatLocalFunction* function);
void visit(NotNull<Scope2> scope, AstStatFunction* function);
void visit(NotNull<Scope2> scope, AstStatReturn* ret);
void visit(NotNull<Scope2> scope, AstStatAssign* assign);
void visit(NotNull<Scope2> scope, AstStatIf* ifStatement);
void visit(NotNull<Scope2> scope, AstStatTypeAlias* alias);
TypePackId checkExprList(NotNull<Scope2> scope, const AstArray<AstExpr*>& exprs);
TypePackId checkPack(NotNull<Scope2> scope, AstArray<AstExpr*> exprs);
TypePackId checkPack(NotNull<Scope2> scope, AstExpr* expr);
/**
* Checks an expression that is expected to evaluate to one type.
* @param scope the scope the expression is contained within.
* @param expr the expression to check.
* @return the type of the expression.
*/
TypeId check(NotNull<Scope2> scope, AstExpr* expr);
TypeId checkExprTable(NotNull<Scope2> scope, AstExprTable* expr);
TypeId check(NotNull<Scope2> scope, AstExprIndexName* indexName);
TypeId check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr);
TypeId check(NotNull<Scope2> scope, AstExprUnary* unary);
TypeId check(NotNull<Scope2> scope, AstExprBinary* binary);
struct FunctionSignature
{
// The type of the function.
TypeId signature;
// The scope that encompasses the function's signature. May be nullptr
// if there was no need for a signature scope (the function has no
// generics).
Scope2* signatureScope;
// The scope that encompasses the function's body. Is a child scope of
// signatureScope, if present.
NotNull<Scope2> bodyScope;
};
FunctionSignature checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn);
/**
* Checks the body of a function expression.
* @param scope the interior scope of the body of the function.
* @param fn the function expression to check.
*/
void checkFunctionBody(NotNull<Scope2> scope, AstExprFunction* fn);
/**
* Resolves a type from its AST annotation.
* @param scope the scope that the type annotation appears within.
* @param ty the AST annotation to resolve.
* @return the type of the AST annotation.
**/
TypeId resolveType(NotNull<Scope2> scope, AstType* ty);
/**
* Resolves a type pack from its AST annotation.
* @param scope the scope that the type annotation appears within.
* @param tp the AST annotation to resolve.
* @return the type pack of the AST annotation.
**/
TypePackId resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp);
TypePackId resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list);
std::vector<std::pair<Name, GenericTypeDefinition>> createGenerics(NotNull<Scope2> scope, AstArray<AstGenericType> generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> createGenericPacks(NotNull<Scope2> scope, AstArray<AstGenericTypePack> packs);
TypeId flattenPack(NotNull<Scope2> scope, Location location, TypePackId tp);
void reportError(Location location, TypeErrorData err);
void reportCodeTooComplex(Location location);
/** Scan the program for global definitions.
*
* ConstraintGraphBuilder needs to differentiate between globals and accesses to undefined symbols. Doing this "for
* real" in a general way is going to be pretty hard, so we are choosing not to tackle that yet. For now, we do an
* initial scan of the AST and note what globals are defined.
*/
void prepopulateGlobalScope(NotNull<Scope2> globalScope, AstStatBlock* program);
};
/**
* Collects a vector of borrowed constraints from the scope and all its child
* scopes. It is important to only call this function when you're done adding
* constraints to the scope or its descendants, lest the borrowed pointers
* become invalid due to a container reallocation.
* @param rootScope the root scope of the scope graph to collect constraints
* from.
* @return a list of pointers to constraints contained within the scope graph.
* None of these pointers should be null.
*/
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope);
} // namespace lluz

View file

@ -0,0 +1,126 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Error.h"
#include "lluz/Variant.h"
#include "lluz/Constraint.h"
#include "lluz/ConstraintSolverLogger.h"
#include "lluz/TypeVar.h"
#include <vector>
namespace lluz
{
// TypeId, TypePackId, or Constraint*. It is impossible to know which, but we
// never dereference this pointer.
using BlockedConstraintId = const void*;
struct ConstraintSolver
{
TypeArena* arena;
InternalErrorReporter iceReporter;
// The entire set of constraints that the solver is trying to resolve. It
// is important to not add elements to this vector, lest the underlying
// storage that we retain pointers to be mutated underneath us.
const std::vector<NotNull<Constraint>> constraints;
NotNull<Scope2> rootScope;
// This includes every constraint that has not been fully solved.
// A constraint can be both blocked and unsolved, for instance.
std::vector<NotNull<const Constraint>> unsolvedConstraints;
// A mapping of constraint pointer to how many things the constraint is
// blocked on. Can be empty or 0 for constraints that are not blocked on
// anything.
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
// A mapping of type/pack pointers to the constraints they block.
std::unordered_map<BlockedConstraintId, std::vector<NotNull<const Constraint>>> blocked;
ConstraintSolverLogger logger;
explicit ConstraintSolver(TypeArena* arena, NotNull<Scope2> rootScope);
/**
* Attempts to dispatch all pending constraints and reach a type solution
* that satisfies all of the constraints.
**/
void run();
bool done();
/** Attempt to dispatch a constraint. Returns true if it was successful.
* If tryDispatch() returns false, the constraint remains in the unsolved set and will be retried later.
*/
bool tryDispatch(NotNull<const Constraint> c, bool force);
bool tryDispatch(const SubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const PackSubtypeConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const GeneralizationConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const InstantiationConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const UnaryConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const BinaryConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
/**
* Block a constraint on the resolution of a TypeVar.
* @returns false always. This is just to allow tryDispatch to return the result of block()
*/
bool block(TypeId target, NotNull<const Constraint> constraint);
bool block(TypePackId target, NotNull<const Constraint> constraint);
void unblock(NotNull<const Constraint> progressed);
void unblock(TypeId progressed);
void unblock(TypePackId progressed);
/**
* @returns true if the TypeId is in a blocked state.
*/
bool isBlocked(TypeId ty);
/**
* Returns whether the constraint is blocked on anything.
* @param constraint the constraint to check.
*/
bool isBlocked(NotNull<const Constraint> constraint);
/**
* Creates a new Unifier and performs a single unification operation. Commits
* the result.
* @param subType the sub-type to unify.
* @param superType the super-type to unify.
*/
void unify(TypeId subType, TypeId superType);
/**
* Creates a new Unifier and performs a single unification operation. Commits
* the result.
* @param subPack the sub-type pack to unify.
* @param superPack the super-type pack to unify.
*/
void unify(TypePackId subPack, TypePackId superPack);
private:
/**
* Marks a constraint as being blocked on a type or type pack. The constraint
* solver will not attempt to dispatch blocked constraints until their
* dependencies have made progress.
* @param target the type or type pack pointer that the constraint is blocked on.
* @param constraint the constraint to block.
**/
void block_(BlockedConstraintId target, NotNull<const Constraint> constraint);
/**
* Informs the solver that progress has been made on a type or type pack. The
* solver will wake up all constraints that are blocked on the type or type pack,
* and will resume attempting to dispatch them.
* @param progressed the type or type pack pointer that has progressed.
**/
void unblock_(BlockedConstraintId progressed);
};
void dump(NotNull<Scope2> rootScope, struct ToStringOptions& opts);
} // namespace lluz

View file

@ -0,0 +1,28 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Constraint.h"
#include "lluz/NotNull.h"
#include "lluz/Scope.h"
#include "lluz/ToString.h"
#include <optional>
#include <string>
#include <vector>
namespace lluz
{
struct ConstraintSolverLogger
{
std::string compileOutput();
void captureBoundarySnapshot(const Scope2* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void prepareStepSnapshot(const Scope2* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints);
void commitPreparedStepSnapshot();
private:
std::vector<std::string> snapshots;
std::optional<std::string> preparedSnapshot;
ToStringOptions opts;
};
} // namespace lluz

View file

@ -0,0 +1,62 @@
#pragma once
#include "lluz/DenseHash.h"
#include "lluz/Variant.h"
#include <string>
#include <vector>
namespace lluz
{
struct FunctionDocumentation;
struct TableDocumentation;
struct OverloadedFunctionDocumentation;
struct BasicDocumentation;
using Documentation = lluz::Variant<BasicDocumentation, FunctionDocumentation, TableDocumentation, OverloadedFunctionDocumentation>;
using DocumentationSymbol = std::string;
struct BasicDocumentation
{
std::string documentation;
std::string learnMoreLink;
std::string codeSample;
};
struct FunctionParameterDocumentation
{
std::string name;
DocumentationSymbol documentation;
};
// Represents documentation for anything callable. This could be a method or a
// callback or a free function.
struct FunctionDocumentation
{
std::string documentation;
std::vector<FunctionParameterDocumentation> parameters;
std::vector<DocumentationSymbol> returns;
std::string learnMoreLink;
std::string codeSample;
};
struct OverloadedFunctionDocumentation
{
// This is a map of function signature to overload symbol name.
lluz::DenseHashMap<std::string, DocumentationSymbol> overloads;
};
// Represents documentation for a table-like item, meaning "anything with keys".
// This could be a table or a class.
struct TableDocumentation
{
std::string documentation;
lluz::DenseHashMap<std::string, DocumentationSymbol> keys;
std::string learnMoreLink;
std::string codeSample;
};
using DocumentationDatabase = lluz::DenseHashMap<DocumentationSymbol, Documentation>;
} // namespace lluz

View file

@ -0,0 +1,393 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/FileResolver.h"
#include "lluz/Location.h"
#include "lluz/TypeVar.h"
#include "lluz/Variant.h"
#include "lluz/TypeArena.h"
namespace lluz
{
struct TypeError;
struct TypeMismatch
{
TypeMismatch() = default;
TypeMismatch(TypeId wantedType, TypeId givenType);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason);
TypeMismatch(TypeId wantedType, TypeId givenType, std::string reason, TypeError error);
TypeId wantedType = nullptr;
TypeId givenType = nullptr;
std::string reason;
std::shared_ptr<TypeError> error;
bool operator==(const TypeMismatch& rhs) const;
};
struct UnknownSymbol
{
enum Context
{
Binding,
Type,
Generic
};
Name name;
Context context;
bool operator==(const UnknownSymbol& rhs) const;
};
struct UnknownProperty
{
TypeId table;
Name key;
bool operator==(const UnknownProperty& rhs) const;
};
struct NotATable
{
TypeId ty;
bool operator==(const NotATable& rhs) const;
};
struct CannotExtendTable
{
enum Context
{
Property,
Indexer,
Metatable
};
TypeId tableType;
Context context;
Name prop;
bool operator==(const CannotExtendTable& rhs) const;
};
struct OnlyTablesCanHaveMethods
{
TypeId tableType;
bool operator==(const OnlyTablesCanHaveMethods& rhs) const;
};
struct DuplicateTypeDefinition
{
Name name;
Location previousLocation;
bool operator==(const DuplicateTypeDefinition& rhs) const;
};
struct CountMismatch
{
enum Context
{
Arg,
Result,
Return,
};
size_t expected;
size_t actual;
Context context = Arg;
bool isVariadic = false;
bool operator==(const CountMismatch& rhs) const;
};
struct FunctionDoesNotTakeSelf
{
bool operator==(const FunctionDoesNotTakeSelf& rhs) const;
};
struct FunctionRequiresSelf
{
bool operator==(const FunctionRequiresSelf& rhs) const;
};
struct OccursCheckFailed
{
bool operator==(const OccursCheckFailed& rhs) const;
};
struct UnknownRequire
{
std::string modulePath;
bool operator==(const UnknownRequire& rhs) const;
};
struct IncorrectGenericParameterCount
{
Name name;
TypeFun typeFun;
size_t actualParameters;
size_t actualPackParameters;
bool operator==(const IncorrectGenericParameterCount& rhs) const;
};
struct SyntaxError
{
std::string message;
bool operator==(const SyntaxError& rhs) const;
};
struct CodeTooComplex
{
bool operator==(const CodeTooComplex&) const;
};
struct UnificationTooComplex
{
bool operator==(const UnificationTooComplex&) const;
};
// Could easily be folded into UnknownProperty with an extra field, std::set<Name> candidates.
// But for telemetry purposes, we want to have this be a distinct variant.
struct UnknownPropButFoundLikeProp
{
TypeId table;
Name key;
std::set<Name> candidates;
bool operator==(const UnknownPropButFoundLikeProp& rhs) const;
};
struct GenericError
{
std::string message;
bool operator==(const GenericError& rhs) const;
};
struct InternalError
{
std::string message;
bool operator==(const InternalError& rhs) const;
};
struct CannotCallNonFunction
{
TypeId ty;
bool operator==(const CannotCallNonFunction& rhs) const;
};
struct ExtraInformation
{
std::string message;
bool operator==(const ExtraInformation& rhs) const;
};
struct DeprecatedApiUsed
{
std::string symbol;
std::string useInstead;
bool operator==(const DeprecatedApiUsed& rhs) const;
};
struct ModuleHasCyclicDependency
{
std::vector<ModuleName> cycle;
bool operator==(const ModuleHasCyclicDependency& rhs) const;
};
struct FunctionExitsWithoutReturning
{
TypePackId expectedReturnType;
bool operator==(const FunctionExitsWithoutReturning& rhs) const;
};
struct IllegalRequire
{
std::string moduleName;
std::string reason;
bool operator==(const IllegalRequire& rhs) const;
};
struct MissingProperties
{
enum Context
{
Missing,
Extra
};
TypeId superType;
TypeId subType;
std::vector<Name> properties;
Context context = Missing;
bool operator==(const MissingProperties& rhs) const;
};
struct DuplicateGenericParameter
{
std::string parameterName;
bool operator==(const DuplicateGenericParameter& rhs) const;
};
struct CannotInferBinaryOperation
{
enum OpKind
{
Operation,
Comparison,
};
AstExprBinary::Op op;
std::optional<std::string> suggestedToAnnotate;
OpKind kind;
bool operator==(const CannotInferBinaryOperation& rhs) const;
};
struct SwappedGenericTypeParameter
{
enum Kind
{
Type,
Pack,
};
std::string name;
// What was `name` being used as?
Kind kind;
bool operator==(const SwappedGenericTypeParameter& rhs) const;
};
struct OptionalValueAccess
{
TypeId optional;
bool operator==(const OptionalValueAccess& rhs) const;
};
struct MissingUnionProperty
{
TypeId type;
std::vector<TypeId> missing;
Name key;
bool operator==(const MissingUnionProperty& rhs) const;
};
struct TypesAreUnrelated
{
TypeId left;
TypeId right;
bool operator==(const TypesAreUnrelated& rhs) const;
};
struct NormalizationTooComplex
{
bool operator==(const NormalizationTooComplex&) const
{
return true;
}
};
using TypeErrorData = Variant<TypeMismatch, UnknownSymbol, UnknownProperty, NotATable, CannotExtendTable, OnlyTablesCanHaveMethods,
DuplicateTypeDefinition, CountMismatch, FunctionDoesNotTakeSelf, FunctionRequiresSelf, OccursCheckFailed, UnknownRequire,
IncorrectGenericParameterCount, SyntaxError, CodeTooComplex, UnificationTooComplex, UnknownPropButFoundLikeProp, GenericError, InternalError,
CannotCallNonFunction, ExtraInformation, DeprecatedApiUsed, ModuleHasCyclicDependency, IllegalRequire, FunctionExitsWithoutReturning,
DuplicateGenericParameter, CannotInferBinaryOperation, MissingProperties, SwappedGenericTypeParameter, OptionalValueAccess, MissingUnionProperty,
TypesAreUnrelated, NormalizationTooComplex>;
struct TypeError
{
Location location;
ModuleName moduleName;
TypeErrorData data;
int code() const;
TypeError() = default;
TypeError(const Location& location, const ModuleName& moduleName, const TypeErrorData& data)
: location(location)
, moduleName(moduleName)
, data(data)
{
}
TypeError(const Location& location, const TypeErrorData& data)
: TypeError(location, {}, data)
{
}
bool operator==(const TypeError& rhs) const;
};
template<typename T>
const T* get(const TypeError& e)
{
return get_if<T>(&e.data);
}
template<typename T>
T* get(TypeError& e)
{
return get_if<T>(&e.data);
}
using ErrorVec = std::vector<TypeError>;
struct TypeErrorToStringOptions
{
FileResolver* fileResolver = nullptr;
};
std::string toString(const TypeError& error);
std::string toString(const TypeError& error, TypeErrorToStringOptions options);
bool containsParseErrorName(const TypeError& error);
// Copy any types named in the error into destArena.
void copyErrors(ErrorVec& errors, struct TypeArena& destArena);
// Internal Compiler Error
struct InternalErrorReporter
{
std::function<void(const char*)> onInternalError;
std::string moduleName;
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);
};
class InternalCompilerError : public std::exception
{
public:
explicit InternalCompilerError(const std::string& message, const std::string& moduleName)
: message(message)
, moduleName(moduleName)
{
}
explicit InternalCompilerError(const std::string& message, const std::string& moduleName, const Location& location)
: message(message)
, moduleName(moduleName)
, location(location)
{
}
virtual const char* what() const throw();
const std::string message;
const std::string moduleName;
const std::optional<Location> location;
};
} // namespace lluz

View file

@ -0,0 +1,64 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <string>
#include <optional>
namespace lluz
{
class AstExpr;
using ModuleName = std::string;
struct SourceCode
{
enum Type
{
None,
Module,
Script,
Local
};
std::string source;
Type type;
};
struct ModuleInfo
{
ModuleName name;
bool optional = false;
};
struct FileResolver
{
virtual ~FileResolver() {}
virtual std::optional<SourceCode> readSource(const ModuleName& name) = 0;
virtual std::optional<ModuleInfo> resolveModule(const ModuleInfo* context, AstExpr* expr)
{
return std::nullopt;
}
virtual std::string getHumanReadableModuleName(const ModuleName& name) const
{
return name;
}
virtual std::optional<std::string> getEnvironmentForModule(const ModuleName& name) const
{
return std::nullopt;
}
};
struct NullFileResolver : FileResolver
{
std::optional<SourceCode> readSource(const ModuleName& name) override
{
return std::nullopt;
}
};
} // namespace lluz

View file

@ -0,0 +1,199 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Config.h"
#include "lluz/Module.h"
#include "lluz/ModuleResolver.h"
#include "lluz/RequireTracer.h"
#include "lluz/Scope.h"
#include "lluz/TypeInfer.h"
#include "lluz/Variant.h"
#include <string>
#include <vector>
#include <optional>
namespace lluz
{
class AstStat;
class ParseError;
struct Frontend;
struct TypeError;
struct LintWarning;
struct TypeChecker;
struct FileResolver;
struct ModuleResolver;
struct ParseResult;
struct HotComment;
struct LoadDefinitionFileResult
{
bool success;
ParseResult parseResult;
ModulePtr module;
};
LoadDefinitionFileResult loadDefinitionFile(
TypeChecker& typeChecker, ScopePtr targetScope, std::string_view definition, const std::string& packageName);
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments);
std::vector<std::string_view> parsePathExpr(const AstExpr& pathExpr);
// Exported only for convenient testing.
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const std::vector<std::string_view>& expr);
/** Try to convert an AST fragment into a ModuleName.
* Returns std::nullopt if the expression cannot be resolved. This will most likely happen in cases where
* the import path involves some dynamic computation that we cannot see into at typechecking time.
*
* Unintuitively, weirdly-formulated modules (like game.Parent.Parent.Parent.Foo) will successfully produce a ModuleName
* as long as it falls within the permitted syntax. This is ok because we will fail to find the module and produce an
* error when we try during typechecking.
*/
std::optional<ModuleName> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& expr);
struct SourceNode
{
bool hasDirtySourceModule() const
{
return dirtySourceModule;
}
bool hasDirtyModule(bool forAutocomplete) const
{
return forAutocomplete ? dirtyModuleForAutocomplete : dirtyModule;
}
ModuleName name;
std::unordered_set<ModuleName> requireSet;
std::vector<std::pair<ModuleName, Location>> requireLocations;
bool dirtySourceModule = true;
bool dirtyModule = true;
bool dirtyModuleForAutocomplete = true;
double autocompleteLimitsMult = 1.0;
};
struct FrontendOptions
{
// When true, we retain full type information about every term in the AST.
// Setting this to false cuts back on RAM and is a good idea for batch
// jobs where the type graph is not deeply inspected after typechecking
// is complete.
bool retainFullTypeGraphs = false;
// Run typechecking only in mode required for autocomplete (strict mode in order to get more precise type information)
bool forAutocomplete = false;
};
struct CheckResult
{
std::vector<TypeError> errors;
std::vector<ModuleName> timeoutHits;
};
struct FrontendModuleResolver : ModuleResolver
{
FrontendModuleResolver(Frontend* frontend);
const ModulePtr getModule(const ModuleName& moduleName) const override;
bool moduleExists(const ModuleName& moduleName) const override;
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override;
std::string getHumanReadableModuleName(const ModuleName& moduleName) const override;
Frontend* frontend;
std::unordered_map<ModuleName, ModulePtr> modules;
};
struct Frontend
{
struct Stats
{
size_t files = 0;
size_t lines = 0;
size_t filesStrict = 0;
size_t filesNonstrict = 0;
double timeRead = 0;
double timeParse = 0;
double timeCheck = 0;
double timeLint = 0;
};
Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {});
CheckResult check(const ModuleName& name, std::optional<FrontendOptions> optionOverride = {}); // new shininess
LintResult lint(const ModuleName& name, std::optional<LintOptions> enabledLintWarnings = {});
/** Lint some code that has no associated DataModel object
*
* Since this source fragment has no name, we cannot cache its AST. Instead,
* we return it to the caller to use as they wish.
*/
std::pair<SourceModule, LintResult> lintFragment(std::string_view source, std::optional<LintOptions> enabledLintWarnings = {});
LintResult lint(const SourceModule& module, std::optional<LintOptions> enabledLintWarnings = {});
bool isDirty(const ModuleName& name, bool forAutocomplete = false) const;
void markDirty(const ModuleName& name, std::vector<ModuleName>* markedDirty = nullptr);
/** Borrow a pointer into the SourceModule cache.
*
* Returns nullptr if we don't have it. This could mean that the script
* doesn't exist, or simply that its contents have changed since the previous
* check, in which case we do not have its AST.
*
* IMPORTANT: this pointer is only valid until the next call to markDirty. Do not retain it.
*/
SourceModule* getSourceModule(const ModuleName& name);
const SourceModule* getSourceModule(const ModuleName& name) const;
void clearStats();
void clear();
ScopePtr addEnvironment(const std::string& environmentName);
ScopePtr getEnvironmentScope(const std::string& environmentName);
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
NotNull<Scope2> getGlobalScope2();
private:
ModulePtr check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope);
std::pair<SourceNode*, SourceModule*> getSourceNode(CheckResult& checkResult, const ModuleName& name);
SourceModule parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions);
bool parseGraph(std::vector<ModuleName>& buildQueue, CheckResult& checkResult, const ModuleName& root, bool forAutocomplete);
static LintResult classifyLints(const std::vector<LintWarning>& warnings, const Config& config);
ScopePtr getModuleEnvironment(const SourceModule& module, const Config& config);
std::unordered_map<std::string, ScopePtr> environments;
std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
std::unique_ptr<Scope2> globalScope2;
public:
FileResolver* fileResolver;
FrontendModuleResolver moduleResolver;
FrontendModuleResolver moduleResolverForAutocomplete;
TypeChecker typeChecker;
TypeChecker typeCheckerForAutocomplete;
ConfigResolver* configResolver;
FrontendOptions options;
InternalErrorReporter iceHandler;
TypeArena arenaForAutocomplete;
std::unordered_map<ModuleName, SourceNode> sourceNodes;
std::unordered_map<ModuleName, SourceModule> sourceModules;
std::unordered_map<ModuleName, RequireTraceResult> requireTrace;
Stats stats = {};
};
} // namespace lluz

View file

@ -0,0 +1,53 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Substitution.h"
#include "lluz/TypeVar.h"
#include "lluz/Unifiable.h"
namespace lluz
{
struct TypeArena;
struct TxnLog;
// A substitution which replaces generic types in a given set by free types.
struct ReplaceGenerics : Substitution
{
ReplaceGenerics(
const TxnLog* log, TypeArena* arena, TypeLevel level, const std::vector<TypeId>& generics, const std::vector<TypePackId>& genericPacks)
: Substitution(log, arena)
, level(level)
, generics(generics)
, genericPacks(genericPacks)
{
}
TypeLevel level;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
// A substitution which replaces generic functions by monomorphic functions
struct Instantiation : Substitution
{
Instantiation(const TxnLog* log, TypeArena* arena, TypeLevel level)
: Substitution(log, arena)
, level(level)
{
}
TypeLevel level;
bool ignoreChildren(TypeId ty) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
} // namespace lluz

View file

@ -0,0 +1,51 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Error.h"
#include "lluz/Location.h"
#include "lluz/TypeVar.h"
#include "lluz/Ast.h"
#include <ostream>
namespace lluz
{
std::ostream& operator<<(std::ostream& lhs, const Position& position);
std::ostream& operator<<(std::ostream& lhs, const Location& location);
std::ostream& operator<<(std::ostream& lhs, const AstName& name);
std::ostream& operator<<(std::ostream& lhs, const TypeError& error);
std::ostream& operator<<(std::ostream& lhs, const TypeMismatch& error);
std::ostream& operator<<(std::ostream& lhs, const UnknownSymbol& error);
std::ostream& operator<<(std::ostream& lhs, const UnknownProperty& error);
std::ostream& operator<<(std::ostream& lhs, const NotATable& error);
std::ostream& operator<<(std::ostream& lhs, const CannotExtendTable& error);
std::ostream& operator<<(std::ostream& lhs, const OnlyTablesCanHaveMethods& error);
std::ostream& operator<<(std::ostream& lhs, const DuplicateTypeDefinition& error);
std::ostream& operator<<(std::ostream& lhs, const CountMismatch& error);
std::ostream& operator<<(std::ostream& lhs, const FunctionDoesNotTakeSelf& error);
std::ostream& operator<<(std::ostream& lhs, const FunctionRequiresSelf& error);
std::ostream& operator<<(std::ostream& lhs, const OccursCheckFailed& error);
std::ostream& operator<<(std::ostream& lhs, const UnknownRequire& error);
std::ostream& operator<<(std::ostream& lhs, const UnknownPropButFoundLikeProp& e);
std::ostream& operator<<(std::ostream& lhs, const GenericError& error);
std::ostream& operator<<(std::ostream& lhs, const InternalError& error);
std::ostream& operator<<(std::ostream& lhs, const FunctionExitsWithoutReturning& error);
std::ostream& operator<<(std::ostream& lhs, const MissingProperties& error);
std::ostream& operator<<(std::ostream& lhs, const IllegalRequire& error);
std::ostream& operator<<(std::ostream& lhs, const ModuleHasCyclicDependency& error);
std::ostream& operator<<(std::ostream& lhs, const DuplicateGenericParameter& error);
std::ostream& operator<<(std::ostream& lhs, const CannotInferBinaryOperation& error);
std::ostream& operator<<(std::ostream& lhs, const SwappedGenericTypeParameter& error);
std::ostream& operator<<(std::ostream& lhs, const OptionalValueAccess& error);
std::ostream& operator<<(std::ostream& lhs, const MissingUnionProperty& error);
std::ostream& operator<<(std::ostream& lhs, const TypesAreUnrelated& error);
std::ostream& operator<<(std::ostream& lhs, const TableState& tv);
std::ostream& operator<<(std::ostream& lhs, const TypeVar& tv);
std::ostream& operator<<(std::ostream& lhs, const TypePackVar& tv);
std::ostream& operator<<(std::ostream& lhs, const TypeErrorData& ted);
} // namespace lluz

View file

@ -0,0 +1,13 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <string>
namespace lluz
{
class AstNode;
std::string toJson(AstNode* node);
} // namespace lluz

View file

@ -0,0 +1,51 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Variant.h"
#include "lluz/Symbol.h"
#include <memory>
#include <unordered_map>
namespace lluz
{
struct TypeVar;
using TypeId = const TypeVar*;
struct Field;
using LValue = Variant<Symbol, Field>;
struct Field
{
std::shared_ptr<LValue> parent;
std::string key;
bool operator==(const Field& rhs) const;
bool operator!=(const Field& rhs) const;
};
struct LValueHasher
{
size_t operator()(const LValue& lvalue) const;
};
const LValue* baseof(const LValue& lvalue);
std::optional<LValue> tryGetLValue(const class AstExpr& expr);
// Utility function: breaks down an LValue to get at the Symbol
Symbol getBaseSymbol(const LValue& lvalue);
template<typename T>
const T* get(const LValue& lvalue)
{
return get_if<T>(&lvalue);
}
using RefinementMap = std::unordered_map<LValue, TypeId, LValueHasher>;
void merge(RefinementMap& l, const RefinementMap& r, std::function<TypeId(TypeId, TypeId)> f);
void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty);
} // namespace lluz

View file

@ -0,0 +1,100 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Location.h"
#include <memory>
#include <vector>
namespace lluz
{
struct AstName;
class AstStat;
class AstNameTable;
struct TypeChecker;
struct Module;
struct HotComment;
using ScopePtr = std::shared_ptr<struct Scope>;
struct LintWarning
{
// Make sure any new lint codes are documented here: https://lluz-lang.org/lint
// Note that in Studio, the active set of lint warnings is determined by FStringStudiolluzLints
enum Code
{
Code_Unknown = 0,
Code_UnknownGlobal = 1, // superseded by type checker
Code_DeprecatedGlobal = 2,
Code_GlobalUsedAsLocal = 3,
Code_LocalShadow = 4, // disabled in Studio
Code_SameLineStatement = 5, // disabled in Studio
Code_MultiLineStatement = 6,
Code_LocalUnused = 7, // disabled in Studio
Code_FunctionUnused = 8, // disabled in Studio
Code_ImportUnused = 9, // disabled in Studio
Code_BuiltinGlobalWrite = 10,
Code_PlaceholderRead = 11,
Code_UnreachableCode = 12,
Code_UnknownType = 13,
Code_ForRange = 14,
Code_UnbalancedAssignment = 15,
Code_ImplicitReturn = 16, // disabled in Studio, superseded by type checker in strict mode
Code_DuplicateLocal = 17,
Code_FormatString = 18,
Code_TableLiteral = 19,
Code_UninitializedLocal = 20,
Code_DuplicateFunction = 21,
Code_DeprecatedApi = 22,
Code_TableOperations = 23,
Code_DuplicateCondition = 24,
Code_MisleadingAndOr = 25,
Code_CommentDirective = 26,
Code__Count
};
Code code;
Location location;
std::string text;
static const char* getName(Code code);
static Code parseName(const char* name);
static uint64_t parseMask(const std::vector<HotComment>& hotcomments);
};
struct LintResult
{
std::vector<LintWarning> errors;
std::vector<LintWarning> warnings;
};
struct LintOptions
{
uint64_t warningMask = 0;
void enableWarning(LintWarning::Code code)
{
warningMask |= 1ull << code;
}
void disableWarning(LintWarning::Code code)
{
warningMask &= ~(1ull << code);
}
bool isEnabled(LintWarning::Code code) const
{
return 0 != (warningMask & (1ull << code));
}
void setDefaults();
};
std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module,
const std::vector<HotComment>& hotcomments, const LintOptions& options);
std::vector<AstName> getDeprecatedGlobals(const AstNameTable& names);
} // namespace lluz

View file

@ -0,0 +1,96 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Error.h"
#include "lluz/FileResolver.h"
#include "lluz/ParseOptions.h"
#include "lluz/ParseResult.h"
#include "lluz/Scope.h"
#include "lluz/TypeArena.h"
#include <memory>
#include <vector>
#include <unordered_map>
#include <optional>
namespace lluz
{
struct Module;
using ScopePtr = std::shared_ptr<struct Scope>;
using ModulePtr = std::shared_ptr<Module>;
class AstType;
class AstTypePack;
/// Root of the AST of a parsed source file
struct SourceModule
{
ModuleName name; // DataModel path if possible. Filename if not.
SourceCode::Type type = SourceCode::None;
std::optional<std::string> environmentName;
bool cyclic = false;
std::shared_ptr<Allocator> allocator;
std::shared_ptr<AstNameTable> names;
std::vector<ParseError> parseErrors;
AstStatBlock* root = nullptr;
std::optional<Mode> mode;
std::vector<HotComment> hotcomments;
std::vector<Comment> commentLocations;
SourceModule()
: allocator(new Allocator)
, names(new AstNameTable(*allocator))
{
}
};
bool isWithinComment(const SourceModule& sourceModule, Position pos);
struct RequireCycle
{
Location location;
std::vector<ModuleName> path; // one of the paths for a require() to go all the way back to the originating module
};
struct Module
{
~Module();
TypeArena interfaceTypes;
TypeArena internalTypes;
// Scopes and AST types refer to parse data, so we need to keep that alive
std::shared_ptr<Allocator> allocator;
std::shared_ptr<AstNameTable> names;
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
std::vector<std::pair<Location, std::unique_ptr<Scope2>>> scope2s; // never empty
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};
DenseHashMap<const AstExpr*, TypeId> astExpectedTypes{nullptr};
DenseHashMap<const AstExpr*, TypeId> astOriginalCallTypes{nullptr};
DenseHashMap<const AstExpr*, TypeId> astOverloadResolvedTypes{nullptr};
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
std::unordered_map<Name, TypeId> declaredGlobals;
ErrorVec errors;
Mode mode;
SourceCode::Type type;
bool timeout = false;
ScopePtr getModuleScope() const;
Scope2* getModuleScope2() const;
// Once a module has been typechecked, we clone its public interface into a separate arena.
// This helps us to force TypeVar ownership into a DAG rather than a DCG.
void clonePublicInterface(InternalErrorReporter& ice);
};
} // namespace lluz

View file

@ -0,0 +1,73 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/FileResolver.h"
#include <memory>
#include <optional>
#include <string>
namespace lluz
{
class AstExpr;
struct Module;
using ModulePtr = std::shared_ptr<Module>;
struct ModuleResolver
{
virtual ~ModuleResolver() {}
/** Compute a ModuleName from an AST fragment. This AST fragment is generally the argument to the require() function.
*
* You probably want to implement this with some variation of pathExprToModuleName.
*
* @returns The ModuleInfo if the expression is a syntactically legal path.
* @returns std::nullopt if we are unable to determine whether or not the expression is a valid path. Type inference will
* silently assume that it could succeed in this case.
*
* FIXME: This is clearly not the right behaviour longterm. We'll want to adust this interface to be able to signal
* a) success,
* b) Definitive failure (this expression will absolutely cause require() to fail at runtime), and
* c) uncertainty
*/
virtual std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) = 0;
/** Get a typechecked module from its name.
*
* This can return null under two circumstances: the module is unknown at compile time,
* or there's a cycle, and we are still in the middle of typechecking the module.
*/
virtual const ModulePtr getModule(const ModuleName& moduleName) const = 0;
/** Is a module known at compile time?
*
* This function can be used to distinguish the above two cases.
*/
virtual bool moduleExists(const ModuleName& moduleName) const = 0;
virtual std::string getHumanReadableModuleName(const ModuleName& moduleName) const = 0;
};
struct NullModuleResolver : ModuleResolver
{
std::optional<ModuleInfo> resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override
{
return std::nullopt;
}
const ModulePtr getModule(const ModuleName& moduleName) const override
{
return nullptr;
}
bool moduleExists(const ModuleName& moduleName) const override
{
return false;
}
std::string getHumanReadableModuleName(const ModuleName& moduleName) const override
{
return moduleName;
}
};
} // namespace lluz

View file

@ -0,0 +1,20 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Substitution.h"
#include "lluz/TypeVar.h"
#include "lluz/Module.h"
namespace lluz
{
struct InternalErrorReporter;
bool isSubtype(TypeId subTy, TypeId superTy, InternalErrorReporter& ice);
bool isSubtype(TypePackId subTy, TypePackId superTy, InternalErrorReporter& ice);
std::pair<TypeId, bool> normalize(TypeId ty, TypeArena& arena, InternalErrorReporter& ice);
std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, InternalErrorReporter& ice);
std::pair<TypePackId, bool> normalize(TypePackId ty, TypeArena& arena, InternalErrorReporter& ice);
std::pair<TypePackId, bool> normalize(TypePackId ty, const ModulePtr& module, InternalErrorReporter& ice);
} // namespace lluz

View file

@ -0,0 +1,90 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Common.h"
#include <functional>
namespace lluz
{
/** A non-owning, non-null pointer to a T.
*
* A NotNull<T> is notionally identical to a T* with the added restriction that
* it can never store nullptr.
*
* The sole conversion rule from T* to NotNull<T> is the single-argument
* constructor, which is intentionally marked explicit. This constructor
* performs a runtime test to verify that the passed pointer is never nullptr.
*
* Pointer arithmetic, increment, decrement, and array indexing are all
* forbidden.
*
* An implicit coersion from NotNull<T> to T* is afforded, as are the pointer
* indirection and member access operators. (*p and p->prop)
*
* The explicit delete statement is permitted (but not recommended) on a
* NotNull<T> through this implicit conversion.
*/
template<typename T>
struct NotNull
{
explicit NotNull(T* t)
: ptr(t)
{
lluz_ASSERT(t);
}
explicit NotNull(std::nullptr_t) = delete;
void operator=(std::nullptr_t) = delete;
template<typename U>
NotNull(NotNull<U> other)
: ptr(other.get())
{
}
operator T*() const noexcept
{
return ptr;
}
T& operator*() const noexcept
{
return *ptr;
}
T* operator->() const noexcept
{
return ptr;
}
T& operator[](int) = delete;
T& operator+(int) = delete;
T& operator-(int) = delete;
T* get() const noexcept
{
return ptr;
}
private:
T* ptr;
};
} // namespace lluz
namespace std
{
template<typename T>
struct hash<lluz::NotNull<T>>
{
size_t operator()(const lluz::NotNull<T>& p) const
{
return std::hash<T*>()(p.get());
}
};
} // namespace std

View file

@ -0,0 +1,90 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Location.h"
#include "lluz/LValue.h"
#include "lluz/Variant.h"
#include <vector>
namespace lluz
{
struct TypeVar;
using TypeId = const TypeVar*;
struct TruthyPredicate;
struct IsAPredicate;
struct TypeGuardPredicate;
struct EqPredicate;
struct AndPredicate;
struct OrPredicate;
struct NotPredicate;
using Predicate = Variant<TruthyPredicate, IsAPredicate, TypeGuardPredicate, EqPredicate, AndPredicate, OrPredicate, NotPredicate>;
using PredicateVec = std::vector<Predicate>;
struct TruthyPredicate
{
LValue lvalue;
Location location;
};
struct IsAPredicate
{
LValue lvalue;
Location location;
TypeId ty;
};
struct TypeGuardPredicate
{
LValue lvalue;
Location location;
std::string kind; // TODO: When singleton types arrive, replace this with `TypeId ty;`
bool isTypeof;
};
struct EqPredicate
{
LValue lvalue;
TypeId type;
Location location;
};
struct AndPredicate
{
PredicateVec lhs;
PredicateVec rhs;
AndPredicate(PredicateVec&& lhs, PredicateVec&& rhs)
: lhs(std::move(lhs))
, rhs(std::move(rhs))
{
}
};
struct OrPredicate
{
PredicateVec lhs;
PredicateVec rhs;
OrPredicate(PredicateVec&& lhs, PredicateVec&& rhs)
: lhs(std::move(lhs))
, rhs(std::move(rhs))
{
}
};
struct NotPredicate
{
PredicateVec predicates;
};
template<typename T>
const T* get(const Predicate& predicate)
{
return get_if<T>(&predicate);
}
} // namespace lluz

View file

@ -0,0 +1,15 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/TypeVar.h"
namespace lluz
{
struct TypeArena;
struct Scope2;
void quantify(TypeId ty, TypeLevel level);
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope);
} // namespace lluz

View file

@ -0,0 +1,52 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Common.h"
#include "..\..\..\..\..\Security\XorString.h"
#include <stdexcept>
#include <exception>
namespace lluz
{
struct RecursionLimitException : public std::exception
{
const char* what() const noexcept
{
return XorStr("Internal recursion counter limit exceeded");
}
};
struct RecursionCounter
{
RecursionCounter(int* count)
: count(count)
{
++(*count);
}
~RecursionCounter()
{
lluz_ASSERT(*count > 0);
--(*count);
}
private:
int* count;
};
struct RecursionLimiter : RecursionCounter
{
RecursionLimiter(int* count, int limit)
: RecursionCounter(count)
{
if (limit > 0 && *count > limit)
{
throw RecursionLimitException();
}
}
};
} // namespace lluz

View file

@ -0,0 +1,27 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/DenseHash.h"
#include "lluz/FileResolver.h"
#include "lluz/Location.h"
#include <string>
namespace lluz
{
class AstStat;
class AstExpr;
class AstStatBlock;
struct AstLocal;
struct RequireTraceResult
{
DenseHashMap<const AstExpr*, ModuleInfo> exprs{nullptr};
std::vector<std::pair<ModuleName, Location>> requireList;
};
RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName);
} // namespace lluz

View file

@ -0,0 +1,89 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Constraint.h"
#include "lluz/Location.h"
#include "lluz/NotNull.h"
#include "lluz/TypeVar.h"
#include <unordered_map>
#include <optional>
#include <memory>
namespace lluz
{
struct Scope;
using ScopePtr = std::shared_ptr<Scope>;
struct Binding
{
TypeId typeId;
Location location;
bool deprecated = false;
std::string deprecatedSuggestion;
std::optional<std::string> documentationSymbol;
};
struct Scope
{
explicit Scope(TypePackId returnType); // root scope
explicit Scope(const ScopePtr& parent, int subLevel = 0); // child scope. Parent must not be nullptr.
const ScopePtr parent; // null for the root
std::unordered_map<Symbol, Binding> bindings;
TypePackId returnType;
bool breakOk = false;
std::optional<TypePackId> varargPack;
TypeLevel level;
std::unordered_map<Name, TypeFun> exportedTypeBindings;
std::unordered_map<Name, TypeFun> privateTypeBindings;
std::unordered_map<Name, Location> typeAliasLocations;
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
std::optional<TypeId> lookup(const Symbol& name);
std::optional<TypeFun> lookupType(const Name& name);
std::optional<TypeFun> lookupImportedType(const Name& moduleAlias, const Name& name);
std::unordered_map<Name, TypePackId> privateTypePackBindings;
std::optional<TypePackId> lookupPack(const Name& name);
// WARNING: This function linearly scans for a string key of equal value! It is thus O(n**2)
std::optional<Binding> linearSearchForBinding(const std::string& name, bool traverseScopeChain = true);
RefinementMap refinements;
// For mutually recursive type aliases, it's important that
// they use the same types for the same names.
// For instance, in `type Tree<T> { data: T, children: Forest<T> } type Forest<T> = {Tree<T>}`
// we need that the generic type `T` in both cases is the same, so we use a cache.
std::unordered_map<Name, TypeId> typeAliasTypeParameters;
std::unordered_map<Name, TypePackId> typeAliasTypePackParameters;
};
struct Scope2
{
// The parent scope of this scope. Null if there is no parent (i.e. this
// is the module-level scope).
Scope2* parent = nullptr;
// All the children of this scope.
std::vector<NotNull<Scope2>> children;
std::unordered_map<Symbol, TypeId> bindings; // TODO: I think this can be a DenseHashMap
std::unordered_map<Name, TypeId> typeBindings;
std::unordered_map<Name, TypePackId> typePackBindings;
TypePackId returnType;
std::optional<TypePackId> varargPack;
// All constraints belonging to this scope.
std::vector<ConstraintPtr> constraints;
std::optional<TypeId> lookup(Symbol sym);
std::optional<TypeId> lookupTypeBinding(const Name& name);
std::optional<TypePackId> lookupTypePackBinding(const Name& name);
};
} // namespace lluz

View file

@ -0,0 +1,212 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/TypeArena.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
#include "lluz/DenseHash.h"
// We provide an implementation of substitution on types,
// which recursively replaces types by other types.
// Examples include quantification (replacing free types by generics)
// and instantiation (replacing generic types by free ones).
//
// To implement a substitution, implement a subclass of `Substitution`
// and provide implementations of `isDirty` (which should be true for types that
// should be replaced) and `clean` which replaces any dirty types.
//
// struct MySubst : Substitution
// {
// bool isDirty(TypeId ty) override { ... }
// bool isDirty(TypePackId tp) override { ... }
// TypeId clean(TypeId ty) override { ... }
// TypePackId clean(TypePackId tp) override { ... }
// bool ignoreChildren(TypeId ty) override { ... }
// bool ignoreChildren(TypePackId tp) override { ... }
// };
//
// For example, `Instantiation` in `TypeInfer.cpp` uses this.
// The implementation of substitution tries not to copy types
// unnecessarily. It first finds all the types which can reach
// a dirty type, and either cleans them (if they are dirty)
// or clones them (if they are not). It then updates the children
// of the newly created types. When considering reachability,
// we do not consider the children of any type where ignoreChildren(ty) is true.
// There is a gotcha for cyclic types, which means we can't just use
// a straightforward DFS. For example:
//
// type T = { f : () -> T, g: () -> number, h: X }
//
// If X is dirty, and is being replaced by X' then the result should be:
//
// type T' = { f : () -> T', g: () -> number, h: X' }
//
// that is the type of `f` is replaced, but the type of `g` is not.
//
// For this reason, we first use Tarjan's algorithm to find strongly
// connected components. If any type in an SCC can reach a dirty type,
// them the whole SCC can. For instance, in the above example,
// `T`, and the type of `f` are in the same SCC, which is why `f` gets
// replaced.
namespace lluz
{
struct TxnLog;
enum class TarjanResult
{
TooManyChildren,
Ok
};
struct TarjanWorklistVertex
{
int index;
int currEdge;
int lastEdge;
};
// Tarjan's algorithm for finding the SCCs in a cyclic structure.
// https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
struct Tarjan
{
// Vertices (types and type packs) are indexed, using pre-order traversal.
DenseHashMap<TypeId, int> typeToIndex{nullptr};
DenseHashMap<TypePackId, int> packToIndex{nullptr};
std::vector<TypeId> indexToType;
std::vector<TypePackId> indexToPack;
// Tarjan keeps a stack of vertices where we're still in the process
// of finding their SCC.
std::vector<int> stack;
std::vector<bool> onStack;
// Tarjan calculates the lowlink for each vertex,
// which is the lowest ancestor index reachable from the vertex.
std::vector<int> lowlink;
int childCount = 0;
int childLimit = 0;
// This should never be null; ensure you initialize it before calling
// substitution methods.
const TxnLog* log = nullptr;
std::vector<TypeId> edgesTy;
std::vector<TypePackId> edgesTp;
std::vector<TarjanWorklistVertex> worklist;
// This is hot code, so we optimize recursion to a stack.
TarjanResult loop();
// Find or create the index for a vertex.
// Return a boolean which is `true` if it's a freshly created index.
std::pair<int, bool> indexify(TypeId ty);
std::pair<int, bool> indexify(TypePackId tp);
// Recursively visit all the children of a vertex
void visitChildren(TypeId ty, int index);
void visitChildren(TypePackId tp, int index);
void visitChild(TypeId ty);
void visitChild(TypePackId ty);
// Visit the root vertex.
TarjanResult visitRoot(TypeId ty);
TarjanResult visitRoot(TypePackId ty);
// Each subclass gets called back once for each edge,
// and once for each SCC.
virtual void visitEdge(int index, int parentIndex) {}
virtual void visitSCC(int index) {}
// Each subclass can decide to ignore some nodes.
virtual bool ignoreChildren(TypeId ty)
{
return false;
}
virtual bool ignoreChildren(TypePackId ty)
{
return false;
}
};
// We use Tarjan to calculate dirty bits. We set `dirty[i]` true
// if the vertex with index `i` can reach a dirty vertex.
struct FindDirty : Tarjan
{
std::vector<bool> dirty;
// Get/set the dirty bit for an index (grows the vector if needed)
bool getDirty(int index);
void setDirty(int index, bool d);
// Find all the dirty vertices reachable from `t`.
TarjanResult findDirty(TypeId t);
TarjanResult findDirty(TypePackId t);
// We find dirty vertices using Tarjan
void visitEdge(int index, int parentIndex) override;
void visitSCC(int index) override;
// Subclasses should say which vertices are dirty,
// and what to do with dirty vertices.
virtual bool isDirty(TypeId ty) = 0;
virtual bool isDirty(TypePackId tp) = 0;
virtual void foundDirty(TypeId ty) = 0;
virtual void foundDirty(TypePackId tp) = 0;
};
// And finally substitution, which finds all the reachable dirty vertices
// and replaces them with clean ones.
struct Substitution : FindDirty
{
protected:
Substitution(const TxnLog* log_, TypeArena* arena)
: arena(arena)
{
log = log_;
lluz_ASSERT(log);
lluz_ASSERT(arena);
}
public:
TypeArena* arena;
DenseHashMap<TypeId, TypeId> newTypes{nullptr};
DenseHashMap<TypePackId, TypePackId> newPacks{nullptr};
std::optional<TypeId> substitute(TypeId ty);
std::optional<TypePackId> substitute(TypePackId tp);
TypeId replace(TypeId ty);
TypePackId replace(TypePackId tp);
void replaceChildren(TypeId ty);
void replaceChildren(TypePackId tp);
TypeId clone(TypeId ty);
TypePackId clone(TypePackId tp);
// Substitutions use Tarjan to find dirty nodes and replace them
void foundDirty(TypeId ty) override;
void foundDirty(TypePackId tp) override;
// Implementing subclasses define how to clean a dirty type.
virtual TypeId clean(TypeId ty) = 0;
virtual TypePackId clean(TypePackId tp) = 0;
// Helper functions to create new types (used by subclasses)
template<typename T>
TypeId addType(const T& tv)
{
return arena->addType(tv);
}
template<typename T>
TypePackId addTypePack(const T& tp)
{
return arena->addTypePack(TypePackVar{tp});
}
};
} // namespace lluz

View file

@ -0,0 +1,98 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Ast.h"
#include "lluz/Common.h"
#include <string>
namespace lluz
{
// TODO Rename this to Name once the old type alias is gone.
struct Symbol
{
Symbol()
: local(nullptr)
, global()
{
}
Symbol(AstLocal* local)
: local(local)
, global()
{
}
Symbol(const AstName& global)
: local(nullptr)
, global(global)
{
}
template<typename T>
Symbol(const T&) = delete;
AstLocal* local;
AstName global;
bool operator==(const Symbol& rhs) const
{
if (local)
return local == rhs.local;
if (global.value)
return rhs.global.value && global == rhs.global.value; // Subtlety: AstName::operator==(const char*) uses strcmp, not pointer identity.
return false;
}
bool operator!=(const Symbol& rhs) const
{
return !(*this == rhs);
}
bool operator<(const Symbol& rhs) const
{
if (local && rhs.local)
return local < rhs.local;
else if (global.value && rhs.global.value)
return global < rhs.global;
else if (local)
return true;
else
return false;
}
AstName astName() const
{
if (local)
return local->name;
lluz_ASSERT(global.value);
return global;
}
const char* c_str() const
{
if (local)
return local->name.value;
lluz_ASSERT(global.value);
return global.value;
}
};
std::string toString(const Symbol& name);
} // namespace lluz
namespace std
{
template<>
struct hash<lluz::Symbol>
{
std::size_t operator()(const lluz::Symbol& s) const noexcept
{
return std::hash<const lluz::AstLocal*>()(s.local) ^ (s.global.value ? std::hash<std::string_view>()(s.global.value) : 0);
}
};
} // namespace std

View file

@ -0,0 +1,31 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Common.h"
#include <string>
namespace lluz
{
struct TypeVar;
using TypeId = const TypeVar*;
struct TypePackVar;
using TypePackId = const TypePackVar*;
struct ToDotOptions
{
bool showPointers = true; // Show pointer value in the node label
bool duplicatePrimitives = true; // Display primitive types and 'any' as separate nodes
};
std::string toDot(TypeId ty, const ToDotOptions& opts);
std::string toDot(TypePackId tp, const ToDotOptions& opts);
std::string toDot(TypeId ty);
std::string toDot(TypePackId tp);
void dumpDot(TypeId ty);
void dumpDot(TypePackId tp);
} // namespace lluz

View file

@ -0,0 +1,90 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Common.h"
#include "lluz/TypeVar.h"
#include "lluz/ConstraintGraphBuilder.h"
#include <unordered_map>
#include <optional>
#include <memory>
#include <string>
lluz_FASTINT(LluTableTypeMaximumStringifierLength)
lluz_FASTINT(LluTypeMaximumStringifierLength)
namespace lluz
{
struct ToStringNameMap
{
std::unordered_map<TypeId, std::string> typeVars;
std::unordered_map<TypePackId, std::string> typePacks;
};
struct ToStringOptions
{
bool exhaustive = false; // If true, we produce complete output rather than comprehensible output
bool useLineBreaks = false; // If true, we insert new lines to separate long results such as table entries/metatable.
bool functionTypeArguments = false; // If true, output function type argument names when they are available
bool hideTableKind = false; // If true, all tables will be surrounded with plain '{}'
bool hideNamedFunctionTypeParameters = false; // If true, type parameters of functions will be hidden at top-level.
bool hideFunctionSelfArgument = false; // If true, `self: X` will be omitted from the function signature if the function has self
bool indent = false;
size_t maxTableLength = size_t(FInt::LluTableTypeMaximumStringifierLength); // Only applied to TableTypeVars
size_t maxTypeLength = size_t(FInt::LluTypeMaximumStringifierLength);
std::optional<ToStringNameMap> nameMap;
std::shared_ptr<Scope> scope; // If present, module names will be added and types that are not available in scope will be marked as 'invalid'
std::vector<std::string> namedFunctionOverrideArgNames; // If present, named function argument names will be overridden
};
struct ToStringResult
{
std::string name;
ToStringNameMap nameMap;
bool invalid = false;
bool error = false;
bool cycle = false;
bool truncated = false;
};
ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts = {});
ToStringResult toStringDetailed(TypePackId ty, const ToStringOptions& opts = {});
std::string toString(TypeId ty, const ToStringOptions& opts);
std::string toString(TypePackId ty, const ToStringOptions& opts);
std::string toString(const Constraint& c, ToStringOptions& opts);
// These are offered as overloads rather than a default parameter so that they can be easily invoked from within the MSVC debugger.
// You can use them in watch expressions!
inline std::string toString(TypeId ty)
{
return toString(ty, ToStringOptions{});
}
inline std::string toString(TypePackId ty)
{
return toString(ty, ToStringOptions{});
}
inline std::string toString(const Constraint& c)
{
ToStringOptions opts;
return toString(c, opts);
}
std::string toString(const TypeVar& tv, const ToStringOptions& opts = {});
std::string toString(const TypePackVar& tp, const ToStringOptions& opts = {});
std::string toStringNamedFunction(const std::string& funcName, const FunctionTypeVar& ftv, const ToStringOptions& opts = {});
// It could be useful to see the text representation of a type during a debugging session instead of exploring the content of the class
// These functions will dump the type to stdout and can be evaluated in Watch/Immediate windows or as gdb/lldb expression
std::string dump(TypeId ty);
std::string dump(TypePackId ty);
std::string dump(const Constraint& c);
std::string dump(const std::shared_ptr<Scope>& scope, const char* name);
std::string generateName(size_t n);
} // namespace lluz

View file

@ -0,0 +1,19 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <vector>
namespace lluz
{
template<typename T>
struct AstArray;
class AstStat;
bool containsFunctionCall(const AstStat& stat);
bool containsFunctionCallOrReturn(const AstStat& stat);
bool isFunction(const AstStat& stat);
void toposort(std::vector<AstStat*>& stats);
} // namespace lluz

View file

@ -0,0 +1,31 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Location.h"
#include "lluz/ParseOptions.h"
#include <string>
namespace lluz
{
class AstNode;
class AstStatBlock;
struct TranspileResult
{
std::string code;
Location errorLocation;
std::string parseError; // Nonempty if the transpile failed
};
std::string toString(AstNode* node);
void dump(AstNode* node);
// Never fails on a well-formed AST
std::string transpile(AstStatBlock& ast);
std::string transpileWithTypes(AstStatBlock& block);
// Only fails when parsing fails
TranspileResult transpile(std::string_view source, ParseOptions options = ParseOptions{}, bool withTypes = false);
} // namespace lluz

View file

@ -0,0 +1,291 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <memory>
#include <unordered_map>
#include "lluz/TypeVar.h"
#include "lluz/TypePack.h"
namespace lluz
{
using TypeOrPackId = const void*;
// Pending state for a TypeVar. Generated by a TxnLog and committed via
// TxnLog::commit.
struct PendingType
{
// The pending TypeVar state.
TypeVar pending;
explicit PendingType(TypeVar state)
: pending(std::move(state))
{
}
};
std::string toString(PendingType* pending);
std::string dump(PendingType* pending);
// Pending state for a TypePackVar. Generated by a TxnLog and committed via
// TxnLog::commit.
struct PendingTypePack
{
// The pending TypePackVar state.
TypePackVar pending;
explicit PendingTypePack(TypePackVar state)
: pending(std::move(state))
{
}
};
std::string toString(PendingTypePack* pending);
std::string dump(PendingTypePack* pending);
template<typename T>
T* getMutable(PendingType* pending)
{
// We use getMutable here because this state is intended to be mutated freely.
return getMutable<T>(&pending->pending);
}
template<typename T>
T* getMutable(PendingTypePack* pending)
{
// We use getMutable here because this state is intended to be mutated freely.
return getMutable<T>(&pending->pending);
}
// Log of what TypeIds we are rebinding, to be committed later.
struct TxnLog
{
TxnLog()
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, ownedSeen()
, sharedSeen(&ownedSeen)
{
}
explicit TxnLog(TxnLog* parent)
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, parent(parent)
{
if (parent)
{
sharedSeen = parent->sharedSeen;
}
else
{
sharedSeen = &ownedSeen;
}
}
explicit TxnLog(std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen)
: typeVarChanges(nullptr)
, typePackChanges(nullptr)
, sharedSeen(sharedSeen)
{
}
TxnLog(const TxnLog&) = delete;
TxnLog& operator=(const TxnLog&) = delete;
TxnLog(TxnLog&&) = default;
TxnLog& operator=(TxnLog&&) = default;
// Gets an empty TxnLog pointer. This is useful for constructs that
// take a TxnLog, like TypePackIterator - use the empty log if you
// don't have a TxnLog to give it.
static const TxnLog* empty();
// Joins another TxnLog onto this one. You should use std::move to avoid
// copying the rhs TxnLog.
//
// If both logs talk about the same type, pack, or table, the rhs takes
// priority.
void concat(TxnLog rhs);
// Commits the TxnLog, rebinding all type pointers to their pending states.
// Clears the TxnLog afterwards.
void commit();
// Clears the TxnLog without committing any pending changes.
void clear();
// Computes an inverse of this TxnLog at the current time.
// This method should be called before commit is called in order to give an
// accurate result. Committing the inverse of a TxnLog will undo the changes
// made by commit, assuming the inverse log is accurate.
TxnLog inverse();
bool haveSeen(TypeId lhs, TypeId rhs) const;
void pushSeen(TypeId lhs, TypeId rhs);
void popSeen(TypeId lhs, TypeId rhs);
bool haveSeen(TypePackId lhs, TypePackId rhs) const;
void pushSeen(TypePackId lhs, TypePackId rhs);
void popSeen(TypePackId lhs, TypePackId rhs);
// Queues a type for modification. The original type will not change until commit
// is called. Use pending to get the pending state.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* queue(TypeId ty);
// Queues a type pack for modification. The original type pack will not change
// until commit is called. Use pending to get the pending state.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* queue(TypePackId tp);
// Returns the pending state of a type, or nullptr if there isn't any. It is important
// to note that this pending state is not transitive: the pending state may reference
// non-pending types freely, so you may need to call pending multiple times to view the
// entire pending state of a type graph.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* pending(TypeId ty) const;
// Returns the pending state of a type pack, or nullptr if there isn't any. It is
// important to note that this pending state is not transitive: the pending state may
// reference non-pending types freely, so you may need to call pending multiple times
// to view the entire pending state of a type graph.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* pending(TypePackId tp) const;
// Queues a replacement of a type with another type.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* replace(TypeId ty, TypeVar replacement);
// Queues a replacement of a type pack with another type pack.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* replace(TypePackId tp, TypePackVar replacement);
// Queues a replacement of a table type with another table type that is bound
// to a specific value.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* bindTable(TypeId ty, std::optional<TypeId> newBoundTo);
// Queues a replacement of a type with a level with a duplicate of that type
// with a new type level.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* changeLevel(TypeId ty, TypeLevel newLevel);
// Queues a replacement of a type pack with a level with a duplicate of that
// type pack with a new type level.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingTypePack* changeLevel(TypePackId tp, TypeLevel newLevel);
// Queues a replacement of a table type with another table type with a new
// indexer.
//
// The pointer returned lives until `commit` or `clear` is called.
PendingType* changeIndexer(TypeId ty, std::optional<TableIndexer> indexer);
// Returns the type level of the pending state of the type, or the level of that
// type, if no pending state exists. If the type doesn't have a notion of a level,
// returns nullopt. If the pending state doesn't have a notion of a level, but the
// original state does, returns nullopt.
std::optional<TypeLevel> getLevel(TypeId ty) const;
// Follows a type, accounting for pending type states. The returned type may have
// pending state; you should use `pending` or `get` to find out.
TypeId follow(TypeId ty) const;
// Follows a type pack, accounting for pending type states. The returned type pack
// may have pending state; you should use `pending` or `get` to find out.
TypePackId follow(TypePackId tp) const;
// Replaces a given type's state with a new variant. Returns the new pending state
// of that type.
//
// The pointer returned lives until `commit` or `clear` is called.
template<typename T>
PendingType* replace(TypeId ty, T replacement)
{
return replace(ty, TypeVar(replacement));
}
// Replaces a given type pack's state with a new variant. Returns the new
// pending state of that type pack.
//
// The pointer returned lives until `commit` or `clear` is called.
template<typename T>
PendingTypePack* replace(TypePackId tp, T replacement)
{
return replace(tp, TypePackVar(replacement));
}
// Returns T if a given type or type pack is this variant, respecting the
// log's pending state.
//
// Do not retain this pointer; it has the potential to be invalidated when
// commit or clear is called.
template<typename T, typename TID>
T* getMutable(TID ty) const
{
auto* pendingTy = pending(ty);
if (pendingTy)
return lluz::getMutable<T>(pendingTy);
return lluz::getMutable<T>(ty);
}
template<typename T, typename TID>
const T* get(TID ty) const
{
return this->getMutable<T>(ty);
}
// Returns whether a given type or type pack is a given state, respecting the
// log's pending state.
//
// This method will not assert if called on a BoundTypeVar or BoundTypePack.
template<typename T, typename TID>
bool is(TID ty) const
{
// We do not use getMutable here because this method can be called on
// BoundTypeVars, which triggers an assertion.
auto* pendingTy = pending(ty);
if (pendingTy)
return lluz::get_if<T>(&pendingTy->pending.ty) != nullptr;
return lluz::get_if<T>(&ty->ty) != nullptr;
}
private:
// unique_ptr is used to give us stable pointers across insertions into the
// map. Otherwise, it would be really easy to accidentally invalidate the
// pointers returned from queue/pending.
DenseHashMap<TypeId, std::unique_ptr<PendingType>> typeVarChanges;
DenseHashMap<TypePackId, std::unique_ptr<PendingTypePack>> typePackChanges;
TxnLog* parent = nullptr;
// Owned version of sharedSeen. This should not be accessed directly in
// TxnLogs; use sharedSeen instead. This field exists because in the tree
// of TxnLogs, the root must own its seen set. In all descendant TxnLogs,
// this is an empty vector.
std::vector<std::pair<TypeOrPackId, TypeOrPackId>> ownedSeen;
bool haveSeen(TypeOrPackId lhs, TypeOrPackId rhs) const;
void pushSeen(TypeOrPackId lhs, TypeOrPackId rhs);
void popSeen(TypeOrPackId lhs, TypeOrPackId rhs);
public:
// Used to avoid infinite recursion when types are cyclic.
// Shared with all the descendent TxnLogs.
std::vector<std::pair<TypeOrPackId, TypeOrPackId>>* sharedSeen;
};
} // namespace lluz

View file

@ -0,0 +1,48 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/TypedAllocator.h"
#include "lluz/TypeVar.h"
#include "lluz/TypePack.h"
#include <vector>
namespace lluz
{
struct TypeArena
{
TypedAllocator<TypeVar> typeVars;
TypedAllocator<TypePackVar> typePacks;
void clear();
template<typename T>
TypeId addType(T tv)
{
if constexpr (std::is_same_v<T, UnionTypeVar>)
lluz_ASSERT(tv.options.size() >= 2);
return addTV(TypeVar(std::move(tv)));
}
TypeId addTV(TypeVar&& tv);
TypeId freshType(TypeLevel level);
TypePackId addTypePack(std::initializer_list<TypeId> types);
TypePackId addTypePack(std::vector<TypeId> types);
TypePackId addTypePack(TypePack pack);
TypePackId addTypePack(TypePackVar pack);
template<typename T>
TypePackId addTypePack(T tp)
{
return addTypePack(TypePackVar(std::move(tp)));
}
};
void freeze(TypeArena& arena);
void unfreeze(TypeArena& arena);
} // namespace lluz

View file

@ -0,0 +1,21 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Module.h"
#include <unordered_set>
namespace lluz
{
struct TypeRehydrationOptions
{
std::unordered_set<std::string> bannedNames;
bool expandClassProps = false;
};
void attachTypeData(SourceModule& source, Module& result);
AstType* rehydrateAnnotation(TypeId type, Allocator* allocator, const TypeRehydrationOptions& options = {});
} // namespace lluz

View file

@ -0,0 +1,13 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Ast.h"
#include "lluz/Module.h"
namespace lluz
{
void check(const SourceModule& sourceModule, Module* module);
} // namespace lluz

View file

@ -0,0 +1,436 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Predicate.h"
#include "lluz/Error.h"
#include "lluz/Module.h"
#include "lluz/Symbol.h"
#include "lluz/Substitution.h"
#include "lluz/TxnLog.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
#include "lluz/Unifier.h"
#include "lluz/UnifierSharedState.h"
#include <memory>
#include <unordered_map>
#include <unordered_set>
namespace lluz
{
struct Scope;
struct TypeChecker;
struct ModuleResolver;
using Name = std::string;
using ScopePtr = std::shared_ptr<Scope>;
using OverloadErrorEntry = std::tuple<std::vector<TypeError>, std::vector<TypeId>, const FunctionTypeVar*>;
bool doesCallError(const AstExprCall* call);
bool hasBreak(AstStat* node);
const AstStat* getFallthrough(const AstStat* node);
struct UnifierOptions;
struct Unifier;
// A substitution which replaces free types by any
struct Anyification : Substitution
{
Anyification(TypeArena* arena, InternalErrorReporter* iceHandler, TypeId anyType, TypePackId anyTypePack)
: Substitution(TxnLog::empty(), arena)
, iceHandler(iceHandler)
, anyType(anyType)
, anyTypePack(anyTypePack)
{
}
InternalErrorReporter* iceHandler;
TypeId anyType;
TypePackId anyTypePack;
bool normalizationTooComplex = false;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
bool ignoreChildren(TypeId ty) override
{
return ty->persistent;
}
bool ignoreChildren(TypePackId ty) override
{
return ty->persistent;
}
};
// A substitution which replaces the type parameters of a type function by arguments
struct ApplyTypeFunction : Substitution
{
ApplyTypeFunction(TypeArena* arena, TypeLevel level)
: Substitution(TxnLog::empty(), arena)
, level(level)
, encounteredForwardedType(false)
{
}
TypeLevel level;
bool encounteredForwardedType;
std::unordered_map<TypeId, TypeId> typeArguments;
std::unordered_map<TypePackId, TypePackId> typePackArguments;
bool ignoreChildren(TypeId ty) override;
bool ignoreChildren(TypePackId tp) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
struct GenericTypeDefinitions
{
std::vector<GenericTypeDefinition> genericTypes;
std::vector<GenericTypePackDefinition> genericPacks;
};
struct HashBoolNamePair
{
size_t operator()(const std::pair<bool, Name>& pair) const;
};
class TimeLimitError : public std::exception
{
public:
virtual const char* what() const throw();
};
// All TypeVars are retained via Environment::typeVars. All TypeIds
// within a program are borrowed pointers into this set.
struct TypeChecker
{
explicit TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHandler);
TypeChecker(const TypeChecker&) = delete;
TypeChecker& operator=(const TypeChecker&) = delete;
ModulePtr check(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope = std::nullopt);
ModulePtr checkWithoutRecursionCheck(const SourceModule& module, Mode mode, std::optional<ScopePtr> environmentScope = std::nullopt);
std::vector<std::pair<Location, ScopePtr>> getScopes() const;
void check(const ScopePtr& scope, const AstStat& statement);
void check(const ScopePtr& scope, const AstStatBlock& statement);
void check(const ScopePtr& scope, const AstStatIf& statement);
void check(const ScopePtr& scope, const AstStatWhile& statement);
void check(const ScopePtr& scope, const AstStatRepeat& statement);
void check(const ScopePtr& scope, const AstStatReturn& return_);
void check(const ScopePtr& scope, const AstStatAssign& assign);
void check(const ScopePtr& scope, const AstStatCompoundAssign& assign);
void check(const ScopePtr& scope, const AstStatLocal& local);
void check(const ScopePtr& scope, const AstStatFor& local);
void check(const ScopePtr& scope, const AstStatForIn& forin);
void check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatFunction& function);
void check(const ScopePtr& scope, TypeId ty, const ScopePtr& funScope, const AstStatLocalFunction& function);
void check(const ScopePtr& scope, const AstStatTypeAlias& typealias, int subLevel = 0, bool forwardDeclare = false);
void check(const ScopePtr& scope, const AstStatDeclareClass& declaredClass);
void check(const ScopePtr& scope, const AstStatDeclareFunction& declaredFunction);
void checkBlock(const ScopePtr& scope, const AstStatBlock& statement);
void checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& statement);
void checkBlockTypeAliases(const ScopePtr& scope, std::vector<AstStat*>& sorted);
WithPredicate<TypeId> checkExpr(
const ScopePtr& scope, const AstExpr& expr, std::optional<TypeId> expectedType = std::nullopt, bool forceSingleton = false);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprLocal& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprGlobal& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprVarargs& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprCall& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIndexName& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIndexExpr& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprFunction& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTable& expr, std::optional<TypeId> expectedType = std::nullopt);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprUnary& expr);
TypeId checkRelationalOperation(
const ScopePtr& scope, const AstExprBinary& expr, TypeId lhsType, TypeId rhsType, const PredicateVec& predicates = {});
TypeId checkBinaryOperation(
const ScopePtr& scope, const AstExprBinary& expr, TypeId lhsType, TypeId rhsType, const PredicateVec& predicates = {});
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprBinary& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprError& expr);
WithPredicate<TypeId> checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional<TypeId> expectedType = std::nullopt);
TypeId checkExprTable(const ScopePtr& scope, const AstExprTable& expr, const std::vector<std::pair<TypeId, TypeId>>& fieldTypes,
std::optional<TypeId> expectedType);
// Returns the type of the lvalue.
TypeId checkLValue(const ScopePtr& scope, const AstExpr& expr);
// Returns the type of the lvalue.
TypeId checkLValueBinding(const ScopePtr& scope, const AstExpr& expr);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprLocal& expr);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprGlobal& expr);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprIndexName& expr);
TypeId checkLValueBinding(const ScopePtr& scope, const AstExprIndexExpr& expr);
TypeId checkFunctionName(const ScopePtr& scope, AstExpr& funName, TypeLevel level);
std::pair<TypeId, ScopePtr> checkFunctionSignature(const ScopePtr& scope, int subLevel, const AstExprFunction& expr,
std::optional<Location> originalNameLoc, std::optional<TypeId> selfType, std::optional<TypeId> expectedType);
void checkFunctionBody(const ScopePtr& scope, TypeId type, const AstExprFunction& function);
void checkArgumentList(
const ScopePtr& scope, Unifier& state, TypePackId paramPack, TypePackId argPack, const std::vector<Location>& argLocations);
WithPredicate<TypePackId> checkExprPack(const ScopePtr& scope, const AstExpr& expr);
WithPredicate<TypePackId> checkExprPack(const ScopePtr& scope, const AstExprCall& expr);
std::vector<std::optional<TypeId>> getExpectedTypesForCall(const std::vector<TypeId>& overloads, size_t argumentCount, bool selfCall);
std::optional<WithPredicate<TypePackId>> checkCallOverload(const ScopePtr& scope, const AstExprCall& expr, TypeId fn, TypePackId retPack,
TypePackId argPack, TypePack* args, const std::vector<Location>* argLocations, const WithPredicate<TypePackId>& argListResult,
std::vector<TypeId>& overloadsThatMatchArgCount, std::vector<TypeId>& overloadsThatDont, std::vector<OverloadErrorEntry>& errors);
bool handleSelfCallMismatch(const ScopePtr& scope, const AstExprCall& expr, TypePack* args, const std::vector<Location>& argLocations,
const std::vector<OverloadErrorEntry>& errors);
void reportOverloadResolutionError(const ScopePtr& scope, const AstExprCall& expr, TypePackId retPack, TypePackId argPack,
const std::vector<Location>& argLocations, const std::vector<TypeId>& overloads, const std::vector<TypeId>& overloadsThatMatchArgCount,
const std::vector<OverloadErrorEntry>& errors);
WithPredicate<TypePackId> checkExprList(const ScopePtr& scope, const Location& location, const AstArray<AstExpr*>& exprs,
bool substituteFreeForNil = false, const std::vector<bool>& lhsAnnotations = {},
const std::vector<std::optional<TypeId>>& expectedTypes = {});
static std::optional<AstExpr*> matchRequire(const AstExprCall& call);
TypeId checkRequire(const ScopePtr& scope, const ModuleInfo& moduleInfo, const Location& location);
// Try to infer that the provided type is a table of some sort.
// Reports an error if the type is already some kind of non-table.
void tablify(TypeId type);
/** In nonstrict mode, many typevars need to be replaced by any.
*/
TypeId anyIfNonstrict(TypeId ty) const;
/** Attempt to unify the types.
* Treat any failures as type errors in the final typecheck report.
*/
bool unify(TypeId subTy, TypeId superTy, const Location& location);
bool unify(TypeId subTy, TypeId superTy, const Location& location, const UnifierOptions& options);
bool unify(TypePackId subTy, TypePackId superTy, const Location& location, CountMismatch::Context ctx = CountMismatch::Context::Arg);
/** Attempt to unify the types.
* If this fails, and the subTy type can be instantiated, do so and try unification again.
*/
bool unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId subTy, TypeId superTy, const Location& location);
void unifyWithInstantiationIfNeeded(const ScopePtr& scope, TypeId subTy, TypeId superTy, Unifier& state);
/** Attempt to unify.
* If there are errors, undo everything and return the errors.
* If there are no errors, commit and return an empty error vector.
*/
template<typename Id>
ErrorVec tryUnify_(Id subTy, Id superTy, const Location& location);
ErrorVec tryUnify(TypeId subTy, TypeId superTy, const Location& location);
ErrorVec tryUnify(TypePackId subTy, TypePackId superTy, const Location& location);
// Test whether the two type vars unify. Never commits the result.
template<typename Id>
ErrorVec canUnify_(Id subTy, Id superTy, const Location& location);
ErrorVec canUnify(TypeId subTy, TypeId superTy, const Location& location);
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, const Location& location);
void unifyLowerBound(TypePackId subTy, TypePackId superTy, TypeLevel demotedLevel, const Location& location);
std::optional<TypeId> findMetatableEntry(TypeId type, std::string entry, const Location& location);
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location);
std::optional<TypeId> getIndexTypeFromType(const ScopePtr& scope, TypeId type, const Name& name, const Location& location, bool addErrors);
// Reduces the union to its simplest possible shape.
// (A | B) | B | C yields A | B | C
std::vector<TypeId> reduceUnion(const std::vector<TypeId>& types);
std::optional<TypeId> tryStripUnionFromNil(TypeId ty);
TypeId stripFromNilAndReport(TypeId ty, const Location& location);
public:
/*
* Convert monotype into a a polytype, by replacing any metavariables in descendant scopes
* by bound generic type variables. This is used to infer that a function is generic.
*/
TypeId quantify(const ScopePtr& scope, TypeId ty, Location location);
/*
* Convert a polytype into a monotype, by replacing any bound generic types by type metavariables.
* This is used to typecheck particular calls to generic functions, and when generic functions
* are passed as arguments.
*
* The "changed" boolean is used to permit us to return the same TypeId in the case that the instantiated type is unchanged.
* This is important in certain cases, such as methods on objects, where a table contains a function whose first argument is the table.
* Without this property, we can wind up in a situation where a new TypeId is allocated for the outer table. This can cause us to produce
* unfortunate types like
*
* {method: ({method: (<CYCLE>) -> a}) -> a}
*
*/
TypeId instantiate(const ScopePtr& scope, TypeId ty, Location location, const TxnLog* log = TxnLog::empty());
// Replace any free types or type packs by `any`.
// This is used when exporting types from modules, to make sure free types don't leak.
TypeId anyify(const ScopePtr& scope, TypeId ty, Location location);
TypePackId anyify(const ScopePtr& scope, TypePackId ty, Location location);
void reportError(const TypeError& error);
void reportError(const Location& location, TypeErrorData error);
void reportErrors(const ErrorVec& errors);
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);
ScopePtr childFunctionScope(const ScopePtr& parent, const Location& location, int subLevel = 0);
ScopePtr childScope(const ScopePtr& parent, const Location& location);
// Wrapper for merge(l, r, toUnion) but without the lambda junk.
void merge(RefinementMap& l, const RefinementMap& r);
// Produce an "emergency backup type" for recovery from type errors.
// This comes in two flavours, depening on whether or not we can make a good guess
// for an error recovery type.
TypeId errorRecoveryType(TypeId guess);
TypePackId errorRecoveryTypePack(TypePackId guess);
TypeId errorRecoveryType(const ScopePtr& scope);
TypePackId errorRecoveryTypePack(const ScopePtr& scope);
private:
void prepareErrorsForDisplay(ErrorVec& errVec);
void diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& data);
void reportErrorCodeTooComplex(const Location& location);
private:
Unifier mkUnifier(const Location& location);
// These functions are only safe to call when we are in the process of typechecking a module.
// Produce a new free type var.
TypeId freshType(const ScopePtr& scope);
TypeId freshType(TypeLevel level);
// Produce a new singleton type var.
TypeId singletonType(bool value);
TypeId singletonType(std::string value);
TypeIdPredicate mkTruthyPredicate(bool sense);
// Returns nullopt if the predicate filters down the TypeId to 0 options.
std::optional<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
public:
std::optional<TypeId> pickTypesFromSense(TypeId type, bool sense);
private:
TypeId unionOfTypes(TypeId a, TypeId b, const Location& location, bool unifyFreeTypes = true);
// ex
// TypeId id = addType(FreeTypeVar());
template<typename T>
TypeId addType(const T& tv)
{
return addTV(TypeVar(tv));
}
TypeId addTV(TypeVar&& tv);
TypePackId addTypePack(TypePackVar&& tp);
TypePackId addTypePack(TypePack&& tp);
TypePackId addTypePack(const std::vector<TypeId>& ty);
TypePackId addTypePack(const std::vector<TypeId>& ty, std::optional<TypePackId> tail);
TypePackId addTypePack(std::initializer_list<TypeId>&& ty);
TypePackId freshTypePack(const ScopePtr& scope);
TypePackId freshTypePack(TypeLevel level);
TypeId resolveType(const ScopePtr& scope, const AstType& annotation);
TypeId resolveTypeWorker(const ScopePtr& scope, const AstType& annotation);
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypeList& types);
TypePackId resolveTypePack(const ScopePtr& scope, const AstTypePack& annotation);
TypeId instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& typePackParams, const Location& location);
// Note: `scope` must be a fresh scope.
GenericTypeDefinitions createGenericTypes(const ScopePtr& scope, std::optional<TypeLevel> levelOpt, const AstNode& node,
const AstArray<AstGenericType>& genericNames, const AstArray<AstGenericTypePack>& genericPackNames, bool useCache = false);
public:
void resolve(const PredicateVec& predicates, const ScopePtr& scope, bool sense);
private:
void refineLValue(const LValue& lvalue, RefinementMap& refis, const ScopePtr& scope, TypeIdPredicate predicate);
std::optional<TypeId> resolveLValue(const ScopePtr& scope, const LValue& lvalue);
std::optional<TypeId> resolveLValue(const RefinementMap& refis, const ScopePtr& scope, const LValue& lvalue);
void resolve(const PredicateVec& predicates, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr = false);
void resolve(const Predicate& predicate, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr);
void resolve(const TruthyPredicate& truthyP, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr);
void resolve(const AndPredicate& andP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const OrPredicate& orP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const IsAPredicate& isaP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const TypeGuardPredicate& typeguardP, RefinementMap& refis, const ScopePtr& scope, bool sense);
void resolve(const EqPredicate& eqP, RefinementMap& refis, const ScopePtr& scope, bool sense);
bool isNonstrictMode() const;
bool useConstrainedIntersections() const;
public:
/** Extract the types in a type pack, given the assumption that the pack must have some exact length.
* TypePacks can have free tails, which means that inference has not yet determined the length of the pack.
* Calling this function means submitting evidence that the pack must have the length provided.
* If the pack is known not to have the correct length, an error will be reported.
* The return vector is always of the exact requested length. In the event that the pack's length does
* not match up, excess TypeIds will be ErrorTypeVars.
*/
std::vector<TypeId> unTypePack(const ScopePtr& scope, TypePackId pack, size_t expectedLength, const Location& location);
TypeArena globalTypes;
ModuleResolver* resolver;
SourceModule globalNames; // names for symbols entered into globalScope
ScopePtr globalScope; // shared by all modules
ModulePtr currentModule;
ModuleName currentModuleName;
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope;
InternalErrorReporter* iceHandler;
UnifierSharedState unifierState;
std::vector<RequireCycle> requireCycles;
// Type inference limits
std::optional<double> finishTime;
std::optional<int> instantiationChildLimit;
std::optional<int> unifierIterationLimit;
public:
const TypeId nilType;
const TypeId numberType;
const TypeId stringType;
const TypeId booleanType;
const TypeId threadType;
const TypeId anyType;
const TypePackId anyTypePack;
private:
int checkRecursionCount = 0;
int recursionCount = 0;
/**
* We use this to avoid doing second-pass analysis of type aliases that are duplicates. We record a pair
* (exported, name) to properly deal with the case where the two duplicates do not have the same export status.
*/
DenseHashSet<std::pair<bool, Name>, HashBoolNamePair> duplicateTypeAliases;
std::vector<std::pair<TypeId, ScopePtr>> deferredQuantification;
};
// Unit test hook
void setPrintLine(void (*pl)(const std::string& s));
void resetPrintLine();
} // namespace lluz

View file

@ -0,0 +1,177 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/TypeVar.h"
#include "lluz/Unifiable.h"
#include "lluz/Variant.h"
#include <optional>
#include <set>
namespace lluz
{
struct TypeArena;
struct TypePack;
struct VariadicTypePack;
struct TypePackVar;
struct TxnLog;
using TypePackId = const TypePackVar*;
using FreeTypePack = Unifiable::Free;
using BoundTypePack = Unifiable::Bound<TypePackId>;
using GenericTypePack = Unifiable::Generic;
using TypePackVariant = Unifiable::Variant<TypePackId, TypePack, VariadicTypePack>;
/* A TypePack is a rope-like string of TypeIds. We use this structure to encode
* notions like packs of unknown length and packs of any length, as well as more
* nuanced compositions like "a pack which is a number prepended to this other pack,"
* or "a pack that is 2 numbers followed by any number of any other types."
*/
struct TypePack
{
std::vector<TypeId> head;
std::optional<TypePackId> tail;
};
struct VariadicTypePack
{
TypeId ty;
bool hidden = false; // if true, we don't display this when toString()ing a pack with this variadic as its tail.
};
struct TypePackVar
{
explicit TypePackVar(const TypePackVariant& ty);
explicit TypePackVar(TypePackVariant&& ty);
TypePackVar(TypePackVariant&& ty, bool persistent);
bool operator==(const TypePackVar& rhs) const;
TypePackVar& operator=(TypePackVariant&& tp);
TypePackVar& operator=(const TypePackVar& rhs);
// Re-assignes the content of the pack, but doesn't change the owning arena and can't make pack persistent.
void reassign(const TypePackVar& rhs)
{
ty = rhs.ty;
}
TypePackVariant ty;
bool persistent = false;
// Pointer to the type arena that allocated this pack.
TypeArena* owningArena = nullptr;
};
/* Walk the set of TypeIds in a TypePack.
*
* Like TypeVars, individual TypePacks can be free, generic, or any.
*
* We afford the ability to work with these kinds of packs by giving the
* iterator a .tail() property that yields the tail-most TypePack in the
* rope.
*
* It is very commonplace to want to walk each type in a pack, then handle
* the tail specially. eg when checking parameters, it might be the case
* that the parameter pack ends with a VariadicTypePack. In this case, we
* want to allow any number of extra arguments.
*
* The iterator obtained by calling end(tp) does not have a .tail(), but is
* equivalent with end(tp2) for any two type packs.
*/
struct TypePackIterator
{
using value_type = lluz::TypeId;
using pointer = value_type*;
using reference = value_type&;
using difference_type = size_t;
using iterator_category = std::input_iterator_tag;
TypePackIterator() = default;
explicit TypePackIterator(TypePackId tp);
TypePackIterator(TypePackId tp, const TxnLog* log);
TypePackIterator& operator++();
TypePackIterator operator++(int);
bool operator!=(const TypePackIterator& rhs);
bool operator==(const TypePackIterator& rhs);
const TypeId& operator*();
/** Return the tail of a TypePack.
* This may *only* be called on an iterator that has been incremented to the end.
* Returns nullopt if the pack has fixed length.
*/
std::optional<TypePackId> tail();
friend TypePackIterator end(TypePackId tp);
private:
TypePackId currentTypePack = nullptr;
const TypePack* tp = nullptr;
size_t currentIndex = 0;
const TxnLog* log;
};
TypePackIterator begin(TypePackId tp);
TypePackIterator begin(TypePackId tp, const TxnLog* log);
TypePackIterator end(TypePackId tp);
using SeenSet = std::set<std::pair<const void*, const void*>>;
bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs);
TypePackId follow(TypePackId tp);
TypePackId follow(TypePackId tp, std::function<TypePackId(TypePackId)> mapper);
size_t size(TypePackId tp, TxnLog* log = nullptr);
bool finite(TypePackId tp, TxnLog* log = nullptr);
size_t size(const TypePack& tp, TxnLog* log = nullptr);
std::optional<TypeId> first(TypePackId tp, bool ignoreHiddenVariadics = true);
TypePackVar* asMutable(TypePackId tp);
TypePack* asMutable(const TypePack* tp);
template<typename T>
const T* get(TypePackId tp)
{
lluz_ASSERT(tp);
if constexpr (!std::is_same_v<T, BoundTypePack>)
lluz_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
return get_if<T>(&(tp->ty));
}
template<typename T>
T* getMutable(TypePackId tp)
{
lluz_ASSERT(tp);
if constexpr (!std::is_same_v<T, BoundTypePack>)
lluz_ASSERT(get_if<BoundTypePack>(&tp->ty) == nullptr);
return get_if<T>(&(asMutable(tp)->ty));
}
/// Returns true if the type pack is known to be empty (no types in the head and no/an empty tail).
bool isEmpty(TypePackId tp);
/// Flattens out a type pack. Also returns a valid TypePackId tail if the type pack's full size is not known
std::pair<std::vector<TypeId>, std::optional<TypePackId>> flatten(TypePackId tp);
std::pair<std::vector<TypeId>, std::optional<TypePackId>> flatten(TypePackId tp, const TxnLog& log);
/// Returs true if the type pack arose from a function that is declared to be variadic.
/// Returns *false* for function argument packs that are inferred to be safe to oversaturate!
bool isVariadic(TypePackId tp);
bool isVariadic(TypePackId tp, const TxnLog& log);
} // namespace lluz

View file

@ -0,0 +1,19 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Error.h"
#include "lluz/Location.h"
#include "lluz/TypeVar.h"
#include <memory>
#include <optional>
namespace lluz
{
using ScopePtr = std::shared_ptr<struct Scope>;
std::optional<TypeId> findMetatableEntry(ErrorVec& errors, TypeId type, std::string entry, Location location);
std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, TypeId ty, Name name, Location location);
} // namespace lluz

View file

@ -0,0 +1,685 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/DenseHash.h"
#include "lluz/Predicate.h"
#include "lluz/Unifiable.h"
#include "lluz/Variant.h"
#include "lluz/Common.h"
#include <set>
#include <string>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <deque>
#include <memory>
#include <optional>
lluz_FASTINT(LluTableTypeMaximumStringifierLength)
lluz_FASTINT(LluTypeMaximumStringifierLength)
namespace lluz
{
struct TypeArena;
struct Scope2;
/**
* There are three kinds of type variables:
* - `Free` variables are metavariables, which stand for unconstrained types.
* - `Bound` variables are metavariables that have an equality constraint.
* - `Generic` variables are type variables that are bound by generic functions.
*
* For example, consider the program:
* ```
* function(x, y) x.f = y end
* ```
* To typecheck this, we first introduce free metavariables for the types of `x` and `y`:
* ```
* function(x: X, y: Y) x.f = y end
* ```
* Type inference for the function body then produces the constraint:
* ```
* X = { f: Y }
* ```
* so `X` is now a bound metavariable. We can then quantify the metavariables,
* which replaces any bound metavariables by their binding, and free metavariables
* by bound generic variables:
* ```
* function<a>(x: { f: a }, y: a) x.f = y end
* ```
*/
// So... why `const T*` here rather than `T*`?
// It's because we've had problems caused by the type graph being mutated
// in ways it shouldn't be, for example mutating types from other modules.
// To try to control this, we make the use of types immutable by default,
// then provide explicit mutable access via getMutable and asMutable.
// This means we can grep for all the places we're mutating the type graph,
// and it makes it possible to provide other APIs (e.g. the txn log)
// which control mutable access to the type graph.
struct TypePackVar;
using TypePackId = const TypePackVar*;
// TODO: rename to Type? CLI-39100
struct TypeVar;
// Should never be null
using TypeId = const TypeVar*;
using Name = std::string;
// A free type var is one whose exact shape has yet to be fully determined.
using FreeTypeVar = Unifiable::Free;
// When a free type var is unified with any other, it is then "bound"
// to that type var, indicating that the two types are actually the same type.
using BoundTypeVar = Unifiable::Bound<TypeId>;
using GenericTypeVar = Unifiable::Generic;
using Tags = std::vector<std::string>;
using ModuleName = std::string;
/** A TypeVar that cannot be computed.
*
* BlockedTypeVars essentially serve as a way to encode partial ordering on the
* constraint graph. Until a BlockedTypeVar is unblocked by its owning
* constraint, nothing at all can be said about it. Constraints that need to
* process a BlockedTypeVar cannot be dispatched.
*
* Whenever a BlockedTypeVar is added to the graph, we also record a constraint
* that will eventually unblock it.
*/
struct BlockedTypeVar
{
BlockedTypeVar();
int index;
static int nextIndex;
};
struct PrimitiveTypeVar
{
enum Type
{
NilType, // ObjC #defines Nil :(
Boolean,
Number,
String,
Thread,
};
Type type;
std::optional<TypeId> metatable; // string has a metatable
explicit PrimitiveTypeVar(Type type)
: type(type)
{
}
explicit PrimitiveTypeVar(Type type, TypeId metatable)
: type(type)
, metatable(metatable)
{
}
};
struct ConstrainedTypeVar
{
explicit ConstrainedTypeVar(TypeLevel level)
: level(level)
{
}
explicit ConstrainedTypeVar(TypeLevel level, const std::vector<TypeId>& parts)
: parts(parts)
, level(level)
{
}
std::vector<TypeId> parts;
TypeLevel level;
Scope2* scope = nullptr;
};
// Singleton types https://github.com/Roblox/lluz/blob/master/rfcs/syntax-singleton-types.md
// Types for true and false
struct BooleanSingleton
{
bool value;
bool operator==(const BooleanSingleton& rhs) const
{
return value == rhs.value;
}
bool operator!=(const BooleanSingleton& rhs) const
{
return !(*this == rhs);
}
};
// Types for "foo", "bar" etc.
struct StringSingleton
{
std::string value;
bool operator==(const StringSingleton& rhs) const
{
return value == rhs.value;
}
bool operator!=(const StringSingleton& rhs) const
{
return !(*this == rhs);
}
};
// No type for float singletons, partly because === isn't any equalivalence on floats
// (NaN != NaN).
using SingletonVariant = lluz::Variant<BooleanSingleton, StringSingleton>;
struct SingletonTypeVar
{
explicit SingletonTypeVar(const SingletonVariant& variant)
: variant(variant)
{
}
explicit SingletonTypeVar(SingletonVariant&& variant)
: variant(std::move(variant))
{
}
// Default operator== is C++20.
bool operator==(const SingletonTypeVar& rhs) const
{
return variant == rhs.variant;
}
bool operator!=(const SingletonTypeVar& rhs) const
{
return !(*this == rhs);
}
SingletonVariant variant;
};
template<typename T>
const T* get(const SingletonTypeVar* stv)
{
if (stv)
return get_if<T>(&stv->variant);
else
return nullptr;
}
struct GenericTypeDefinition
{
TypeId ty;
std::optional<TypeId> defaultValue;
};
struct GenericTypePackDefinition
{
TypePackId tp;
std::optional<TypePackId> defaultValue;
};
struct FunctionArgument
{
Name name;
Location location;
};
struct FunctionDefinition
{
std::optional<ModuleName> definitionModuleName;
Location definitionLocation;
std::optional<Location> varargLocation;
Location originalNameLocation;
};
// TODO: Come up with a better name.
// TODO: Do we actually need this? We'll find out later if we can delete this.
// Does not exactly belong in TypeVar.h, but this is the only way to appease the compiler.
template<typename T>
struct WithPredicate
{
T type;
PredicateVec predicates;
};
using MagicFunction = std::function<std::optional<WithPredicate<TypePackId>>(
struct TypeChecker&, const std::shared_ptr<struct Scope>&, const class AstExprCall&, WithPredicate<TypePackId>)>;
struct FunctionTypeVar
{
// Global monomorphic function
FunctionTypeVar(TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
// Global polymorphic function
FunctionTypeVar(std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retTypes,
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
// Local monomorphic function
FunctionTypeVar(TypeLevel level, TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
// Local polymorphic function
FunctionTypeVar(TypeLevel level, std::vector<TypeId> generics, std::vector<TypePackId> genericPacks, TypePackId argTypes, TypePackId retTypes,
std::optional<FunctionDefinition> defn = {}, bool hasSelf = false);
TypeLevel level;
Scope2* scope = nullptr;
/// These should all be generic
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
TypePackId argTypes;
std::vector<std::optional<FunctionArgument>> argNames;
TypePackId retTypes;
std::optional<FunctionDefinition> definition;
MagicFunction magicFunction = nullptr; // Function pointer, can be nullptr.
bool hasSelf;
Tags tags;
bool hasNoGenerics = false;
};
enum class TableState
{
// Sealed tables have an exact, known shape
Sealed,
// An unsealed table can have extra properties added to it
Unsealed,
// Tables which are not yet fully understood. We are still in the process of learning its shape.
Free,
// A table which is a generic parameter to a function. We know that certain properties are required,
// but we don't care about the full shape.
Generic,
};
struct TableIndexer
{
TableIndexer(TypeId indexType, TypeId indexResultType)
: indexType(indexType)
, indexResultType(indexResultType)
{
}
TypeId indexType;
TypeId indexResultType;
};
struct Property
{
TypeId type;
bool deprecated = false;
std::string deprecatedSuggestion;
std::optional<Location> location = std::nullopt;
Tags tags;
std::optional<std::string> documentationSymbol;
};
struct TableTypeVar
{
// We choose std::map over unordered_map here just because we have unit tests that compare
// textual outputs. I don't want to spend the effort making them resilient in the case where
// random events cause the iteration order of the map elements to change.
// If this shows up in a profile, we can revisit it.
using Props = std::map<Name, Property>;
TableTypeVar() = default;
explicit TableTypeVar(TableState state, TypeLevel level);
TableTypeVar(const Props& props, const std::optional<TableIndexer>& indexer, TypeLevel level, TableState state);
Props props;
std::optional<TableIndexer> indexer;
TableState state = TableState::Unsealed;
TypeLevel level;
Scope2* scope = nullptr;
std::optional<std::string> name;
// Sometimes we throw a type on a name to make for nicer error messages, but without creating any entry in the type namespace
// We need to know which is which when we stringify types.
std::optional<std::string> syntheticName;
std::vector<TypeId> instantiatedTypeParams;
std::vector<TypePackId> instantiatedTypePackParams;
ModuleName definitionModuleName;
std::optional<TypeId> boundTo;
Tags tags;
// Methods of this table that have an untyped self will use the same shared self type.
std::optional<TypeId> selfTy;
};
// Represents a metatable attached to a table typevar. Somewhat analogous to a bound typevar.
struct MetatableTypeVar
{
// Always points to a TableTypeVar.
TypeId table;
// Always points to either a TableTypeVar or a MetatableTypeVar.
TypeId metatable;
std::optional<std::string> syntheticName;
};
// Custom userdata of a class type
struct ClassUserData
{
virtual ~ClassUserData() {}
};
/** The type of a class.
*
* Classes behave like tables in many ways, but there are some important differences:
*
* The properties of a class are always exactly known.
* Classes optionally have a parent class.
* Two different classes that share the same properties are nevertheless distinct and mutually incompatible.
*/
struct ClassTypeVar
{
using Props = TableTypeVar::Props;
Name name;
Props props;
std::optional<TypeId> parent;
std::optional<TypeId> metatable; // metaclass?
Tags tags;
std::shared_ptr<ClassUserData> userData;
ModuleName definitionModuleName;
ClassTypeVar(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName)
: name(name)
, props(props)
, parent(parent)
, metatable(metatable)
, tags(tags)
, userData(userData)
, definitionModuleName(definitionModuleName)
{
}
};
struct TypeFun
{
// These should all be generic
std::vector<GenericTypeDefinition> typeParams;
std::vector<GenericTypePackDefinition> typePackParams;
/** The underlying type.
*
* WARNING! This is not safe to use as a type if typeParams is not empty!!
* You must first use TypeChecker::instantiateTypeFun to turn it into a real type.
*/
TypeId type;
TypeFun() = default;
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type)
: typeParams(std::move(typeParams))
, type(type)
{
}
TypeFun(std::vector<GenericTypeDefinition> typeParams, std::vector<GenericTypePackDefinition> typePackParams, TypeId type)
: typeParams(std::move(typeParams))
, typePackParams(std::move(typePackParams))
, type(type)
{
}
};
// Anything! All static checking is off.
struct AnyTypeVar
{
};
struct UnionTypeVar
{
std::vector<TypeId> options;
};
struct IntersectionTypeVar
{
std::vector<TypeId> parts;
};
struct LazyTypeVar
{
std::function<TypeId()> thunk;
};
using ErrorTypeVar = Unifiable::Error;
using TypeVariant = Unifiable::Variant<TypeId, PrimitiveTypeVar, ConstrainedTypeVar, BlockedTypeVar, SingletonTypeVar, FunctionTypeVar, TableTypeVar,
MetatableTypeVar, ClassTypeVar, AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar>;
struct TypeVar final
{
explicit TypeVar(const TypeVariant& ty)
: ty(ty)
{
}
explicit TypeVar(TypeVariant&& ty)
: ty(std::move(ty))
{
}
TypeVar(const TypeVariant& ty, bool persistent)
: ty(ty)
, persistent(persistent)
, normal(persistent) // We assume that all persistent types are irreducable.
{
}
// Re-assignes the content of the type, but doesn't change the owning arena and can't make type persistent.
void reassign(const TypeVar& rhs)
{
ty = rhs.ty;
normal = rhs.normal;
documentationSymbol = rhs.documentationSymbol;
}
TypeVariant ty;
// Kludge: A persistent TypeVar is one that belongs to the global scope.
// Global type bindings are immutable but are reused many times.
// Persistent TypeVars do not get cloned.
bool persistent = false;
// Normalization sets this for types that are fully normalized.
// This implies that they are transitively immutable.
bool normal = false;
std::optional<std::string> documentationSymbol;
// Pointer to the type arena that allocated this type.
TypeArena* owningArena = nullptr;
bool operator==(const TypeVar& rhs) const;
bool operator!=(const TypeVar& rhs) const;
TypeVar& operator=(const TypeVariant& rhs);
TypeVar& operator=(TypeVariant&& rhs);
TypeVar& operator=(const TypeVar& rhs);
};
using SeenSet = std::set<std::pair<const void*, const void*>>;
bool areEqual(SeenSet& seen, const TypeVar& lhs, const TypeVar& rhs);
// Follow BoundTypeVars until we get to something real
TypeId follow(TypeId t);
TypeId follow(TypeId t, std::function<TypeId(TypeId)> mapper);
std::vector<TypeId> flattenIntersection(TypeId ty);
bool isPrim(TypeId ty, PrimitiveTypeVar::Type primType);
bool isNil(TypeId ty);
bool isBoolean(TypeId ty);
bool isNumber(TypeId ty);
bool isString(TypeId ty);
bool isThread(TypeId ty);
bool isOptional(TypeId ty);
bool isTableIntersection(TypeId ty);
bool isOverloadedFunction(TypeId ty);
// True when string is a subtype of ty
bool maybeString(TypeId ty);
std::optional<TypeId> getMetatable(TypeId type);
TableTypeVar* getMutableTableType(TypeId type);
const TableTypeVar* getTableType(TypeId type);
// If the type has a name, return that. Else if it has a synthetic name, return that.
// Returns nullptr if the type has no name.
const std::string* getName(TypeId type);
// Returns name of the module where type was defined if type has that information
std::optional<ModuleName> getDefinitionModuleName(TypeId type);
// Checks whether a union contains all types of another union.
bool isSubset(const UnionTypeVar& super, const UnionTypeVar& sub);
// Checks if a type contains generic type binders
bool isGeneric(const TypeId ty);
// Checks if a type may be instantiated to one containing generic type binders
bool maybeGeneric(const TypeId ty);
// Checks if a type is of the form T1|...|Tn where one of the Ti is a singleton
bool maybeSingleton(TypeId ty);
// Checks if the length operator can be applied on the value of type
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 TypePackId anyTypePack;
SingletonTypes();
~SingletonTypes();
SingletonTypes(const SingletonTypes&) = delete;
void operator=(const SingletonTypes&) = delete;
TypeId errorRecoveryType(TypeId guess);
TypePackId errorRecoveryTypePack(TypePackId guess);
TypeId errorRecoveryType();
TypePackId errorRecoveryTypePack();
private:
std::unique_ptr<struct TypeArena> arena;
bool debugFreezeArena = false;
TypeId makeStringMetatable();
};
SingletonTypes& getSingletonTypes();
void persist(TypeId ty);
void persist(TypePackId tp);
const TypeLevel* getLevel(TypeId ty);
TypeLevel* getMutableLevel(TypeId ty);
std::optional<TypeLevel> getLevel(TypePackId tp);
const Property* lookupClassProp(const ClassTypeVar* cls, const Name& name);
bool isSubclass(const ClassTypeVar* cls, const ClassTypeVar* parent);
TypeVar* asMutable(TypeId ty);
template<typename T>
const T* get(TypeId tv)
{
lluz_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
lluz_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
return get_if<T>(&tv->ty);
}
template<typename T>
T* getMutable(TypeId tv)
{
lluz_ASSERT(tv);
if constexpr (!std::is_same_v<T, BoundTypeVar>)
lluz_ASSERT(get_if<BoundTypeVar>(&tv->ty) == nullptr);
return get_if<T>(&asMutable(tv)->ty);
}
/* Traverses the UnionTypeVar yielding each TypeId.
* If the iterator encounters a nested UnionTypeVar, it will instead yield each TypeId within.
*
* Beware: the iterator does not currently filter for unique TypeIds. This may change in the future.
*/
struct UnionTypeVarIterator
{
using value_type = lluz::TypeId;
using pointer = value_type*;
using reference = value_type&;
using difference_type = size_t;
using iterator_category = std::input_iterator_tag;
explicit UnionTypeVarIterator(const UnionTypeVar* utv);
UnionTypeVarIterator& operator++();
UnionTypeVarIterator operator++(int);
bool operator!=(const UnionTypeVarIterator& rhs);
bool operator==(const UnionTypeVarIterator& rhs);
const TypeId& operator*();
friend UnionTypeVarIterator end(const UnionTypeVar* utv);
private:
UnionTypeVarIterator() = default;
// (UnionTypeVar* utv, size_t currentIndex)
using SavedIterInfo = std::pair<const UnionTypeVar*, size_t>;
std::deque<SavedIterInfo> stack;
std::unordered_set<const UnionTypeVar*> seen; // Only needed to protect the iterator from hanging the thread.
void advance();
void descend();
};
UnionTypeVarIterator begin(const UnionTypeVar* utv);
UnionTypeVarIterator end(const UnionTypeVar* utv);
using TypeIdPredicate = std::function<std::optional<TypeId>(TypeId)>;
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate);
void attachTag(TypeId ty, const std::string& tagName);
void attachTag(Property& prop, const std::string& tagName);
bool hasTag(TypeId ty, const std::string& tagName);
bool hasTag(const Property& prop, const std::string& tagName);
bool hasTag(const Tags& tags, const std::string& tagName); // Do not use in new work.
} // namespace lluz

View file

@ -0,0 +1,141 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Common.h"
#include <vector>
#include <memory>
namespace lluz
{
void* pagedAllocate(size_t size);
void pagedDeallocate(void* ptr);
void pagedFreeze(void* ptr, size_t size);
void pagedUnfreeze(void* ptr, size_t size);
template<typename T>
class TypedAllocator
{
public:
TypedAllocator()
{
currentBlockSize = kBlockSize;
}
TypedAllocator(const TypedAllocator&) = delete;
TypedAllocator& operator=(const TypedAllocator&) = delete;
TypedAllocator(TypedAllocator&&) = default;
TypedAllocator& operator=(TypedAllocator&&) = default;
~TypedAllocator()
{
if (frozen)
unfreeze();
free();
}
template<typename... Args>
T* allocate(Args&&... args)
{
lluz_ASSERT(!frozen);
if (currentBlockSize >= kBlockSize)
{
lluz_ASSERT(currentBlockSize == kBlockSize);
appendBlock();
}
T* block = stuff.back();
T* res = block + currentBlockSize;
new (res) T(std::forward<Args&&...>(args...));
++currentBlockSize;
return res;
}
bool contains(const T* ptr) const
{
for (T* block : stuff)
if (ptr >= block && ptr < block + kBlockSize)
return true;
return false;
}
bool empty() const
{
return stuff.empty();
}
size_t size() const
{
return stuff.empty() ? 0 : kBlockSize * (stuff.size() - 1) + currentBlockSize;
}
void clear()
{
if (frozen)
unfreeze();
free();
currentBlockSize = kBlockSize;
}
void freeze()
{
for (T* block : stuff)
pagedFreeze(block, kBlockSizeBytes);
frozen = true;
}
void unfreeze()
{
for (T* block : stuff)
pagedUnfreeze(block, kBlockSizeBytes);
frozen = false;
}
bool isFrozen()
{
return frozen;
}
private:
void free()
{
lluz_ASSERT(!frozen);
for (T* block : stuff)
{
size_t blockSize = (block == stuff.back()) ? currentBlockSize : kBlockSize;
for (size_t i = 0; i < blockSize; ++i)
block[i].~T();
pagedDeallocate(block);
}
stuff.clear();
currentBlockSize = 0;
}
void appendBlock()
{
void* block = pagedAllocate(kBlockSizeBytes);
if (!block)
throw std::bad_alloc();
stuff.emplace_back(static_cast<T*>(block));
currentBlockSize = 0;
}
bool frozen = false;
std::vector<T*> stuff;
size_t currentBlockSize = 0;
static constexpr size_t kBlockSizeBytes = 32768;
static constexpr size_t kBlockSize = kBlockSizeBytes / sizeof(T);
};
} // namespace lluz

View file

@ -0,0 +1,147 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Variant.h"
#include <string>
namespace lluz
{
struct Scope2;
/**
* The 'level' of a TypeVar is an indirect way to talk about the scope that it 'belongs' too.
* To start, read http://okmij.org/ftp/ML/generalization.html
*
* We extend the idea by adding a "sub-level" which helps us to differentiate sibling scopes
* within a single larger scope.
*
* We need this because we try to prototype functions and add them to the type environment before
* we check the function bodies. This allows us to properly typecheck many scenarios where there
* is no single good order in which to typecheck a program.
*/
struct TypeLevel
{
int level = 0;
int subLevel = 0;
// Returns true if the level of "this" belongs to an equal or larger scope than that of rhs
bool subsumes(const TypeLevel& rhs) const
{
if (level < rhs.level)
return true;
if (level > rhs.level)
return false;
if (subLevel == rhs.subLevel)
return true; // if level == rhs.level and subLevel == rhs.subLevel, then they are the exact same TypeLevel
// Sibling TypeLevels (that is, TypeLevels that share a level but have a different subLevel) are not considered to subsume one another
return false;
}
// Returns true if the level of "this" belongs to a larger (not equal) scope than that of rhs
bool subsumesStrict(const TypeLevel& rhs) const
{
if (level == rhs.level && subLevel == rhs.subLevel)
return false;
else
return subsumes(rhs);
}
TypeLevel incr() const
{
TypeLevel result;
result.level = level + 1;
result.subLevel = 0;
return result;
}
};
inline TypeLevel max(const TypeLevel& a, const TypeLevel& b)
{
if (a.subsumes(b))
return b;
else
return a;
}
inline TypeLevel min(const TypeLevel& a, const TypeLevel& b)
{
if (a.subsumes(b))
return a;
else
return b;
}
} // namespace lluz
namespace lluz::Unifiable
{
using Name = std::string;
struct Free
{
explicit Free(TypeLevel level);
explicit Free(Scope2* scope);
int index;
TypeLevel level;
Scope2* scope = nullptr;
// True if this free type variable is part of a mutually
// recursive type alias whose definitions haven't been
// resolved yet.
bool forwardedTypeAlias = false;
private:
static int nextIndex;
};
template<typename Id>
struct Bound
{
explicit Bound(Id boundTo)
: boundTo(boundTo)
{
}
Id boundTo;
};
struct Generic
{
// By default, generics are global, with a synthetic name
Generic();
explicit Generic(TypeLevel level);
explicit Generic(const Name& name);
explicit Generic(Scope2* scope);
Generic(TypeLevel level, const Name& name);
Generic(Scope2* scope, const Name& name);
int index;
TypeLevel level;
Scope2* scope = nullptr;
Name name;
bool explicitName = false;
private:
static int nextIndex;
};
struct Error
{
// This constructor has to be public, since it's used in TypeVar and TypePack,
// but shouldn't be called directly. Please use errorRecoveryType() instead.
Error();
int index;
private:
static int nextIndex;
};
template<typename Id, typename... Value>
using Variant = lluz::Variant<Free, Bound<Id>, Generic, Error, Value...>;
} // namespace lluz::Unifiable

View file

@ -0,0 +1,136 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Error.h"
#include "lluz/Location.h"
#include "lluz/TxnLog.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypeArena.h"
#include "lluz/UnifierSharedState.h"
#include <unordered_set>
namespace lluz
{
enum Variance
{
Covariant,
Invariant
};
// A substitution which replaces singleton types by their wider types
struct Widen : Substitution
{
Widen(TypeArena* arena)
: Substitution(TxnLog::empty(), arena)
{
}
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId ty) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId ty) override;
bool ignoreChildren(TypeId ty) override;
TypeId operator()(TypeId ty);
TypePackId operator()(TypePackId ty);
};
// TODO: Use this more widely.
struct UnifierOptions
{
bool isFunctionCall = false;
};
struct Unifier
{
TypeArena* const types;
Mode mode;
TxnLog log;
ErrorVec errors;
Location location;
Variance variance = Covariant;
bool anyIsTop = false; // If true, we consider any to be a top type. If false, it is a familiar but weird mix of top and bottom all at once.
CountMismatch::Context ctx = CountMismatch::Arg;
UnifierSharedState& sharedState;
Unifier(TypeArena* types, Mode mode, 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);
ErrorVec canUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
/** Attempt to unify.
* Populate the vector errors with any type errors that may arise.
* Populate the transaction log with the set of TypeIds that need to be reset to undo the unification attempt.
*/
void tryUnify(TypeId subTy, TypeId superTy, bool isFunctionCall = false, bool isIntersection = false);
private:
void tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall = false, bool isIntersection = false);
void tryUnifyUnionWithType(TypeId subTy, const UnionTypeVar* uv, TypeId superTy);
void tryUnifyTypeWithUnion(TypeId subTy, TypeId superTy, const UnionTypeVar* uv, bool cacheEnabled, bool isFunctionCall);
void tryUnifyTypeWithIntersection(TypeId subTy, TypeId superTy, const IntersectionTypeVar* uv);
void tryUnifyIntersectionWithType(TypeId subTy, const IntersectionTypeVar* uv, TypeId superTy, bool cacheEnabled, bool isFunctionCall);
void tryUnifyPrimitives(TypeId subTy, TypeId superTy);
void tryUnifySingletons(TypeId subTy, TypeId superTy);
void tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCall = false);
void tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection = false);
void tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed);
void tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed);
TypeId widen(TypeId ty);
TypePackId widen(TypePackId tp);
TypeId deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> seen = {});
bool canCacheResult(TypeId subTy, TypeId superTy);
void cacheResult(TypeId subTy, TypeId superTy, size_t prevErrorCount);
public:
void tryUnify(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
private:
void tryUnify_(TypePackId subTy, TypePackId superTy, bool isFunctionCall = false);
void tryUnifyVariadics(TypePackId subTy, TypePackId superTy, bool reversed, int subOffset = 0);
void tryUnifyWithAny(TypeId subTy, TypeId anyTy);
void tryUnifyWithAny(TypePackId subTy, TypePackId anyTp);
std::optional<TypeId> findTablePropertyRespectingMeta(TypeId lhsType, Name name);
void tryUnifyWithConstrainedSubTypeVar(TypeId subTy, TypeId superTy);
void tryUnifyWithConstrainedSuperTypeVar(TypeId subTy, TypeId superTy);
public:
void unifyLowerBound(TypePackId subTy, TypePackId superTy, TypeLevel demotedLevel);
// Report an "infinite type error" if the type "needle" already occurs within "haystack"
void occursCheck(TypeId needle, TypeId haystack);
void occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack);
void occursCheck(TypePackId needle, TypePackId haystack);
void occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, TypePackId haystack);
Unifier makeChildUnifier();
void reportError(TypeError err);
private:
bool isNonstrictMode() const;
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, TypeId wantedType, TypeId givenType);
void checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const std::string& prop, TypeId wantedType, TypeId givenType);
[[noreturn]] void ice(const std::string& message, const Location& location);
[[noreturn]] void ice(const std::string& message);
// Available after regular type pack unification errors
std::optional<int> firstPackErrorPos;
};
void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, TypePackId tp);
} // namespace lluz

View file

@ -0,0 +1,55 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/DenseHash.h"
#include "lluz/Error.h"
#include "lluz/TypeVar.h"
#include "lluz/TypePack.h"
#include <utility>
namespace lluz
{
struct InternalErrorReporter;
struct TypeIdPairHash
{
size_t hashOne(lluz::TypeId key) const
{
return (uintptr_t(key) >> 4) ^ (uintptr_t(key) >> 9);
}
size_t operator()(const std::pair<lluz::TypeId, lluz::TypeId>& x) const
{
return hashOne(x.first) ^ (hashOne(x.second) << 1);
}
};
struct UnifierCounters
{
int recursionCount = 0;
int recursionLimit = 0;
int iterationCount = 0;
int iterationLimit = 0;
};
struct UnifierSharedState
{
UnifierSharedState(InternalErrorReporter* iceHandler)
: iceHandler(iceHandler)
{
}
InternalErrorReporter* iceHandler;
DenseHashMap<TypeId, bool> skipCacheForType{nullptr};
DenseHashSet<std::pair<TypeId, TypeId>, TypeIdPairHash> cachedUnify{{nullptr, nullptr}};
DenseHashMap<std::pair<TypeId, TypeId>, TypeErrorData, TypeIdPairHash> cachedUnifyError{{nullptr, nullptr}};
DenseHashSet<TypeId> tempSeenTy{nullptr};
DenseHashSet<TypePackId> tempSeenTp{nullptr};
UnifierCounters counters;
};
} // namespace lluz

View file

@ -0,0 +1,289 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "lluz/Common.h"
#include <new>
#include <type_traits>
#include <initializer_list>
#include <stddef.h>
#include <utility>
namespace lluz
{
template<typename... Ts>
class Variant
{
static_assert(sizeof...(Ts) > 0, XorStr("variant must have at least 1 type (empty variants are ill-formed)"));
static_assert(std::disjunction_v<std::is_void<Ts>...> == false, XorStr("variant does not allow void as an alternative type"));
static_assert(std::disjunction_v<std::is_reference<Ts>...> == false, XorStr("variant does not allow references as an alternative type"));
static_assert(std::disjunction_v<std::is_array<Ts>...> == false, XorStr("variant does not allow arrays as an alternative type"));
private:
template<typename T>
static constexpr int getTypeId()
{
using TT = std::decay_t<T>;
constexpr int N = sizeof...(Ts);
constexpr bool is[N] = {std::is_same_v<TT, Ts>...};
for (int i = 0; i < N; ++i)
if (is[i])
return i;
return -1;
}
template<typename T, typename... Tail>
struct First
{
using type = T;
};
public:
using first_alternative = typename First<Ts...>::type;
Variant()
{
static_assert(std::is_default_constructible_v<first_alternative>, XorStr("first alternative type must be default constructible"));
typeId = 0;
new (&storage) first_alternative();
}
template<typename T>
Variant(T&& value, std::enable_if_t<getTypeId<T>() >= 0>* = 0)
{
using TT = std::decay_t<T>;
constexpr int tid = getTypeId<T>();
typeId = tid;
new (&storage) TT(value);
}
Variant(const Variant& other)
{
typeId = other.typeId;
tableCopy[typeId](&storage, &other.storage);
}
Variant(Variant&& other)
{
typeId = other.typeId;
tableMove[typeId](&storage, &other.storage);
}
~Variant()
{
tableDtor[typeId](&storage);
}
Variant& operator=(const Variant& other)
{
Variant copy(other);
// static_cast<T&&> is equivalent to std::move() but faster in Debug
return *this = static_cast<Variant&&>(copy);
}
Variant& operator=(Variant&& other)
{
if (this != &other)
{
tableDtor[typeId](&storage);
typeId = other.typeId;
tableMove[typeId](&storage, &other.storage); // nothrow
}
return *this;
}
template<typename T, typename... Args>
T& emplace(Args&&... args)
{
using TT = std::decay_t<T>;
constexpr int tid = getTypeId<T>();
static_assert(tid >= 0, XorStr("unsupported T"));
tableDtor[typeId](&storage);
typeId = tid;
new (&storage) TT(std::forward<Args>(args)...);
return *reinterpret_cast<T*>(&storage);
}
template<typename T>
const T* get_if() const
{
constexpr int tid = getTypeId<T>();
static_assert(tid >= 0, XorStr("unsupported T"));
return tid == typeId ? reinterpret_cast<const T*>(&storage) : nullptr;
}
template<typename T>
T* get_if()
{
constexpr int tid = getTypeId<T>();
static_assert(tid >= 0, XorStr("unsupported T"));
return tid == typeId ? reinterpret_cast<T*>(&storage) : nullptr;
}
bool valueless_by_exception() const
{
return false;
}
int index() const
{
return typeId;
}
bool operator==(const Variant& other) const
{
static constexpr FnPred table[sizeof...(Ts)] = {&fnPredEq<Ts>...};
return typeId == other.typeId && table[typeId](&storage, &other.storage);
}
bool operator!=(const Variant& other) const
{
return !(*this == other);
}
private:
static constexpr size_t cmax(std::initializer_list<size_t> l)
{
size_t res = 0;
for (size_t i : l)
res = (res < i) ? i : res;
return res;
}
static constexpr size_t storageSize = cmax({sizeof(Ts)...});
static constexpr size_t storageAlign = cmax({alignof(Ts)...});
using FnCopy = void (*)(void*, const void*);
using FnMove = void (*)(void*, void*);
using FnDtor = void (*)(void*);
using FnPred = bool (*)(const void*, const void*);
template<typename T>
static void fnCopy(void* dst, const void* src)
{
new (dst) T(*static_cast<const T*>(src));
}
template<typename T>
static void fnMove(void* dst, void* src)
{
// static_cast<T&&> is equivalent to std::move() but faster in Debug
new (dst) T(static_cast<T&&>(*static_cast<T*>(src)));
}
template<typename T>
static void fnDtor(void* dst)
{
static_cast<T*>(dst)->~T();
}
template<typename T>
static bool fnPredEq(const void* lhs, const void* rhs)
{
return *static_cast<const T*>(lhs) == *static_cast<const T*>(rhs);
}
static constexpr FnCopy tableCopy[sizeof...(Ts)] = {&fnCopy<Ts>...};
static constexpr FnMove tableMove[sizeof...(Ts)] = {&fnMove<Ts>...};
static constexpr FnDtor tableDtor[sizeof...(Ts)] = {&fnDtor<Ts>...};
int typeId;
alignas(storageAlign) char storage[storageSize];
template<class Visitor, typename... _Ts>
friend auto visit(Visitor&& vis, const Variant<_Ts...>& var);
template<class Visitor, typename... _Ts>
friend auto visit(Visitor&& vis, Variant<_Ts...>& var);
};
template<typename T, typename... Ts>
const T* get_if(const Variant<Ts...>* var)
{
return var ? var->template get_if<T>() : nullptr;
}
template<typename T, typename... Ts>
T* get_if(Variant<Ts...>* var)
{
return var ? var->template get_if<T>() : nullptr;
}
template<typename Visitor, typename Result, typename T>
static void fnVisitR(Visitor& vis, Result& dst, std::conditional_t<std::is_const_v<T>, const void, void>* src)
{
dst = vis(*static_cast<T*>(src));
}
template<typename Visitor, typename T>
static void fnVisitV(Visitor& vis, std::conditional_t<std::is_const_v<T>, const void, void>* src)
{
vis(*static_cast<T*>(src));
}
template<class Visitor, typename... Ts>
auto visit(Visitor&& vis, const Variant<Ts...>& var)
{
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts>...>, XorStr("visitor must accept every alternative as an argument"));
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative>;
static_assert(std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts>>...>,
XorStr("visitor result type must be consistent between alternatives"));
if constexpr (std::is_same_v<Result, void>)
{
using FnVisitV = void (*)(Visitor&, const void*);
static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV<Visitor, const Ts>...};
tableVisit[var.typeId](vis, &var.storage);
}
else
{
using FnVisitR = void (*)(Visitor&, Result&, const void*);
static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR<Visitor, Result, const Ts>...};
Result res;
tableVisit[var.typeId](vis, res, &var.storage);
return res;
}
}
template<class Visitor, typename... Ts>
auto visit(Visitor&& vis, Variant<Ts...>& var)
{
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts&>...>, XorStr("visitor must accept every alternative as an argument"));
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative&>;
static_assert(std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts&>>...>,
XorStr("visitor result type must be consistent between alternatives"));
if constexpr (std::is_same_v<Result, void>)
{
using FnVisitV = void (*)(Visitor&, void*);
static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV<Visitor, Ts>...};
tableVisit[var.typeId](vis, &var.storage);
}
else
{
using FnVisitR = void (*)(Visitor&, Result&, void*);
static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR<Visitor, Result, Ts>...};
Result res;
tableVisit[var.typeId](vis, res, &var.storage);
return res;
}
}
template<class>
inline constexpr bool always_false_v = false;
} // namespace lluz

View file

@ -0,0 +1,353 @@
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <unordered_set>
#include "lluz/DenseHash.h"
#include "lluz/RecursionCounter.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
lluz_FASTINT(LluVisitRecursionLimit)
lluz_FASTFLAG(LluNormalizeFlagIsConservative)
namespace lluz
{
namespace visit_detail
{
/**
* Apply f(tid, t, seen) if doing so would pass type checking, else apply f(tid, t)
*
* We do this to permit (but not require) TypeVar visitors to accept the seen set as an argument.
*/
template<typename F, typename A, typename B, typename C>
auto apply(A tid, const B& t, C& c, F& f) -> decltype(f(tid, t, c))
{
return f(tid, t, c);
}
template<typename A, typename B, typename C, typename F>
auto apply(A tid, const B& t, C&, F& f) -> decltype(f(tid, t))
{
return f(tid, t);
}
inline bool hasSeen(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
return !seen.insert(ttv).second;
}
inline bool hasSeen(DenseHashSet<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
if (seen.contains(ttv))
return true;
seen.insert(ttv);
return false;
}
inline void unsee(std::unordered_set<void*>& seen, const void* tv)
{
void* ttv = const_cast<void*>(tv);
seen.erase(ttv);
}
inline void unsee(DenseHashSet<void*>& seen, const void* tv)
{
// When DenseHashSet is used for 'visitTypeVarOnce', where don't forget visited elements
}
} // namespace visit_detail
template<typename S>
struct GenericTypeVarVisitor
{
using Set = S;
Set seen;
int recursionCounter = 0;
GenericTypeVarVisitor() = default;
explicit GenericTypeVarVisitor(Set seen)
: seen(std::move(seen))
{
}
virtual void cycle(TypeId) {}
virtual void cycle(TypePackId) {}
virtual bool visit(TypeId ty)
{
return true;
}
virtual bool visit(TypeId ty, const BoundTypeVar& btv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const FreeTypeVar& ftv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const GenericTypeVar& gtv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ErrorTypeVar& etv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ConstrainedTypeVar& ctv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const PrimitiveTypeVar& ptv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const FunctionTypeVar& ftv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const TableTypeVar& ttv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const MetatableTypeVar& mtv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const ClassTypeVar& ctv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const AnyTypeVar& atv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const UnionTypeVar& utv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const IntersectionTypeVar& itv)
{
return visit(ty);
}
virtual bool visit(TypePackId tp)
{
return true;
}
virtual bool visit(TypePackId tp, const BoundTypePack& btp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const FreeTypePack& ftp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const GenericTypePack& gtp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const Unifiable::Error& etp)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const TypePack& pack)
{
return visit(tp);
}
virtual bool visit(TypePackId tp, const VariadicTypePack& vtp)
{
return visit(tp);
}
void traverse(TypeId ty)
{
RecursionLimiter limiter{&recursionCounter, FInt::LluVisitRecursionLimit};
if (visit_detail::hasSeen(seen, ty))
{
cycle(ty);
return;
}
if (auto btv = get<BoundTypeVar>(ty))
{
if (visit(ty, *btv))
traverse(btv->boundTo);
}
else if (auto ftv = get<FreeTypeVar>(ty))
visit(ty, *ftv);
else if (auto gtv = get<GenericTypeVar>(ty))
visit(ty, *gtv);
else if (auto etv = get<ErrorTypeVar>(ty))
visit(ty, *etv);
else if (auto ctv = get<ConstrainedTypeVar>(ty))
{
if (visit(ty, *ctv))
{
for (TypeId part : ctv->parts)
traverse(part);
}
}
else if (auto ptv = get<PrimitiveTypeVar>(ty))
visit(ty, *ptv);
else if (auto ftv = get<FunctionTypeVar>(ty))
{
if (visit(ty, *ftv))
{
traverse(ftv->argTypes);
traverse(ftv->retTypes);
}
}
else if (auto ttv = get<TableTypeVar>(ty))
{
// Some visitors want to see bound tables, that's why we traverse the original type
if (visit(ty, *ttv))
{
if (ttv->boundTo)
{
traverse(*ttv->boundTo);
}
else
{
for (auto& [_name, prop] : ttv->props)
traverse(prop.type);
if (ttv->indexer)
{
traverse(ttv->indexer->indexType);
traverse(ttv->indexer->indexResultType);
}
}
}
}
else if (auto mtv = get<MetatableTypeVar>(ty))
{
if (visit(ty, *mtv))
{
traverse(mtv->table);
traverse(mtv->metatable);
}
}
else if (auto ctv = get<ClassTypeVar>(ty))
{
if (visit(ty, *ctv))
{
for (const auto& [name, prop] : ctv->props)
traverse(prop.type);
if (ctv->parent)
traverse(*ctv->parent);
if (ctv->metatable)
traverse(*ctv->metatable);
}
}
else if (auto atv = get<AnyTypeVar>(ty))
visit(ty, *atv);
else if (auto utv = get<UnionTypeVar>(ty))
{
if (visit(ty, *utv))
{
for (TypeId optTy : utv->options)
traverse(optTy);
}
}
else if (auto itv = get<IntersectionTypeVar>(ty))
{
if (visit(ty, *itv))
{
for (TypeId partTy : itv->parts)
traverse(partTy);
}
}
visit_detail::unsee(seen, ty);
}
void traverse(TypePackId tp)
{
if (visit_detail::hasSeen(seen, tp))
{
cycle(tp);
return;
}
if (auto btv = get<BoundTypePack>(tp))
{
if (visit(tp, *btv))
traverse(btv->boundTo);
}
else if (auto ftv = get<Unifiable::Free>(tp))
visit(tp, *ftv);
else if (auto gtv = get<Unifiable::Generic>(tp))
visit(tp, *gtv);
else if (auto etv = get<Unifiable::Error>(tp))
visit(tp, *etv);
else if (auto pack = get<TypePack>(tp))
{
bool res = visit(tp, *pack);
if (!FFlag::LluNormalizeFlagIsConservative || res)
{
for (TypeId ty : pack->head)
traverse(ty);
if (pack->tail)
traverse(*pack->tail);
}
}
else if (auto pack = get<VariadicTypePack>(tp))
{
bool res = visit(tp, *pack);
if (!FFlag::LluNormalizeFlagIsConservative || res)
traverse(pack->ty);
}
else
lluz_ASSERT(!XorStr("GenericTypeVarVisitor::traverse(TypePackId) is not exhaustive!"));
visit_detail::unsee(seen, tp);
}
};
/** Visit each type under a given type. Skips over cycles and keeps recursion depth under control.
*
* The same type may be visited multiple times if there are multiple distinct paths to it. If this is undesirable, use
* TypeVarOnceVisitor.
*/
struct TypeVarVisitor : GenericTypeVarVisitor<std::unordered_set<void*>>
{
};
/// Visit each type under a given type. Each type will only be checked once even if there are multiple paths to it.
struct TypeVarOnceVisitor : GenericTypeVarVisitor<DenseHashSet<void*>>
{
TypeVarOnceVisitor()
: GenericTypeVarVisitor{DenseHashSet<void*>{nullptr}}
{
}
};
} // namespace lluz

View file

@ -1,120 +1,22 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/AstQuery.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/AstQuery.h"
#include "Luau/Module.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypeVar.h"
#include "Luau/ToString.h"
#include "lluz/Module.h"
#include "lluz/Scope.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypeVar.h"
#include "lluz/ToString.h"
#include "Luau/Common.h"
#include "lluz/Common.h"
#include <algorithm>
namespace Luau
namespace lluz
{
namespace
{
struct AutocompleteNodeFinder : public AstVisitor
{
const Position pos;
std::vector<AstNode*> ancestry;
explicit AutocompleteNodeFinder(Position pos, AstNode* root)
: pos(pos)
{
}
bool visit(AstExpr* expr) override
{
if (expr->location.begin < pos && pos <= expr->location.end)
{
ancestry.push_back(expr);
return true;
}
return false;
}
bool visit(AstStat* stat) override
{
if (stat->location.begin < pos && pos <= stat->location.end)
{
ancestry.push_back(stat);
return true;
}
return false;
}
bool visit(AstType* type) override
{
if (type->location.begin < pos && pos <= type->location.end)
{
ancestry.push_back(type);
return true;
}
return false;
}
bool visit(AstTypeError* type) override
{
// For a missing type, match the whole range including the start position
if (type->isMissing && type->location.containsClosed(pos))
{
ancestry.push_back(type);
return true;
}
return false;
}
bool visit(class AstTypePack* typePack) override
{
return true;
}
bool visit(AstStatBlock* block) override
{
// If ancestry is empty, we are inspecting the root of the AST. Its extent is considered to be infinite.
if (ancestry.empty())
{
ancestry.push_back(block);
return true;
}
// AstExprIndexName nodes are nested outside-in, so we want the outermost node in the case of nested nodes.
// ex foo.bar.baz is represented in the AST as IndexName{ IndexName {foo, bar}, baz}
if (!ancestry.empty() && ancestry.back()->is<AstExprIndexName>())
return false;
// Type annotation error might intersect the block statement when the function header is being written,
// annotation takes priority
if (!ancestry.empty() && ancestry.back()->is<AstTypeError>())
return false;
// If the cursor is at the end of an expression or type and simultaneously at the beginning of a block,
// the expression or type wins out.
// The exception to this is if we are in a block under an AstExprFunction. In this case, we consider the position to
// be within the block.
if (block->location.begin == pos && !ancestry.empty())
{
if (ancestry.back()->asExpr() && !ancestry.back()->is<AstExprFunction>())
return false;
if (ancestry.back()->asType())
return false;
}
if (block->location.begin <= pos && pos <= block->location.end)
{
ancestry.push_back(block);
return true;
}
return false;
}
};
struct FindNode : public AstVisitor
{
const Position pos;
@ -200,13 +102,6 @@ struct FindFullAncestry final : public AstVisitor
} // namespace
std::vector<AstNode*> findAncestryAtPositionForAutocomplete(const SourceModule& source, Position pos)
{
AutocompleteNodeFinder finder{pos, source.root};
source.root->visit(&finder);
return finder.ancestry;
}
std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Position pos)
{
const Position end = source.root->location.end;
@ -215,7 +110,7 @@ std::vector<AstNode*> findAstAncestryOfPosition(const SourceModule& source, Posi
FindFullAncestry finder(pos, end);
source.root->visit(&finder);
return finder.nodes;
return std::move(finder.nodes);
}
AstNode* findNodeAtPosition(const SourceModule& source, Position pos)
@ -243,7 +138,7 @@ AstExpr* findExprAtPosition(const SourceModule& source, Position pos)
ScopePtr findScopeAtPosition(const Module& module, Position pos)
{
LUAU_ASSERT(!module.scopes.empty());
lluz_ASSERT(!module.scopes.empty());
Location scopeLocation = module.scopes.front().first;
ScopePtr scope = module.scopes.front().second;
@ -307,7 +202,7 @@ std::optional<Binding> findBindingAtPosition(const Module& module, const SourceM
return std::nullopt;
ScopePtr currentScope = findScopeAtPosition(module, pos);
LUAU_ASSERT(currentScope);
lluz_ASSERT(currentScope);
while (currentScope)
{
@ -530,4 +425,4 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
return std::nullopt;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,25 +1,124 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Autocomplete.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Autocomplete.h"
#include "Luau/AstQuery.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Frontend.h"
#include "Luau/ToString.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "lluz/AstQuery.h"
#include "lluz/BuiltinDefinitions.h"
#include "lluz/Frontend.h"
#include "lluz/ToString.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypePack.h"
#include "lluz/Parser.h" // TODO: only needed for autocompleteSource which is deprecated
#include "..\..\..\..\Security\XorString.h"
#include <algorithm>
#include <unordered_set>
#include <utility>
LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3)
lluz_FASTFLAG(LluSelfCallAutocompleteFix2)
static const std::unordered_set<std::string> kStatementStartingKeywords = {
"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"};
XorStr("while"), XorStr("if"), XorStr("local"), XorStr("repeat"), XorStr("function"), XorStr("do"), XorStr("for"), XorStr("return"), XorStr("break"), XorStr("continue"), XorStr("type"), XorStr("export")};
namespace Luau
namespace lluz
{
struct NodeFinder : public AstVisitor
{
const Position pos;
std::vector<AstNode*> ancestry;
explicit NodeFinder(Position pos, AstNode* root)
: pos(pos)
{
}
bool visit(AstExpr* expr) override
{
if (expr->location.begin < pos && pos <= expr->location.end)
{
ancestry.push_back(expr);
return true;
}
return false;
}
bool visit(AstStat* stat) override
{
if (stat->location.begin < pos && pos <= stat->location.end)
{
ancestry.push_back(stat);
return true;
}
return false;
}
bool visit(AstType* type) override
{
if (type->location.begin < pos && pos <= type->location.end)
{
ancestry.push_back(type);
return true;
}
return false;
}
bool visit(AstTypeError* type) override
{
// For a missing type, match the whole range including the start position
if (type->isMissing && type->location.containsClosed(pos))
{
ancestry.push_back(type);
return true;
}
return false;
}
bool visit(class AstTypePack* typePack) override
{
return true;
}
bool visit(AstStatBlock* block) override
{
// If ancestry is empty, we are inspecting the root of the AST. Its extent is considered to be infinite.
if (ancestry.empty())
{
ancestry.push_back(block);
return true;
}
// AstExprIndexName nodes are nested outside-in, so we want the outermost node in the case of nested nodes.
// ex foo.bar.baz is represented in the AST as IndexName{ IndexName {foo, bar}, baz}
if (!ancestry.empty() && ancestry.back()->is<AstExprIndexName>())
return false;
// Type annotation error might intersect the block statement when the function header is being written,
// annotation takes priority
if (!ancestry.empty() && ancestry.back()->is<AstTypeError>())
return false;
// If the cursor is at the end of an expression or type and simultaneously at the beginning of a block,
// the expression or type wins out.
// The exception to this is if we are in a block under an AstExprFunction. In this case, we consider the position to
// be within the block.
if (block->location.begin == pos && !ancestry.empty())
{
if (ancestry.back()->asExpr() && !ancestry.back()->is<AstExprFunction>())
return false;
if (ancestry.back()->asType())
return false;
}
if (block->location.begin <= pos && pos <= block->location.end)
{
ancestry.push_back(block);
return true;
}
return false;
}
};
static bool alreadyHasParens(const std::vector<AstNode*>& nodes)
{
@ -52,7 +151,7 @@ static ParenthesesRecommendation getParenRecommendationForFunc(const FunctionTyp
auto idxExpr = nodes.back()->as<AstExprIndexName>();
bool hasImplicitSelf = idxExpr && idxExpr->op == ':';
auto [argTypes, argVariadicPack] = Luau::flatten(func->argTypes);
auto [argTypes, argVariadicPack] = lluz::flatten(func->argTypes);
if (argVariadicPack.has_value() && isVariadic(*argVariadicPack))
return ParenthesesRecommendation::CursorInside;
@ -64,9 +163,9 @@ static ParenthesesRecommendation getParenRecommendationForFunc(const FunctionTyp
static ParenthesesRecommendation getParenRecommendationForIntersect(const IntersectionTypeVar* intersect, const std::vector<AstNode*>& nodes)
{
ParenthesesRecommendation rec = ParenthesesRecommendation::None;
for (Luau::TypeId partId : intersect->parts)
for (lluz::TypeId partId : intersect->parts)
{
if (auto partFunc = Luau::get<FunctionTypeVar>(partId))
if (auto partFunc = lluz::get<FunctionTypeVar>(partId))
{
rec = std::max(rec, getParenRecommendationForFunc(partFunc, nodes));
}
@ -84,7 +183,7 @@ static ParenthesesRecommendation getParenRecommendation(TypeId id, const std::ve
if (typeCorrect == TypeCorrectKind::Correct)
return ParenthesesRecommendation::None;
id = Luau::follow(id);
id = lluz::follow(id);
if (auto func = get<FunctionTypeVar>(id))
{
return getParenRecommendationForFunc(func, nodes);
@ -149,7 +248,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
ty = follow(ty);
auto canUnify = [&typeArena](TypeId subTy, TypeId superTy) {
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
lluz_ASSERT(!FFlag::LluSelfCallAutocompleteFix2);
InternalErrorReporter iceReporter;
UnifierSharedState unifierState(&iceReporter);
@ -168,7 +267,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
TypeId expectedType = follow(*typeAtPosition);
auto checkFunctionType = [typeArena, &canUnify, &expectedType](const FunctionTypeVar* ftv) {
if (FFlag::LuauSelfCallAutocompleteFix3)
if (FFlag::LluSelfCallAutocompleteFix2)
{
if (std::optional<TypeId> firstRetTy = first(ftv->retTypes))
return checkTypeMatch(typeArena, *firstRetTy, expectedType);
@ -209,7 +308,7 @@ static TypeCorrectKind checkTypeCorrectKind(const Module& module, TypeArena* typ
}
}
if (FFlag::LuauSelfCallAutocompleteFix3)
if (FFlag::LluSelfCallAutocompleteFix2)
return checkTypeMatch(typeArena, ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
else
return canUnify(ty, expectedType) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
@ -226,7 +325,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
const std::vector<AstNode*>& nodes, AutocompleteEntryMap& result, std::unordered_set<TypeId>& seen,
std::optional<const ClassTypeVar*> containingClass = std::nullopt)
{
if (FFlag::LuauSelfCallAutocompleteFix3)
if (FFlag::LluSelfCallAutocompleteFix2)
rootTy = follow(rootTy);
ty = follow(ty);
@ -235,8 +334,8 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
return;
seen.insert(ty);
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](Luau::TypeId type) {
LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3);
auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get<ClassTypeVar>(ty)](lluz::TypeId type) {
lluz_ASSERT(!FFlag::LluSelfCallAutocompleteFix2);
if (indexType == PropIndexType::Key)
return false;
@ -252,7 +351,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
bool allHaveSelf = true;
for (auto subType : itv->parts)
{
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(Luau::follow(subType)))
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(lluz::follow(subType)))
{
allHaveSelf &= ftv->hasSelf;
}
@ -268,8 +367,8 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
return colonIndex;
}
};
auto isWrongIndexer = [typeArena, rootTy, indexType](Luau::TypeId type) {
LUAU_ASSERT(FFlag::LuauSelfCallAutocompleteFix3);
auto isWrongIndexer = [typeArena, rootTy, indexType](lluz::TypeId type) {
lluz_ASSERT(FFlag::LluSelfCallAutocompleteFix2);
if (indexType == PropIndexType::Key)
return false;
@ -277,20 +376,21 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
bool calledWithSelf = indexType == PropIndexType::Colon;
auto isCompatibleCall = [typeArena, rootTy, calledWithSelf](const FunctionTypeVar* ftv) {
// Strong match with definition is a success
if (calledWithSelf == ftv->hasSelf)
return true;
// Calls on classes require strict match between how function is declared and how it's called
if (get<ClassTypeVar>(rootTy))
return false;
// When called with ':', but declared without 'self', it is invalid if a function has incompatible first argument or no arguments at all
// When called with '.', but declared with 'self', it is considered invalid if first argument is compatible
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
{
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
return calledWithSelf;
// Calls on classes require strict match between how function is declared and how it's called
return calledWithSelf == ftv->hasSelf;
}
// If a call is made with ':', it is invalid if a function has incompatible first argument or no arguments at all
// If a call is made with '.', but it was declared with 'self', it is considered invalid if first argument is compatible
if (calledWithSelf || ftv->hasSelf)
{
if (std::optional<TypeId> firstArgTy = first(ftv->argTypes))
{
if (checkTypeMatch(typeArena, rootTy, *firstArgTy))
return calledWithSelf;
}
}
return !calledWithSelf;
@ -304,7 +404,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
{
for (auto subType : itv->parts)
{
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(Luau::follow(subType)))
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(lluz::follow(subType)))
{
if (isCompatibleCall(ftv))
return false;
@ -322,7 +422,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
// already populated, it takes precedence over the property we found just now.
if (result.count(name) == 0 && name != kParseNameError)
{
Luau::TypeId type = Luau::follow(prop.type);
lluz::TypeId type = lluz::follow(prop.type);
TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct
: checkTypeCorrectKind(module, typeArena, nodes.back(), {{}, {}}, type);
ParenthesesRecommendation parens =
@ -332,7 +432,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
AutocompleteEntryKind::Property,
type,
prop.deprecated,
FFlag::LuauSelfCallAutocompleteFix3 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
FFlag::LluSelfCallAutocompleteFix2 ? isWrongIndexer(type) : isWrongIndexer_DEPRECATED(type),
typeCorrect,
containingClass,
&prop,
@ -345,7 +445,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
};
auto fillMetatableProps = [&](const TableTypeVar* mtable) {
auto indexIt = mtable->props.find("__index");
auto indexIt = mtable->props.find(XorStr("__index"));
if (indexIt != mtable->props.end())
{
TypeId followed = follow(indexIt->second.type);
@ -375,7 +475,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
{
autocompleteProps(module, typeArena, rootTy, mt->table, indexType, nodes, result, seen);
if (FFlag::LuauSelfCallAutocompleteFix3)
if (FFlag::LluSelfCallAutocompleteFix2)
{
if (auto mtable = get<TableTypeVar>(mt->metatable))
fillMetatableProps(mtable);
@ -386,7 +486,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
if (!mtable)
return;
auto indexIt = mtable->props.find("__index");
auto indexIt = mtable->props.find(XorStr("__index"));
if (indexIt != mtable->props.end())
{
TypeId followed = follow(indexIt->second.type);
@ -441,7 +541,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
AutocompleteEntryMap inner;
std::unordered_set<TypeId> innerSeen;
if (!FFlag::LuauSelfCallAutocompleteFix3)
if (!FFlag::LluSelfCallAutocompleteFix2)
innerSeen = seen;
if (isNil(*iter))
@ -467,7 +567,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
++iter;
}
}
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LuauSelfCallAutocompleteFix3)
else if (auto pt = get<PrimitiveTypeVar>(ty); pt && FFlag::LluSelfCallAutocompleteFix2)
{
if (pt->metatable)
{
@ -475,7 +575,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
fillMetatableProps(mtable);
}
}
else if (FFlag::LuauSelfCallAutocompleteFix3 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
else if (FFlag::LluSelfCallAutocompleteFix2 && get<StringSingleton>(get<SingletonTypeVar>(ty)))
{
autocompleteProps(module, typeArena, rootTy, getSingletonTypes().stringType, indexType, nodes, result, seen);
}
@ -484,7 +584,7 @@ static void autocompleteProps(const Module& module, TypeArena* typeArena, TypeId
static void autocompleteKeywords(
const SourceModule& sourceModule, const std::vector<AstNode*>& ancestry, Position position, AutocompleteEntryMap& result)
{
LUAU_ASSERT(!ancestry.empty());
lluz_ASSERT(!ancestry.empty());
AstNode* node = ancestry.back();
@ -496,9 +596,9 @@ static void autocompleteKeywords(
// complex, however; this is good enough for now.
// These are not context-sensitive keywords, so we can unconditionally assign.
result["and"] = {AutocompleteEntryKind::Keyword};
result["or"] = {AutocompleteEntryKind::Keyword};
result["not"] = {AutocompleteEntryKind::Keyword};
result[XorStr("and")] = {AutocompleteEntryKind::Keyword};
result[XorStr("or")] = {AutocompleteEntryKind::Keyword};
result[XorStr("not")] = {AutocompleteEntryKind::Keyword};
}
}
@ -539,7 +639,7 @@ static void autocompleteStringSingleton(TypeId ty, bool addQuotes, AutocompleteE
{
auto formatKey = [addQuotes](const std::string& key) {
if (addQuotes)
return "\"" + escape(key) + "\"";
return XorStr("\")" + escape(key) + "\"";
return escape(key);
};
@ -764,7 +864,7 @@ static std::optional<bool> functionIsExpectedAt(const Module& module, AstNode* n
if (const IntersectionTypeVar* itv = get<IntersectionTypeVar>(expectedType))
{
return std::all_of(begin(itv->parts), end(itv->parts), [](auto&& ty) {
return get<FunctionTypeVar>(Luau::follow(ty)) != nullptr;
return get<FunctionTypeVar>(lluz::follow(ty)) != nullptr;
});
}
@ -807,7 +907,7 @@ AutocompleteEntryMap autocompleteTypeNames(const Module& module, Position positi
}
AstNode* parent = nullptr;
AstType* topType = nullptr; // TODO: rename?
AstType* topType = nullptr;
for (auto it = ancestry.rbegin(), e = ancestry.rend(); it != e; ++it)
{
@ -1115,15 +1215,15 @@ static AutocompleteEntryMap autocompleteStatement(
for (auto it = ancestry.rbegin(); it != ancestry.rend(); ++it)
{
if (AstStatForIn* statForIn = (*it)->as<AstStatForIn>(); statForIn && !statForIn->hasEnd)
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
if (AstStatFor* statFor = (*it)->as<AstStatFor>(); statFor && !statFor->hasEnd)
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
if (AstStatIf* statIf = (*it)->as<AstStatIf>(); statIf && !statIf->hasEnd)
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
if (AstStatWhile* statWhile = (*it)->as<AstStatWhile>(); statWhile && !statWhile->hasEnd)
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
if (AstExprFunction* exprFunction = (*it)->as<AstExprFunction>(); exprFunction && !exprFunction->hasEnd)
result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
}
if (ancestry.size() >= 2)
@ -1133,13 +1233,13 @@ static AutocompleteEntryMap autocompleteStatement(
{
if (!statIf->elsebody || (statIf->elseLocation && statIf->elseLocation->containsClosed(position)))
{
result.emplace("else", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace("elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("else"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("elseif"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
}
}
if (AstStatRepeat* statRepeat = parent->as<AstStatRepeat>(); statRepeat && !statRepeat->hasUntil)
result.emplace("until", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("until"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
}
if (ancestry.size() >= 4)
@ -1148,13 +1248,13 @@ static AutocompleteEntryMap autocompleteStatement(
if (AstStatIf* statIf = iter[3]->as<AstStatIf>();
statIf != nullptr && !statIf->elsebody && iter[2]->is<AstStatBlock>() && iter[1]->is<AstStatError>() && isIdentifier(iter[0]))
{
result.emplace("else", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace("elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("else"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("elseif"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
}
}
if (AstStatRepeat* statRepeat = extractStat<AstStatRepeat>(ancestry); statRepeat && !statRepeat->hasUntil)
result.emplace("until", AutocompleteEntry{AutocompleteEntryKind::Keyword});
result.emplace(XorStr("until"), AutocompleteEntry{AutocompleteEntryKind::Keyword});
return result;
}
@ -1181,7 +1281,7 @@ static bool autocompleteIfElseExpression(
}
else if (!ifElseExpr->hasThen)
{
outResult["then"] = {AutocompleteEntryKind::Keyword};
outResult[XorStr("then")] = {AutocompleteEntryKind::Keyword};
return true;
}
else if (ifElseExpr->trueExpr->location.containsClosed(position))
@ -1190,8 +1290,8 @@ static bool autocompleteIfElseExpression(
}
else if (!ifElseExpr->hasElse)
{
outResult["else"] = {AutocompleteEntryKind::Keyword};
outResult["elseif"] = {AutocompleteEntryKind::Keyword};
outResult[XorStr("else")] = {AutocompleteEntryKind::Keyword};
outResult[XorStr("elseif")] = {AutocompleteEntryKind::Keyword};
return true;
}
else
@ -1203,7 +1303,7 @@ static bool autocompleteIfElseExpression(
static void autocompleteExpression(const SourceModule& sourceModule, const Module& module, const TypeChecker& typeChecker, TypeArena* typeArena,
const std::vector<AstNode*>& ancestry, Position position, AutocompleteEntryMap& result)
{
LUAU_ASSERT(!ancestry.empty());
lluz_ASSERT(!ancestry.empty());
AstNode* node = ancestry.rbegin()[0];
@ -1250,12 +1350,12 @@ static void autocompleteExpression(const SourceModule& sourceModule, const Modul
TypeCorrectKind correctForFunction =
functionIsExpectedAt(module, node, position).value_or(false) ? TypeCorrectKind::Correct : TypeCorrectKind::None;
result["if"] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false};
result["true"] = {AutocompleteEntryKind::Keyword, typeChecker.booleanType, false, false, correctForTrue};
result["false"] = {AutocompleteEntryKind::Keyword, typeChecker.booleanType, false, false, correctForFalse};
result["nil"] = {AutocompleteEntryKind::Keyword, typeChecker.nilType, false, false, correctForNil};
result["not"] = {AutocompleteEntryKind::Keyword};
result["function"] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false, correctForFunction};
result[XorStr("if")] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false};
result[XorStr("true")] = {AutocompleteEntryKind::Keyword, typeChecker.booleanType, false, false, correctForTrue};
result[XorStr("false")] = {AutocompleteEntryKind::Keyword, typeChecker.booleanType, false, false, correctForFalse};
result[XorStr("nil")] = {AutocompleteEntryKind::Keyword, typeChecker.nilType, false, false, correctForNil};
result[XorStr("not")] = {AutocompleteEntryKind::Keyword};
result[XorStr("function")] = {AutocompleteEntryKind::Keyword, std::nullopt, false, false, correctForFunction};
if (auto ty = findExpectedTypeAt(module, node, position))
autocompleteStringSingleton(*ty, true, result);
@ -1292,14 +1392,14 @@ static std::optional<const ClassTypeVar*> getMethodContainingClass(const ModuleP
return std::nullopt;
}
Luau::TypeId parentType = Luau::follow(*parentIt);
lluz::TypeId parentType = lluz::follow(*parentIt);
if (auto parentClass = Luau::get<ClassTypeVar>(parentType))
if (auto parentClass = lluz::get<ClassTypeVar>(parentType))
{
return parentClass;
}
if (auto parentUnion = Luau::get<UnionTypeVar>(parentType))
if (auto parentUnion = lluz::get<UnionTypeVar>(parentType))
{
return returnFirstNonnullOptionOfType<ClassTypeVar>(parentUnion);
}
@ -1350,17 +1450,17 @@ static std::optional<AutocompleteEntryMap> autocompleteStringParams(const Source
return std::nullopt;
};
auto followedId = Luau::follow(*it);
if (auto functionType = Luau::get<FunctionTypeVar>(followedId))
auto followedId = lluz::follow(*it);
if (auto functionType = lluz::get<FunctionTypeVar>(followedId))
{
return performCallback(functionType);
}
if (auto intersect = Luau::get<IntersectionTypeVar>(followedId))
if (auto intersect = lluz::get<IntersectionTypeVar>(followedId))
{
for (TypeId part : intersect->parts)
{
if (auto candidateFunctionType = Luau::get<FunctionTypeVar>(part))
if (auto candidateFunctionType = lluz::get<FunctionTypeVar>(part))
{
if (std::optional<AutocompleteEntryMap> ret = performCallback(candidateFunctionType))
{
@ -1379,20 +1479,21 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
if (isWithinComment(sourceModule, position))
return {};
std::vector<AstNode*> ancestry = findAncestryAtPositionForAutocomplete(sourceModule, position);
LUAU_ASSERT(!ancestry.empty());
AstNode* node = ancestry.back();
NodeFinder finder{position, sourceModule.root};
sourceModule.root->visit(&finder);
lluz_ASSERT(!finder.ancestry.empty());
AstNode* node = finder.ancestry.back();
AstExprConstantNil dummy{Location{}};
AstNode* parent = ancestry.size() >= 2 ? ancestry.rbegin()[1] : &dummy;
AstNode* parent = finder.ancestry.size() >= 2 ? finder.ancestry.rbegin()[1] : &dummy;
// If we are inside a body of a function that doesn't have a completed argument list, ignore the body node
if (auto exprFunction = parent->as<AstExprFunction>(); exprFunction && !exprFunction->argLocation && node == exprFunction->body)
{
ancestry.pop_back();
finder.ancestry.pop_back();
node = ancestry.back();
parent = ancestry.size() >= 2 ? ancestry.rbegin()[1] : &dummy;
node = finder.ancestry.back();
parent = finder.ancestry.size() >= 2 ? finder.ancestry.rbegin()[1] : &dummy;
}
if (auto indexName = node->as<AstExprIndexName>())
@ -1404,48 +1505,50 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
TypeId ty = follow(*it);
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};
if (!FFlag::LluSelfCallAutocompleteFix2 && isString(ty))
return {autocompleteProps(*module, typeArena, typeChecker.globalScope->bindings[AstName{XorStr("string")}].typeId, indexType, finder.ancestry),
finder.ancestry};
else
return {autocompleteProps(*module, typeArena, ty, indexType, ancestry), ancestry};
return {autocompleteProps(*module, typeArena, ty, indexType, finder.ancestry), finder.ancestry};
}
else if (auto typeReference = node->as<AstTypeReference>())
{
if (typeReference->prefix)
return {autocompleteModuleTypes(*module, position, typeReference->prefix->value), ancestry};
return {autocompleteModuleTypes(*module, position, typeReference->prefix->value), finder.ancestry};
else
return {autocompleteTypeNames(*module, position, ancestry), ancestry};
return {autocompleteTypeNames(*module, position, finder.ancestry), finder.ancestry};
}
else if (node->is<AstTypeError>())
{
return {autocompleteTypeNames(*module, position, ancestry), ancestry};
return {autocompleteTypeNames(*module, position, finder.ancestry), finder.ancestry};
}
else if (AstStatLocal* statLocal = node->as<AstStatLocal>())
{
if (statLocal->vars.size == 1 && (!statLocal->equalsSignLocation || position < statLocal->equalsSignLocation->begin))
return {{{"function", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("function"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
else if (statLocal->equalsSignLocation && position >= statLocal->equalsSignLocation->end)
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
else
return {};
}
else if (AstStatFor* statFor = extractStat<AstStatFor>(ancestry))
else if (AstStatFor* statFor = extractStat<AstStatFor>(finder.ancestry))
{
if (!statFor->hasDo || position < statFor->doLocation.begin)
{
if (!statFor->from->is<AstExprError>() && !statFor->to->is<AstExprError>() && (!statFor->step || !statFor->step->is<AstExprError>()))
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("do"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
if (statFor->from->location.containsClosed(position) || statFor->to->location.containsClosed(position) ||
(statFor->step && statFor->step->location.containsClosed(position)))
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
return {};
}
return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry};
return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry};
}
else if (AstStatForIn* statForIn = parent->as<AstStatForIn>(); statForIn && (node->is<AstStatBlock>() || isIdentifier(node)))
@ -1461,67 +1564,76 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
return {};
}
return {{{"in", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("in"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
}
if (!statForIn->hasDo || position <= statForIn->doLocation.begin)
{
LUAU_ASSERT(statForIn->values.size > 0);
lluz_ASSERT(statForIn->values.size > 0);
AstExpr* lastExpr = statForIn->values.data[statForIn->values.size - 1];
if (lastExpr->location.containsClosed(position))
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
if (position > lastExpr->location.end)
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("do"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
return {}; // Not sure what this means
}
}
else if (AstStatForIn* statForIn = extractStat<AstStatForIn>(ancestry))
else if (AstStatForIn* statForIn = extractStat<AstStatForIn>(finder.ancestry))
{
// The AST looks a bit differently if the cursor is at a position where only the "do" keyword is allowed.
// ex "for f in f do"
if (!statForIn->hasDo)
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("do"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry};
return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry};
}
else if (AstStatWhile* statWhile = parent->as<AstStatWhile>(); node->is<AstStatBlock>() && statWhile)
{
if (!statWhile->hasDo && !statWhile->condition->is<AstStatError>() && position > statWhile->condition->location.end)
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("do"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
if (!statWhile->hasDo || position < statWhile->doLocation.begin)
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
if (statWhile->hasDo && position > statWhile->doLocation.end)
return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry};
return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry};
}
else if (AstStatWhile* statWhile = extractStat<AstStatWhile>(ancestry); statWhile && !statWhile->hasDo)
return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
else if (AstStatWhile* statWhile = extractStat<AstStatWhile>(finder.ancestry); statWhile && !statWhile->hasDo)
return {{{XorStr("do"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
else if (AstStatIf* statIf = node->as<AstStatIf>(); statIf && !statIf->elseLocation.has_value())
{
return {
{{"else", AutocompleteEntry{AutocompleteEntryKind::Keyword}}, {"elseif", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("else"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}, {XorStr("elseif"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}},
finder.ancestry};
}
else if (AstStatIf* statIf = parent->as<AstStatIf>(); statIf && node->is<AstStatBlock>())
{
if (statIf->condition->is<AstExprError>())
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
else if (!statIf->thenLocation || statIf->thenLocation->containsClosed(position))
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("then"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
}
else if (AstStatIf* statIf = extractStat<AstStatIf>(ancestry);
else if (AstStatIf* statIf = extractStat<AstStatIf>(finder.ancestry);
statIf && (!statIf->thenLocation || statIf->thenLocation->containsClosed(position)))
return {{{"then", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry};
return {{{XorStr("then"), AutocompleteEntry{
AutocompleteEntryKind::Keyword}}}, finder.ancestry};
else if (AstStatRepeat* statRepeat = node->as<AstStatRepeat>(); statRepeat && statRepeat->condition->is<AstExprError>())
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
else if (AstStatRepeat* statRepeat = extractStat<AstStatRepeat>(ancestry); statRepeat)
return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
else if (AstStatRepeat* statRepeat = extractStat<AstStatRepeat>(finder.ancestry); statRepeat)
return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry};
else if (AstExprTable* exprTable = parent->as<AstExprTable>(); exprTable && (node->is<AstExprGlobal>() || node->is<AstExprConstantString>()))
{
for (const auto& [kind, key, value] : exprTable->items)
@ -1531,7 +1643,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, *it, PropIndexType::Key, finder.ancestry);
// Remove keys that are already completed
for (const auto& item : exprTable->items)
@ -1545,9 +1657,9 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
// If we know for sure that a key is being written, do not offer general expression suggestions
if (!key)
autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position, result);
autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position, result);
return {result, ancestry};
return {result, finder.ancestry};
}
break;
@ -1555,11 +1667,11 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
}
}
else if (isIdentifier(node) && (parent->is<AstStatExpr>() || parent->is<AstStatError>()))
return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry};
return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry};
if (std::optional<AutocompleteEntryMap> ret = autocompleteStringParams(sourceModule, module, ancestry, position, callback))
if (std::optional<AutocompleteEntryMap> ret = autocompleteStringParams(sourceModule, module, finder.ancestry, position, callback))
{
return {*ret, ancestry};
return {*ret, finder.ancestry};
}
else if (node->is<AstExprConstantString>())
{
@ -1568,14 +1680,14 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
if (auto it = module->astExpectedTypes.find(node->asExpr()))
autocompleteStringSingleton(*it, false, result);
if (ancestry.size() >= 2)
if (finder.ancestry.size() >= 2)
{
if (auto idxExpr = ancestry.at(ancestry.size() - 2)->as<AstExprIndexExpr>())
if (auto idxExpr = finder.ancestry.at(finder.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, follow(*it), PropIndexType::Point, finder.ancestry, result);
}
else if (auto binExpr = ancestry.at(ancestry.size() - 2)->as<AstExprBinary>())
else if (auto binExpr = finder.ancestry.at(finder.ancestry.size() - 2)->as<AstExprBinary>())
{
if (binExpr->op == AstExprBinary::CompareEq || binExpr->op == AstExprBinary::CompareNe)
{
@ -1585,7 +1697,7 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
}
}
return {result, ancestry};
return {result, finder.ancestry};
}
if (node->is<AstExprConstantNumber>())
@ -1594,9 +1706,9 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M
}
if (node->asExpr())
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry};
return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, finder.ancestry, position), finder.ancestry};
else if (node->asStat())
return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry};
return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry};
return {};
}
@ -1626,4 +1738,32 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
return autocompleteResult;
}
} // namespace Luau
OwningAutocompleteResult autocompleteSource(Frontend& frontend, std::string_view source, Position position, StringCompletionCallback callback)
{
// TODO: Remove #include "lluz/Parser.h" with this function
auto sourceModule = std::make_unique<SourceModule>();
ParseOptions parseOptions;
parseOptions.captureComments = true;
ParseResult result = Parser::parse(source.data(), source.size(), *sourceModule->names, *sourceModule->allocator, parseOptions);
if (!result.root)
return {AutocompleteResult{}, {}, nullptr};
sourceModule->name = XorStr("FRAGMENT_SCRIPT");
sourceModule->root = result.root;
sourceModule->mode = Mode::Strict;
sourceModule->commentLocations = std::move(result.commentLocations);
TypeChecker& typeChecker = frontend.typeCheckerForAutocomplete;
ModulePtr module = typeChecker.check(*sourceModule, Mode::Strict);
OwningAutocompleteResult autocompleteResult = {
autocomplete(*sourceModule, module, typeChecker, &frontend.arenaForAutocomplete, position, callback), std::move(module),
std::move(sourceModule)};
frontend.arenaForAutocomplete.clear();
return autocompleteResult;
}
} // namespace lluz

View file

@ -1,15 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/BuiltinDefinitions.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/BuiltinDefinitions.h"
#include "Luau/Frontend.h"
#include "Luau/Symbol.h"
#include "Luau/Common.h"
#include "Luau/ToString.h"
#include "lluz/Frontend.h"
#include "lluz/Symbol.h"
#include "lluz/Common.h"
#include "lluz/ToString.h"
#include <algorithm>
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
LUAU_FASTFLAG(LuauUnknownAndNeverType)
lluz_FASTFLAGVARIABLE(LluSetMetaTableArgsCheck, false)
/** FIXME: Many of these type definitions are not quite completely accurate.
*
@ -17,7 +16,7 @@ LUAU_FASTFLAG(LuauUnknownAndNeverType)
* about a function that takes any number of values, but where each value must have some specific type.
*/
namespace Luau
namespace lluz
{
static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
@ -79,12 +78,12 @@ TypeId makeFunction(TypeArena& arena, std::optional<TypeId> selfType, std::initi
FunctionTypeVar ftv{generics, genericPacks, paramPack, retPack, {}, selfType.has_value()};
if (selfType)
ftv.argNames.push_back(Luau::FunctionArgument{"self", {}});
ftv.argNames.push_back(lluz::FunctionArgument{"self", {}});
if (paramNames.size() != 0)
{
for (auto&& p : paramNames)
ftv.argNames.push_back(Luau::FunctionArgument{std::move(p), {}});
ftv.argNames.push_back(lluz::FunctionArgument{std::move(p), {}});
}
else if (selfType)
{
@ -101,7 +100,7 @@ void attachMagicFunction(TypeId ty, MagicFunction fn)
if (auto ftv = getMutable<FunctionTypeVar>(ty))
ftv->magicFunction = fn;
else
LUAU_ASSERT(!"Got a non functional type");
lluz_ASSERT(!XorStr("Got a non functional type"));
}
Property makeProperty(TypeId ty, std::optional<std::string> documentationSymbol)
@ -140,7 +139,7 @@ void addGlobalBinding(TypeChecker& typeChecker, const ScopePtr& scope, const std
TypeId getGlobalBinding(TypeChecker& typeChecker, const std::string& name)
{
auto t = tryGetGlobalBinding(typeChecker, name);
LUAU_ASSERT(t.has_value());
lluz_ASSERT(t.has_value());
return t->typeId;
}
@ -177,34 +176,34 @@ void assignPropDocumentationSymbols(TableTypeVar::Props& props, const std::strin
void registerBuiltinTypes(TypeChecker& typeChecker)
{
LUAU_ASSERT(!typeChecker.globalTypes.typeVars.isFrozen());
LUAU_ASSERT(!typeChecker.globalTypes.typePacks.isFrozen());
lluz_ASSERT(!typeChecker.globalTypes.typeVars.isFrozen());
lluz_ASSERT(!typeChecker.globalTypes.typePacks.isFrozen());
TypeId nilType = typeChecker.nilType;
TypeArena& arena = typeChecker.globalTypes;
LoadDefinitionFileResult loadResult = Luau::loadDefinitionFile(typeChecker, typeChecker.globalScope, getBuiltinDefinitionSource(), "@luau");
LUAU_ASSERT(loadResult.success);
LoadDefinitionFileResult loadResult = lluz::loadDefinitionFile(typeChecker, typeChecker.globalScope, getBuiltinDefinitionSource(), XorStr("@lluz"));
lluz_ASSERT(loadResult.success);
TypeId genericK = arena.addType(GenericTypeVar{"K"});
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);
LUAU_ASSERT(stringMetatableTy);
lluz_ASSERT(stringMetatableTy);
const TableTypeVar* stringMetatableTable = get<TableTypeVar>(follow(*stringMetatableTy));
LUAU_ASSERT(stringMetatableTable);
lluz_ASSERT(stringMetatableTable);
auto it = stringMetatableTable->props.find("__index");
LUAU_ASSERT(it != stringMetatableTable->props.end());
auto it = stringMetatableTable->props.find(XorStr("__index"));
lluz_ASSERT(it != stringMetatableTable->props.end());
addGlobalBinding(typeChecker, "string", it->second.type, "@luau");
addGlobalBinding(typeChecker, XorStr("string"), it->second.type, XorStr("@lluz"));
// next<K, V>(t: Table<K, V>, i: K?) -> (K, V)
TypePackId nextArgsTypePack = arena.addTypePack(TypePack{{mapOfKtoV, makeOption(typeChecker, arena, genericK)}});
addGlobalBinding(typeChecker, "next",
arena.addType(FunctionTypeVar{{genericK, genericV}, {}, nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})}), "@luau");
arena.addType(FunctionTypeVar{{genericK, genericV}, {}, nextArgsTypePack, arena.addTypePack(TypePack{{genericK, genericV}})}), XorStr("@lluz"));
TypePackId pairsArgsTypePack = arena.addTypePack({mapOfKtoV});
@ -212,7 +211,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
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(typeChecker, "pairs", arena.addType(FunctionTypeVar{{genericK, genericV}, {}, pairsArgsTypePack, pairsReturnTypePack}), "@luau");
addGlobalBinding(typeChecker, XorStr("pairs"), arena.addType(FunctionTypeVar{{genericK, genericV}, {}, pairsArgsTypePack, pairsReturnTypePack}), XorStr("@lluz"));
TypeId genericMT = arena.addType(GenericTypeVar{"MT"});
@ -221,19 +220,19 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
TypeId tableMetaMT = arena.addType(MetatableTypeVar{tabTy, genericMT});
addGlobalBinding(typeChecker, "getmetatable", makeFunction(arena, std::nullopt, {genericMT}, {}, {tableMetaMT}, {genericMT}), "@luau");
addGlobalBinding(typeChecker, XorStr("getmetatable"), makeFunction(arena, std::nullopt, {genericMT}, {}, {tableMetaMT}, {genericMT}), "@lluz");
// setmetatable<MT>({ @metatable MT }, MT) -> { @metatable MT }
// clang-format off
// setmetatable<T: {}, MT>(T, MT) -> { @metatable MT, T }
addGlobalBinding(typeChecker, "setmetatable",
addGlobalBinding(typeChecker, XorStr("setmetatable"),
arena.addType(
FunctionTypeVar{
{genericMT},
{},
arena.addTypePack(TypePack{{FFlag::LuauUnknownAndNeverType ? tabTy : tableMetaMT, genericMT}}),
arena.addTypePack(TypePack{{tableMetaMT, genericMT}}),
arena.addTypePack(TypePack{{tableMetaMT}})
}
), "@luau"
), XorStr("@lluz")
);
// clang-format on
@ -248,20 +247,20 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
}
}
attachMagicFunction(getGlobalBinding(typeChecker, "assert"), magicFunctionAssert);
attachMagicFunction(getGlobalBinding(typeChecker, "setmetatable"), magicFunctionSetMetaTable);
attachMagicFunction(getGlobalBinding(typeChecker, "select"), magicFunctionSelect);
attachMagicFunction(getGlobalBinding(typeChecker, XorStr("assert")), magicFunctionAssert);
attachMagicFunction(getGlobalBinding(typeChecker, XorStr("setmetatable")), magicFunctionSetMetaTable);
attachMagicFunction(getGlobalBinding(typeChecker, XorStr("select")), magicFunctionSelect);
if (TableTypeVar* ttv = getMutable<TableTypeVar>(getGlobalBinding(typeChecker, "table")))
if (TableTypeVar* ttv = getMutable<TableTypeVar>(getGlobalBinding(typeChecker, XorStr("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");
ttv->props[XorStr("freeze")] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), XorStr("@lluz/global/table.freeze"));
ttv->props[XorStr("clone")] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), XorStr("@lluz/global/table.clone"));
attachMagicFunction(ttv->props["pack"].type, magicFunctionPack);
attachMagicFunction(ttv->props[XorStr("pack")].type, magicFunctionPack);
}
attachMagicFunction(getGlobalBinding(typeChecker, "require"), magicFunctionRequire);
attachMagicFunction(getGlobalBinding(typeChecker, XorStr("require")), magicFunctionRequire);
}
static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
@ -273,7 +272,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
if (expr.args.size <= 0)
{
typechecker.reportError(TypeError{expr.location, GenericError{"select should take 1 or more arguments"}});
typechecker.reportError(TypeError{expr.location, GenericError{XorStr("select should take 1 or more arguments")}});
return std::nullopt;
}
@ -294,7 +293,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSelect(
return WithPredicate<TypePackId>{*tail};
}
typechecker.reportError(TypeError{arg1->location, GenericError{"bad argument #1 to select (index out of range)"}});
typechecker.reportError(TypeError{arg1->location, GenericError{XorStr("bad argument #1 to select (index out of range)")}});
}
else if (AstExprConstantString* str = arg1->as<AstExprConstantString>())
{
@ -310,12 +309,6 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
{
auto [paramPack, _predicates] = withPredicate;
if (FFlag::LuauUnknownAndNeverType)
{
if (size(paramPack) < 2 && finite(paramPack))
return std::nullopt;
}
TypeArena& arena = typechecker.currentModule->internalTypes;
std::vector<TypeId> expectedArgs = typechecker.unTypePack(scope, paramPack, 2, expr.location);
@ -323,12 +316,6 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
TypeId target = follow(expectedArgs[0]);
TypeId mt = follow(expectedArgs[1]);
if (FFlag::LuauUnknownAndNeverType)
{
typechecker.tablify(target);
typechecker.tablify(mt);
}
if (const auto& tab = get<TableTypeVar>(target))
{
if (target->persistent)
@ -337,8 +324,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
}
else
{
if (!FFlag::LuauUnknownAndNeverType)
typechecker.tablify(mt);
typechecker.tablify(mt);
const TableTypeVar* mtTtv = get<TableTypeVar>(mt);
MetatableTypeVar mtv{target, mt};
@ -350,20 +336,17 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
if (tableName == metatableName)
mtv.syntheticName = tableName;
else
mtv.syntheticName = "{ @metatable: " + metatableName + ", " + tableName + " }";
mtv.syntheticName = XorStr("{ @metatable: ") + metatableName + ", " + tableName + " }";
}
TypeId mtTy = arena.addType(mtv);
if (FFlag::LuauSetMetaTableArgsCheck && expr.args.size < 1)
if (FFlag::LluSetMetaTableArgsCheck && expr.args.size < 1)
{
if (FFlag::LuauUnknownAndNeverType)
return std::nullopt;
else
return WithPredicate<TypePackId>{};
return WithPredicate<TypePackId>{};
}
if (!FFlag::LuauSetMetaTableArgsCheck || !expr.self)
if (!FFlag::LluSetMetaTableArgsCheck || !expr.self)
{
AstExpr* targetExpr = expr.args.data[0];
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
@ -381,7 +364,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
}
else
{
typechecker.reportError(TypeError{expr.location, GenericError{"setmetatable should take a table"}});
typechecker.reportError(TypeError{expr.location, GenericError{XorStr("setmetatable should take a table")}});
}
return WithPredicate<TypePackId>{arena.addTypePack({target})};
@ -407,21 +390,11 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionAssert(
if (head.size() > 0)
{
auto [ty, ok] = typechecker.pickTypesFromSense(head[0], true);
if (FFlag::LuauUnknownAndNeverType)
{
if (get<NeverTypeVar>(*ty))
head = {*ty};
else
head[0] = *ty;
}
std::optional<TypeId> newhead = typechecker.pickTypesFromSense(head[0], true);
if (!newhead)
head = {typechecker.nilType};
else
{
if (!ty)
head = {typechecker.nilType};
else
head[0] = *ty;
}
head[0] = *newhead;
}
return WithPredicate<TypePackId>{arena.addTypePack(TypePack{std::move(head), tail})};
@ -469,16 +442,16 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionPack(
static bool checkRequirePath(TypeChecker& typechecker, AstExpr* expr)
{
// require(foo.parent.bar) will technically work, but it depends on legacy goop that
// Luau does not and could not support without a bunch of work. It's deprecated anyway, so
// lluz does not and could not support without a bunch of work. It's deprecated anyway, so
// we'll warn here if we see it.
bool good = true;
AstExprIndexName* indexExpr = expr->as<AstExprIndexName>();
while (indexExpr)
{
if (indexExpr->index == "parent")
if (indexExpr->index == XorStr("parent"))
{
typechecker.reportError(indexExpr->indexLocation, DeprecatedApiUsed{"parent", "Parent"});
typechecker.reportError(indexExpr->indexLocation, DeprecatedApiUsed{XorStr("parent"), XorStr("Parent")});
good = false;
}
@ -495,7 +468,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionRequire(
if (expr.args.size != 1)
{
typechecker.reportError(TypeError{expr.location, GenericError{"require takes 1 argument"}});
typechecker.reportError(TypeError{expr.location, GenericError{XorStr("require takes 1 argument")}});
return std::nullopt;
}
@ -508,4 +481,4 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionRequire(
return std::nullopt;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,16 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Clone.h"
#include "Luau/RecursionCounter.h"
#include "Luau/TxnLog.h"
#include "Luau/TypePack.h"
#include "Luau/Unifiable.h"
#include "lluz/Clone.h"
#include "lluz/RecursionCounter.h"
#include "lluz/TxnLog.h"
#include "lluz/TypePack.h"
#include "lluz/Unifiable.h"
LUAU_FASTFLAG(DebugLuauCopyBeforeNormalizing)
lluz_FASTFLAG(DebugLluCopyBeforeNormalizing)
LUAU_FASTINTVARIABLE(LuauTypeCloneRecursionLimit, 300)
lluz_FASTINTVARIABLE(LluTypeCloneRecursionLimit, 300)
namespace Luau
namespace lluz
{
namespace
@ -59,8 +59,6 @@ struct TypeCloner
void operator()(const UnionTypeVar& t);
void operator()(const IntersectionTypeVar& t);
void operator()(const LazyTypeVar& t);
void operator()(const UnknownTypeVar& t);
void operator()(const NeverTypeVar& t);
};
struct TypePackCloner
@ -105,7 +103,7 @@ struct TypePackCloner
void operator()(const Unifiable::Bound<TypePackId>& t)
{
TypePackId cloned = clone(t.boundTo, dest, cloneState);
if (FFlag::DebugLuauCopyBeforeNormalizing)
if (FFlag::DebugLluCopyBeforeNormalizing)
cloned = dest.addTypePack(TypePackVar{BoundTypePack{cloned}});
seenTypePacks[typePackId] = cloned;
}
@ -120,7 +118,7 @@ struct TypePackCloner
{
TypePackId cloned = dest.addTypePack(TypePack{});
TypePack* destTp = getMutable<TypePack>(cloned);
LUAU_ASSERT(destTp != nullptr);
lluz_ASSERT(destTp != nullptr);
seenTypePacks[typePackId] = cloned;
for (TypeId ty : t.head)
@ -151,7 +149,7 @@ void TypeCloner::operator()(const Unifiable::Generic& t)
void TypeCloner::operator()(const Unifiable::Bound<TypeId>& t)
{
TypeId boundTo = clone(t.boundTo, dest, cloneState);
if (FFlag::DebugLuauCopyBeforeNormalizing)
if (FFlag::DebugLluCopyBeforeNormalizing)
boundTo = dest.addType(BoundTypeVar{boundTo});
seenTypes[typeId] = boundTo;
}
@ -175,7 +173,7 @@ void TypeCloner::operator()(const ConstrainedTypeVar& t)
{
TypeId res = dest.addType(ConstrainedTypeVar{t.level});
ConstrainedTypeVar* ctv = getMutable<ConstrainedTypeVar>(res);
LUAU_ASSERT(ctv);
lluz_ASSERT(ctv);
seenTypes[typeId] = res;
@ -195,7 +193,7 @@ void TypeCloner::operator()(const FunctionTypeVar& t)
{
TypeId result = dest.addType(FunctionTypeVar{TypeLevel{0, 0}, {}, {}, nullptr, nullptr, t.definition, t.hasSelf});
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(result);
LUAU_ASSERT(ftv != nullptr);
lluz_ASSERT(ftv != nullptr);
seenTypes[typeId] = result;
@ -215,7 +213,7 @@ void TypeCloner::operator()(const FunctionTypeVar& t)
void TypeCloner::operator()(const TableTypeVar& t)
{
// If table is now bound to another one, we ignore the content of the original
if (!FFlag::DebugLuauCopyBeforeNormalizing && t.boundTo)
if (!FFlag::DebugLluCopyBeforeNormalizing && t.boundTo)
{
TypeId boundTo = clone(*t.boundTo, dest, cloneState);
seenTypes[typeId] = boundTo;
@ -224,7 +222,7 @@ void TypeCloner::operator()(const TableTypeVar& t)
TypeId result = dest.addType(TableTypeVar{});
TableTypeVar* ttv = getMutable<TableTypeVar>(result);
LUAU_ASSERT(ttv != nullptr);
lluz_ASSERT(ttv != nullptr);
*ttv = t;
@ -232,7 +230,7 @@ void TypeCloner::operator()(const TableTypeVar& t)
ttv->level = TypeLevel{0, 0};
if (FFlag::DebugLuauCopyBeforeNormalizing && t.boundTo)
if (FFlag::DebugLluCopyBeforeNormalizing && t.boundTo)
ttv->boundTo = clone(*t.boundTo, dest, cloneState);
for (const auto& [name, prop] : t.props)
@ -301,7 +299,7 @@ void TypeCloner::operator()(const IntersectionTypeVar& t)
seenTypes[typeId] = result;
IntersectionTypeVar* option = getMutable<IntersectionTypeVar>(result);
LUAU_ASSERT(option != nullptr);
lluz_ASSERT(option != nullptr);
for (TypeId ty : t.parts)
option->parts.push_back(clone(ty, dest, cloneState));
@ -312,16 +310,6 @@ void TypeCloner::operator()(const LazyTypeVar& t)
defaultClone(t);
}
void TypeCloner::operator()(const UnknownTypeVar& t)
{
defaultClone(t);
}
void TypeCloner::operator()(const NeverTypeVar& t)
{
defaultClone(t);
}
} // anonymous namespace
TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
@ -329,14 +317,14 @@ TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState)
if (tp->persistent)
return tp;
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LluTypeCloneRecursionLimit);
TypePackId& res = cloneState.seenTypePacks[tp];
if (res == nullptr)
{
TypePackCloner cloner{dest, tp, cloneState};
Luau::visit(cloner, tp->ty); // Mutates the storage that 'res' points into.
lluz::visit(cloner, tp->ty); // Mutates the storage that 'res' points into.
}
return res;
@ -347,14 +335,14 @@ TypeId clone(TypeId typeId, TypeArena& dest, CloneState& cloneState)
if (typeId->persistent)
return typeId;
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LuauTypeCloneRecursionLimit);
RecursionLimiter _ra(&cloneState.recursionCount, FInt::LluTypeCloneRecursionLimit);
TypeId& res = cloneState.seenTypes[typeId];
if (res == nullptr)
{
TypeCloner cloner{dest, typeId, cloneState};
Luau::visit(cloner, typeId->ty); // Mutates the storage that 'res' points into.
lluz::visit(cloner, typeId->ty); // Mutates the storage that 'res' points into.
// Persistent types are not being cloned and we get the original type back which might be read-only
if (!res->persistent)
@ -419,7 +407,7 @@ TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
}
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
{
LUAU_ASSERT(!ttv->boundTo);
lluz_ASSERT(!ttv->boundTo);
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, ttv->state};
clone.definitionModuleName = ttv->definitionModuleName;
clone.name = ttv->name;
@ -459,4 +447,4 @@ TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
return result;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,8 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Config.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Config.h"
#include "Luau/Lexer.h"
#include "Luau/StringUtils.h"
#include "lluz/Lexer.h"
#include "lluz/StringUtils.h"
#include "..\..\..\..\Security\XorString.h"
namespace
{
@ -11,14 +13,14 @@ using Error = std::optional<std::string>;
}
namespace Luau
namespace lluz
{
static Error parseBoolean(bool& result, const std::string& value)
{
if (value == "true")
if (value == XorStr("true"))
result = true;
else if (value == "false")
else if (value == XorStr("false"))
result = false;
else
return Error{"Bad setting '" + value + "'. Valid options are true and false"};
@ -28,13 +30,13 @@ static Error parseBoolean(bool& result, const std::string& value)
Error parseModeString(Mode& mode, const std::string& modeString, bool compat)
{
if (modeString == "nocheck")
if (modeString == XorStr("nocheck"))
mode = Mode::NoCheck;
else if (modeString == "strict")
else if (modeString == XorStr("strict"))
mode = Mode::Strict;
else if (modeString == "nonstrict")
else if (modeString == XorStr("nonstrict"))
mode = Mode::Nonstrict;
else if (modeString == "noinfer" && compat)
else if (modeString == XorStr("noinfer") && compat)
mode = Mode::NoCheck;
else
return Error{"Bad mode \"" + modeString + "\". Valid options are nocheck, nonstrict, and strict"};
@ -45,27 +47,27 @@ Error parseModeString(Mode& mode, const std::string& modeString, bool compat)
static Error parseLintRuleStringForCode(
LintOptions& enabledLints, LintOptions& fatalLints, LintWarning::Code code, const std::string& value, bool compat)
{
if (value == "true")
if (value == XorStr("true"))
{
enabledLints.enableWarning(code);
}
else if (value == "false")
else if (value == XorStr("false"))
{
enabledLints.disableWarning(code);
}
else if (compat)
{
if (value == "enabled")
if (value == XorStr("enabled"))
{
enabledLints.enableWarning(code);
fatalLints.disableWarning(code);
}
else if (value == "disabled")
else if (value == XorStr("disabled"))
{
enabledLints.disableWarning(code);
fatalLints.disableWarning(code);
}
else if (value == "fatal")
else if (value == XorStr("fatal"))
{
enabledLints.enableWarning(code);
fatalLints.enableWarning(code);
@ -85,7 +87,7 @@ static Error parseLintRuleStringForCode(
Error parseLintRuleString(LintOptions& enabledLints, LintOptions& fatalLints, const std::string& warningName, const std::string& value, bool compat)
{
if (warningName == "*")
if (warningName == XorStr("*"))
{
for (int code = LintWarning::Code_Unknown; code < LintWarning::Code__Count; ++code)
{
@ -127,7 +129,7 @@ static Error fail(Lexer& lexer, const char* message)
{
Lexeme cur = lexer.current();
return format("Expected %s at line %d, got %s instead", message, cur.location.begin.line + 1, cur.toString().c_str());
return format(XorStr("Expected %s at line %d, got %s instead"), message, cur.location.begin.line + 1, cur.toString().c_str());
}
template<typename Action>
@ -142,7 +144,7 @@ static Error parseJson(const std::string& contents, Action action)
bool arrayTop = false; // we don't support nested arrays
if (lexer.current().type != '{')
return fail(lexer, "'{'");
return fail(lexer, XorStr("'{'"));
next(lexer);
for (;;)
@ -154,13 +156,13 @@ static Error parseJson(const std::string& contents, Action action)
next(lexer);
arrayTop = false;
LUAU_ASSERT(!keys.empty());
lluz_ASSERT(!keys.empty());
keys.pop_back();
if (lexer.current().type == ',')
next(lexer);
else if (lexer.current().type != '}')
return fail(lexer, "',' or '}'");
return fail(lexer, XorStr("',' or '}'"));
}
else if (lexer.current().type == Lexeme::QuotedString)
{
@ -173,10 +175,10 @@ static Error parseJson(const std::string& contents, Action action)
if (lexer.current().type == ',')
next(lexer);
else if (lexer.current().type != ']')
return fail(lexer, "',' or ']'");
return fail(lexer, XorStr("',' or ']'"));
}
else
return fail(lexer, "array element or ']'");
return fail(lexer, XorStr("array element or ']'"));
}
else
{
@ -187,7 +189,7 @@ static Error parseJson(const std::string& contents, Action action)
if (keys.empty())
{
if (lexer.current().type != Lexeme::Eof)
return fail(lexer, "end of file");
return fail(lexer, XorStr("end of file"));
return {};
}
@ -197,7 +199,7 @@ static Error parseJson(const std::string& contents, Action action)
if (lexer.current().type == ',')
next(lexer);
else if (lexer.current().type != '}')
return fail(lexer, "',' or '}'");
return fail(lexer, XorStr("',' or '}'"));
}
else if (lexer.current().type == Lexeme::QuotedString)
{
@ -207,7 +209,7 @@ static Error parseJson(const std::string& contents, Action action)
keys.push_back(key);
if (lexer.current().type != ':')
return fail(lexer, "':'");
return fail(lexer, XorStr("':'"));
next(lexer);
if (lexer.current().type == '{' || lexer.current().type == '[')
@ -220,7 +222,7 @@ static Error parseJson(const std::string& contents, Action action)
{
std::string value = lexer.current().type == Lexeme::QuotedString
? std::string(lexer.current().data, lexer.current().length)
: (lexer.current().type == Lexeme::ReservedTrue ? "true" : "false");
: (lexer.current().type == Lexeme::ReservedTrue ? XorStr("true" : "false"));
next(lexer);
if (Error err = action(keys, value))
@ -231,13 +233,13 @@ static Error parseJson(const std::string& contents, Action action)
if (lexer.current().type == ',')
next(lexer);
else if (lexer.current().type != '}')
return fail(lexer, "',' or '}'");
return fail(lexer, XorStr("',' or '}'"));
}
else
return fail(lexer, "field value");
return fail(lexer, XorStr("field value"));
}
else
return fail(lexer, "field key");
return fail(lexer, XorStr("field key"));
}
}
@ -247,25 +249,25 @@ static Error parseJson(const std::string& contents, Action action)
Error parseConfig(const std::string& contents, Config& config, bool compat)
{
return parseJson(contents, [&](const std::vector<std::string>& keys, const std::string& value) -> Error {
if (keys.size() == 1 && keys[0] == "languageMode")
if (keys.size() == 1 && keys[0] == XorStr("languageMode"))
return parseModeString(config.mode, value, compat);
else if (keys.size() == 2 && keys[0] == "lint")
else if (keys.size() == 2 && keys[0] == XorStr("lint"))
return parseLintRuleString(config.enabledLint, config.fatalLint, keys[1], value, compat);
else if (keys.size() == 1 && keys[0] == "lintErrors")
else if (keys.size() == 1 && keys[0] == XorStr("lintErrors"))
return parseBoolean(config.lintErrors, value);
else if (keys.size() == 1 && keys[0] == "typeErrors")
else if (keys.size() == 1 && keys[0] == XorStr("typeErrors"))
return parseBoolean(config.typeErrors, value);
else if (keys.size() == 1 && keys[0] == "globals")
else if (keys.size() == 1 && keys[0] == XorStr("globals"))
{
config.globals.push_back(value);
return std::nullopt;
}
else if (compat && keys.size() == 2 && keys[0] == "language" && keys[1] == "mode")
else if (compat && keys.size() == 2 && keys[0] == XorStr("language") && keys[1] == XorStr("mode"))
return parseModeString(config.mode, value, compat);
else
{
std::vector<std::string_view> keysv(keys.begin(), keys.end());
return "Unknown key " + join(keysv, "/");
return XorStr("Unknown key " + join(keysv, "/"));
}
});
}
@ -275,4 +277,4 @@ const Config& NullConfigResolver::getConfig(const ModuleName& name) const
return defaultConfig;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,8 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Constraint.h"
#include "lluz/Constraint.h"
namespace Luau
namespace lluz
{
Constraint::Constraint(ConstraintV&& c)
@ -10,4 +10,4 @@ Constraint::Constraint(ConstraintV&& c)
{
}
} // namespace Luau
} // namespace lluz

View file

@ -1,20 +1,20 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/RecursionCounter.h"
#include "Luau/ToString.h"
#include "lluz/ConstraintGraphBuilder.h"
#include "lluz/RecursionCounter.h"
#include "lluz/ToString.h"
LUAU_FASTINT(LuauCheckRecursionLimit);
lluz_FASTINT(LluCheckRecursionLimit);
#include "Luau/Scope.h"
#include "lluz/Scope.h"
namespace Luau
namespace lluz
{
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
ConstraintGraphBuilder::ConstraintGraphBuilder(
const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope> globalScope)
const ModuleName& moduleName, TypeArena* arena, NotNull<InternalErrorReporter> ice, NotNull<Scope2> globalScope)
: moduleName(moduleName)
, singletonTypes(getSingletonTypes())
, arena(arena)
@ -22,52 +22,54 @@ ConstraintGraphBuilder::ConstraintGraphBuilder(
, ice(ice)
, globalScope(globalScope)
{
LUAU_ASSERT(arena);
lluz_ASSERT(arena);
}
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
TypeId ConstraintGraphBuilder::freshType(NotNull<Scope2> scope)
{
return arena->addType(FreeTypeVar{scope.get()});
return arena->addType(FreeTypeVar{scope});
}
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
TypePackId ConstraintGraphBuilder::freshTypePack(NotNull<Scope2> scope)
{
FreeTypePack f{scope.get()};
FreeTypePack f{scope};
return arena->addTypePack(TypePackVar{std::move(f)});
}
ScopePtr ConstraintGraphBuilder::childScope(Location location, const ScopePtr& parent)
NotNull<Scope2> ConstraintGraphBuilder::childScope(Location location, NotNull<Scope2> parent)
{
auto scope = std::make_shared<Scope>(parent);
scopes.emplace_back(location, scope);
auto scope = std::make_unique<Scope2>();
NotNull<Scope2> borrow = NotNull(scope.get());
scopes.emplace_back(location, std::move(scope));
scope->returnType = parent->returnType;
parent->children.push_back(NotNull(scope.get()));
borrow->parent = parent;
borrow->returnType = parent->returnType;
parent->children.push_back(borrow);
return scope;
return borrow;
}
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, ConstraintV cv)
void ConstraintGraphBuilder::addConstraint(NotNull<Scope2> scope, ConstraintV cv)
{
scope->constraints.emplace_back(new Constraint{std::move(cv)});
}
void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
void ConstraintGraphBuilder::addConstraint(NotNull<Scope2> scope, std::unique_ptr<Constraint> c)
{
scope->constraints.emplace_back(std::move(c));
}
void ConstraintGraphBuilder::visit(AstStatBlock* block)
{
LUAU_ASSERT(scopes.empty());
LUAU_ASSERT(rootScope == nullptr);
ScopePtr scope = std::make_shared<Scope>(singletonTypes.anyTypePack);
rootScope = scope.get();
scopes.emplace_back(block->location, scope);
lluz_ASSERT(scopes.empty());
lluz_ASSERT(rootScope == nullptr);
scopes.emplace_back(block->location, std::make_unique<Scope2>());
rootScope = scopes.back().second.get();
NotNull<Scope2> borrow = NotNull(rootScope);
rootScope->returnType = freshTypePack(scope);
rootScope->returnType = freshTypePack(borrow);
prepopulateGlobalScope(scope, block);
prepopulateGlobalScope(borrow, block);
// TODO: We should share the global scope.
rootScope->typeBindings["nil"] = singletonTypes.nilType;
@ -76,14 +78,14 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
rootScope->typeBindings["boolean"] = singletonTypes.booleanType;
rootScope->typeBindings["thread"] = singletonTypes.threadType;
visitBlockWithoutChildScope(scope, block);
visitBlockWithoutChildScope(borrow, block);
}
void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
void ConstraintGraphBuilder::visitBlockWithoutChildScope(NotNull<Scope2> scope, AstStatBlock* block)
{
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
if (recursionCount >= FInt::LluCheckRecursionLimit)
{
reportCodeTooComplex(block->location);
return;
@ -93,16 +95,14 @@ void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope,
visit(scope, stat);
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStat* stat)
{
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
RecursionLimiter limiter{&recursionCount, FInt::LluCheckRecursionLimit};
if (auto s = stat->as<AstStatBlock>())
visit(scope, s);
else if (auto s = stat->as<AstStatLocal>())
visit(scope, s);
else if (auto s = stat->as<AstStatFor>())
visit(scope, s);
else if (auto f = stat->as<AstStatFunction>())
visit(scope, f);
else if (auto f = stat->as<AstStatLocalFunction>())
@ -118,27 +118,25 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
else if (auto a = stat->as<AstStatTypeAlias>())
visit(scope, a);
else
LUAU_ASSERT(0);
lluz_ASSERT(0);
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocal* local)
{
std::vector<TypeId> varTypes;
for (AstLocal* local : local->vars)
{
TypeId ty = freshType(scope);
Location location = local->location;
if (local->annotation)
{
location = local->annotation->location;
TypeId annotation = resolveType(scope, local->annotation);
addConstraint(scope, SubtypeConstraint{ty, annotation});
}
varTypes.push_back(ty);
scope->bindings[local] = Binding{ty, location};
scope->bindings[local] = ty;
}
for (size_t i = 0; i < local->values.size; ++i)
@ -169,39 +167,18 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
}
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
{
auto checkNumber = [&](AstExpr* expr)
{
if (!expr)
return;
TypeId t = check(scope, expr);
addConstraint(scope, SubtypeConstraint{t, singletonTypes.numberType});
};
checkNumber(for_->from);
checkNumber(for_->to);
checkNumber(for_->step);
ScopePtr forScope = childScope(for_->location, scope);
forScope->bindings[for_->var] = Binding{singletonTypes.numberType, for_->var->location};
visit(forScope, for_->body);
}
void addConstraints(Constraint* constraint, NotNull<Scope> scope)
void addConstraints(Constraint* constraint, NotNull<Scope2> scope)
{
scope->constraints.reserve(scope->constraints.size() + scope->constraints.size());
for (const auto& c : scope->constraints)
constraint->dependencies.push_back(NotNull{c.get()});
for (NotNull<Scope> childScope : scope->children)
for (NotNull<Scope2> childScope : scope->children)
addConstraints(constraint, childScope);
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatLocalFunction* function)
{
// Local
// Global
@ -210,24 +187,24 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction*
TypeId functionType = nullptr;
auto ty = scope->lookup(function->name);
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
lluz_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
functionType = arena->addType(BlockedTypeVar{});
scope->bindings[function->name] = Binding{functionType, function->name->location};
scope->bindings[function->name] = functionType;
FunctionSignature sig = checkFunctionSignature(scope, function->func);
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
sig.bodyScope->bindings[function->name] = sig.signature;
checkFunctionBody(sig.bodyScope, function->func);
std::unique_ptr<Constraint> c{
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
addConstraints(c.get(), sig.bodyScope);
addConstraint(scope, std::move(c));
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* function)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatFunction* function)
{
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
// With or without self
@ -247,9 +224,9 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* funct
else
{
functionType = arena->addType(BlockedTypeVar{});
scope->bindings[localName->local] = Binding{functionType, localName->location};
scope->bindings[localName->local] = functionType;
}
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
sig.bodyScope->bindings[localName->local] = sig.signature;
}
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
{
@ -262,9 +239,9 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* funct
else
{
functionType = arena->addType(BlockedTypeVar{});
rootScope->bindings[globalName->name] = Binding{functionType, globalName->location};
rootScope->bindings[globalName->name] = functionType;
}
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
sig.bodyScope->bindings[globalName->name] = sig.signature;
}
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
{
@ -286,26 +263,26 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* funct
functionType = singletonTypes.errorRecoveryType();
}
LUAU_ASSERT(functionType != nullptr);
lluz_ASSERT(functionType != nullptr);
checkFunctionBody(sig.bodyScope, function->func);
std::unique_ptr<Constraint> c{
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()}}};
addConstraints(c.get(), NotNull(sig.bodyScope.get()));
new Constraint{GeneralizationConstraint{functionType, sig.signature, sig.signatureScope ? sig.signatureScope : sig.bodyScope}}};
addConstraints(c.get(), sig.bodyScope);
addConstraint(scope, std::move(c));
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatReturn* ret)
{
TypePackId exprTypes = checkPack(scope, ret->list);
addConstraint(scope, PackSubtypeConstraint{exprTypes, scope->returnType});
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatBlock* block)
{
ScopePtr innerScope = childScope(block->location, scope);
NotNull<Scope2> innerScope = childScope(block->location, scope);
// In order to enable mutually-recursive type aliases, we need to
// populate the type bindings before we actually check any of the
@ -323,7 +300,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
visitBlockWithoutChildScope(innerScope, block);
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatAssign* assign)
{
TypePackId varPackId = checkExprList(scope, assign->vars);
TypePackId valuePack = checkPack(scope, assign->values);
@ -331,21 +308,21 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
addConstraint(scope, PackSubtypeConstraint{valuePack, varPackId});
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatIf* ifStatement)
{
check(scope, ifStatement->condition);
ScopePtr thenScope = childScope(ifStatement->thenbody->location, scope);
NotNull<Scope2> thenScope = childScope(ifStatement->thenbody->location, scope);
visit(thenScope, ifStatement->thenbody);
if (ifStatement->elsebody)
{
ScopePtr elseScope = childScope(ifStatement->elsebody->location, scope);
NotNull<Scope2> elseScope = childScope(ifStatement->elsebody->location, scope);
visit(elseScope, ifStatement->elsebody);
}
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
void ConstraintGraphBuilder::visit(NotNull<Scope2> scope, AstStatTypeAlias* alias)
{
// TODO: Exported type aliases
// TODO: Generic type aliases
@ -354,7 +331,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alia
// This should always be here since we do a separate pass over the
// AST to set up typeBindings. If it's not, we've somehow skipped
// this alias in that first pass.
LUAU_ASSERT(it != scope->typeBindings.end());
lluz_ASSERT(it != scope->typeBindings.end());
if (it == scope->typeBindings.end())
{
ice->ice("Type alias does not have a pre-populated binding", alias->location);
@ -371,7 +348,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alia
addConstraint(scope, NameConstraint{ty, alias->name.value});
}
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs)
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstArray<AstExpr*> exprs)
{
if (exprs.size == 0)
return arena->addTypePack({});
@ -387,16 +364,16 @@ TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<Ast
last = checkPack(scope, exprs.data[i]);
}
LUAU_ASSERT(last != nullptr);
lluz_ASSERT(last != nullptr);
return arena->addTypePack(TypePack{std::move(types), last});
}
TypePackId ConstraintGraphBuilder::checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs)
TypePackId ConstraintGraphBuilder::checkExprList(NotNull<Scope2> scope, const AstArray<AstExpr*>& exprs)
{
TypePackId result = arena->addTypePack({});
TypePack* resultPack = getMutable<TypePack>(result);
LUAU_ASSERT(resultPack);
lluz_ASSERT(resultPack);
for (size_t i = 0; i < exprs.size; ++i)
{
@ -413,11 +390,11 @@ TypePackId ConstraintGraphBuilder::checkExprList(const ScopePtr& scope, const As
return result;
}
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr)
TypePackId ConstraintGraphBuilder::checkPack(NotNull<Scope2> scope, AstExpr* expr)
{
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
if (recursionCount >= FInt::LluCheckRecursionLimit)
{
reportCodeTooComplex(expr->location);
return singletonTypes.errorRecoveryTypePack();
@ -463,16 +440,16 @@ TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* exp
result = arena->addTypePack({t});
}
LUAU_ASSERT(result);
lluz_ASSERT(result);
astTypePacks[expr] = result;
return result;
}
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExpr* expr)
{
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
if (recursionCount >= FInt::LluCheckRecursionLimit)
{
reportCodeTooComplex(expr->location);
return singletonTypes.errorRecoveryType();
@ -539,16 +516,16 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr)
}
else
{
LUAU_ASSERT(0);
lluz_ASSERT(0);
result = freshType(scope);
}
LUAU_ASSERT(result);
lluz_ASSERT(result);
astTypes[expr] = result;
return result;
}
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* indexName)
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexName* indexName)
{
TypeId obj = check(scope, indexName->expr);
TypeId result = freshType(scope);
@ -564,7 +541,7 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* in
return result;
}
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* indexExpr)
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprIndexExpr* indexExpr)
{
TypeId obj = check(scope, indexExpr->expr);
TypeId indexType = check(scope, indexExpr->index);
@ -579,7 +556,7 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* in
return result;
}
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprUnary* unary)
{
TypeId operandType = check(scope, unary->expr);
@ -592,14 +569,14 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
return resultType;
}
default:
LUAU_ASSERT(0);
lluz_ASSERT(0);
}
LUAU_UNREACHABLE();
lluz_UNREACHABLE();
return singletonTypes.errorRecoveryType();
}
TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary)
TypeId ConstraintGraphBuilder::check(NotNull<Scope2> scope, AstExprBinary* binary)
{
TypeId leftType = check(scope, binary->left);
TypeId rightType = check(scope, binary->right);
@ -617,18 +594,18 @@ TypeId ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binar
return resultType;
}
default:
LUAU_ASSERT(0);
lluz_ASSERT(0);
}
LUAU_ASSERT(0);
lluz_ASSERT(0);
return nullptr;
}
TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTable* expr)
TypeId ConstraintGraphBuilder::checkExprTable(NotNull<Scope2> scope, AstExprTable* expr)
{
TypeId ty = arena->addType(TableTypeVar{});
TableTypeVar* ttv = getMutable<TableTypeVar>(ty);
LUAU_ASSERT(ttv);
lluz_ASSERT(ttv);
auto createIndexer = [this, scope, ttv](TypeId currentIndexType, TypeId currentResultType) {
if (!ttv->indexer)
@ -674,10 +651,10 @@ TypeId ConstraintGraphBuilder::checkExprTable(const ScopePtr& scope, AstExprTabl
return ty;
}
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(const ScopePtr& parent, AstExprFunction* fn)
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(NotNull<Scope2> parent, AstExprFunction* fn)
{
ScopePtr signatureScope = nullptr;
ScopePtr bodyScope = nullptr;
Scope2* signatureScope = nullptr;
Scope2* bodyScope = nullptr;
TypePackId returnType = nullptr;
std::vector<TypeId> genericTypes;
@ -690,17 +667,18 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
// generics properly.
if (hasGenerics)
{
signatureScope = childScope(fn->location, parent);
NotNull signatureBorrow = childScope(fn->location, parent);
signatureScope = signatureBorrow.get();
// We need to assign returnType before creating bodyScope so that the
// return type gets propogated to bodyScope.
returnType = freshTypePack(signatureScope);
returnType = freshTypePack(signatureBorrow);
signatureScope->returnType = returnType;
bodyScope = childScope(fn->body->location, signatureScope);
bodyScope = childScope(fn->body->location, signatureBorrow).get();
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
// We do not support default values on function generics, so we only
// care about the types involved.
@ -718,10 +696,11 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
}
else
{
bodyScope = childScope(fn->body->location, parent);
NotNull bodyBorrow = childScope(fn->body->location, parent);
bodyScope = bodyBorrow.get();
returnType = freshTypePack(bodyScope);
bodyScope->returnType = returnType;
returnType = freshTypePack(bodyBorrow);
bodyBorrow->returnType = returnType;
// To eliminate the need to branch on hasGenerics below, we say that the
// signature scope is the body scope when there is no real signature
@ -729,24 +708,27 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
signatureScope = bodyScope;
}
NotNull bodyBorrow = NotNull(bodyScope);
NotNull signatureBorrow = NotNull(signatureScope);
if (fn->returnAnnotation)
{
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation);
addConstraint(signatureScope, PackSubtypeConstraint{returnType, annotatedRetType});
TypePackId annotatedRetType = resolveTypePack(signatureBorrow, *fn->returnAnnotation);
addConstraint(signatureBorrow, PackSubtypeConstraint{returnType, annotatedRetType});
}
std::vector<TypeId> argTypes;
for (AstLocal* local : fn->args)
{
TypeId t = freshType(signatureScope);
TypeId t = freshType(signatureBorrow);
argTypes.push_back(t);
signatureScope->bindings[local] = Binding{t, local->location};
signatureScope->bindings[local] = t;
if (local->annotation)
{
TypeId argAnnotation = resolveType(signatureScope, local->annotation);
addConstraint(signatureScope, SubtypeConstraint{t, argAnnotation});
TypeId argAnnotation = resolveType(signatureBorrow, local->annotation);
addConstraint(signatureBorrow, SubtypeConstraint{t, argAnnotation});
}
}
@ -759,7 +741,7 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
actualFunction.genericPacks = std::move(genericTypePacks);
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
LUAU_ASSERT(actualFunctionType);
lluz_ASSERT(actualFunctionType);
astTypes[fn] = actualFunctionType;
return {
@ -767,11 +749,11 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
// Undo the workaround we made above: if there's no signature scope,
// don't report it.
/* signatureScope */ hasGenerics ? signatureScope : nullptr,
/* bodyScope */ bodyScope,
/* bodyScope */ bodyBorrow,
};
}
void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
void ConstraintGraphBuilder::checkFunctionBody(NotNull<Scope2> scope, AstExprFunction* fn)
{
visitBlockWithoutChildScope(scope, fn->body);
@ -784,7 +766,7 @@ void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFun
}
}
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
TypeId ConstraintGraphBuilder::resolveType(NotNull<Scope2> scope, AstType* ty)
{
TypeId result = nullptr;
@ -792,8 +774,8 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
{
// TODO: Support imported types w/ require tracing.
// TODO: Support generic type references.
LUAU_ASSERT(!ref->prefix);
LUAU_ASSERT(!ref->hasParameterList);
lluz_ASSERT(!ref->prefix);
lluz_ASSERT(!ref->hasParameterList);
// TODO: If it doesn't exist, should we introduce a free binding?
// This is probably important for handling type aliases.
@ -829,7 +811,7 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
{
// TODO: Recursion limit.
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
ScopePtr signatureScope = nullptr;
Scope2* signatureScope = nullptr;
std::vector<TypeId> genericTypes;
std::vector<TypePackId> genericTypePacks;
@ -838,21 +820,22 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
// for the generic bindings to live on.
if (hasGenerics)
{
signatureScope = childScope(fn->location, scope);
NotNull<Scope2> signatureBorrow = childScope(fn->location, scope);
signatureScope = signatureBorrow.get();
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureBorrow, fn->generics);
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureBorrow, fn->genericPacks);
for (const auto& [name, g] : genericDefinitions)
{
genericTypes.push_back(g.ty);
signatureScope->typeBindings[name] = g.ty;
signatureBorrow->typeBindings[name] = g.ty;
}
for (const auto& [name, g] : genericPackDefinitions)
{
genericTypePacks.push_back(g.tp);
signatureScope->typePackBindings[name] = g.tp;
signatureBorrow->typePackBindings[name] = g.tp;
}
}
else
@ -860,11 +843,13 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
// To eliminate the need to branch on hasGenerics below, we say that
// the signature scope is the parent scope if we don't have
// generics.
signatureScope = scope;
signatureScope = scope.get();
}
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes);
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes);
NotNull<Scope2> signatureBorrow(signatureScope);
TypePackId argTypes = resolveTypePack(signatureBorrow, fn->argTypes);
TypePackId returnTypes = resolveTypePack(signatureBorrow, fn->returnTypes);
// TODO: FunctionTypeVar needs a pointer to the scope so that we know
// how to quantify/instantiate it.
@ -934,7 +919,7 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
}
else
{
LUAU_ASSERT(0);
lluz_ASSERT(0);
result = singletonTypes.errorRecoveryType();
}
@ -942,7 +927,7 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
return result;
}
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp)
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, AstTypePack* tp)
{
TypePackId result;
if (auto expl = tp->as<AstTypePackExplicit>())
@ -956,11 +941,11 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTyp
}
else if (auto gen = tp->as<AstTypePackGeneric>())
{
result = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), gen->genericName.value}});
result = arena->addTypePack(TypePackVar{GenericTypePack{scope, gen->genericName.value}});
}
else
{
LUAU_ASSERT(0);
lluz_ASSERT(0);
result = singletonTypes.errorRecoveryTypePack();
}
@ -968,7 +953,7 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTyp
return result;
}
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list)
TypePackId ConstraintGraphBuilder::resolveTypePack(NotNull<Scope2> scope, const AstTypeList& list)
{
std::vector<TypeId> head;
@ -986,12 +971,12 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const
return arena->addTypePack(TypePack{head, tail});
}
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics)
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(NotNull<Scope2> scope, AstArray<AstGenericType> generics)
{
std::vector<std::pair<Name, GenericTypeDefinition>> result;
for (const auto& generic : generics)
{
TypeId genericTy = arena->addType(GenericTypeVar{scope.get(), generic.name.value});
TypeId genericTy = arena->addType(GenericTypeVar{scope, generic.name.value});
std::optional<TypeId> defaultTy = std::nullopt;
if (generic.defaultValue)
@ -1007,12 +992,12 @@ std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::crea
}
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
const ScopePtr& scope, AstArray<AstGenericTypePack> generics)
NotNull<Scope2> scope, AstArray<AstGenericTypePack> generics)
{
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
for (const auto& generic : generics)
{
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), generic.name.value}});
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope, generic.name.value}});
std::optional<TypePackId> defaultTy = std::nullopt;
if (generic.defaultValue)
@ -1027,7 +1012,7 @@ std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::
return result;
}
TypeId ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, TypePackId tp)
TypeId ConstraintGraphBuilder::flattenPack(NotNull<Scope2> scope, Location location, TypePackId tp)
{
if (auto f = first(tp))
return *f;
@ -1053,10 +1038,10 @@ void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
struct GlobalPrepopulator : AstVisitor
{
const NotNull<Scope> globalScope;
const NotNull<Scope2> globalScope;
const NotNull<TypeArena> arena;
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
GlobalPrepopulator(NotNull<Scope2> globalScope, NotNull<TypeArena> arena)
: globalScope(globalScope)
, arena(arena)
{
@ -1065,33 +1050,33 @@ struct GlobalPrepopulator : AstVisitor
bool visit(AstStatFunction* function) override
{
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
globalScope->bindings[g->name] = Binding{arena->addType(BlockedTypeVar{})};
globalScope->bindings[g->name] = arena->addType(BlockedTypeVar{});
return true;
}
};
void ConstraintGraphBuilder::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
void ConstraintGraphBuilder::prepopulateGlobalScope(NotNull<Scope2> globalScope, AstStatBlock* program)
{
GlobalPrepopulator gp{NotNull{globalScope.get()}, arena};
GlobalPrepopulator gp{NotNull{globalScope}, arena};
program->visit(&gp);
}
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope> scope)
void collectConstraints(std::vector<NotNull<Constraint>>& result, NotNull<Scope2> scope)
{
for (const auto& c : scope->constraints)
result.push_back(NotNull{c.get()});
for (NotNull<Scope> child : scope->children)
for (NotNull<Scope2> child : scope->children)
collectConstraints(result, child);
}
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope> rootScope)
std::vector<NotNull<Constraint>> collectConstraints(NotNull<Scope2> rootScope)
{
std::vector<NotNull<Constraint>> result;
collectConstraints(result, rootScope);
return result;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,51 +1,51 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ConstraintSolver.h"
#include "Luau/Instantiation.h"
#include "Luau/Location.h"
#include "Luau/Quantify.h"
#include "Luau/ToString.h"
#include "Luau/Unifier.h"
#include "lluz/ConstraintSolver.h"
#include "lluz/Instantiation.h"
#include "lluz/Location.h"
#include "lluz/Quantify.h"
#include "lluz/ToString.h"
#include "lluz/Unifier.h"
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
lluz_FASTFLAGVARIABLE(DebugLluLogSolver, false);
lluz_FASTFLAGVARIABLE(DebugLluLogSolverToJson, false);
namespace Luau
namespace lluz
{
[[maybe_unused]] static void dumpBindings(NotNull<Scope> scope, ToStringOptions& opts)
[[maybe_unused]] static void dumpBindings(NotNull<Scope2> scope, ToStringOptions& opts)
{
for (const auto& [k, v] : scope->bindings)
{
auto d = toStringDetailed(v.typeId, opts);
auto d = toStringDetailed(v, opts);
opts.nameMap = d.nameMap;
printf("\t%s : %s\n", k.c_str(), d.name.c_str());
}
for (NotNull<Scope> child : scope->children)
for (NotNull<Scope2> child : scope->children)
dumpBindings(child, opts);
}
static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
static void dumpConstraints(NotNull<Scope2> scope, ToStringOptions& opts)
{
for (const ConstraintPtr& c : scope->constraints)
{
printf("\t%s\n", toString(*c, opts).c_str());
}
for (NotNull<Scope> child : scope->children)
for (NotNull<Scope2> child : scope->children)
dumpConstraints(child, opts);
}
void dump(NotNull<Scope> rootScope, ToStringOptions& opts)
void dump(NotNull<Scope2> rootScope, ToStringOptions& opts)
{
printf("constraints:\n");
printf(XorStr("constraints:\n"));
dumpConstraints(rootScope, opts);
}
void dump(ConstraintSolver* cs, ToStringOptions& opts)
{
printf("constraints:\n");
printf(XorStr("constraints:\n"));
for (const Constraint* c : cs->unsolvedConstraints)
{
printf("\t%s\n", toString(*c, opts).c_str());
@ -55,7 +55,7 @@ void dump(ConstraintSolver* cs, ToStringOptions& opts)
}
}
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope> rootScope)
ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull<Scope2> rootScope)
: arena(arena)
, constraints(collectConstraints(rootScope))
, rootScope(rootScope)
@ -78,13 +78,13 @@ void ConstraintSolver::run()
ToStringOptions opts;
if (FFlag::DebugLuauLogSolver)
if (FFlag::DebugLluLogSolver)
{
printf("Starting solver\n");
printf(XorStr("Starting solver\n"));
dump(this, opts);
}
if (FFlag::DebugLuauLogSolverToJson)
if (FFlag::DebugLluLogSolverToJson)
{
logger.captureBoundarySnapshot(rootScope, unsolvedConstraints);
}
@ -102,9 +102,9 @@ void ConstraintSolver::run()
continue;
}
std::string saveMe = FFlag::DebugLuauLogSolver ? toString(*c, opts) : std::string{};
std::string saveMe = FFlag::DebugLluLogSolver ? toString(*c, opts) : std::string{};
if (FFlag::DebugLuauLogSolverToJson)
if (FFlag::DebugLluLogSolverToJson)
{
logger.prepareStepSnapshot(rootScope, c, unsolvedConstraints);
}
@ -117,15 +117,15 @@ void ConstraintSolver::run()
{
unsolvedConstraints.erase(unsolvedConstraints.begin() + i);
if (FFlag::DebugLuauLogSolverToJson)
if (FFlag::DebugLluLogSolverToJson)
{
logger.commitPreparedStepSnapshot();
}
if (FFlag::DebugLuauLogSolver)
if (FFlag::DebugLluLogSolver)
{
if (force)
printf("Force ");
printf(XorStr("Force "));
printf("Dispatched\n\t%s\n", saveMe.c_str());
dump(this, opts);
}
@ -148,12 +148,12 @@ void ConstraintSolver::run()
progress |= runSolverPass(true);
} while (progress);
if (FFlag::DebugLuauLogSolver)
if (FFlag::DebugLluLogSolver)
{
dumpBindings(rootScope, opts);
}
if (FFlag::DebugLuauLogSolverToJson)
if (FFlag::DebugLluLogSolverToJson)
{
logger.captureBoundarySnapshot(rootScope, unsolvedConstraints);
printf("Logger output:\n%s\n", logger.compileOutput().c_str());
@ -187,7 +187,7 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
else if (auto nc = get<NameConstraint>(*constraint))
success = tryDispatch(*nc, constraint);
else
LUAU_ASSERT(0);
lluz_ASSERT(0);
if (success)
{
@ -248,7 +248,7 @@ bool ConstraintSolver::tryDispatch(const InstantiationConstraint& c, NotNull<con
Instantiation inst(TxnLog::empty(), arena, TypeLevel{});
std::optional<TypeId> instantiated = inst.substitute(c.superType);
LUAU_ASSERT(instantiated); // TODO FIXME HANDLE THIS
lluz_ASSERT(instantiated); // TODO FIXME HANDLE THIS
if (isBlocked(c.subType))
asMutable(c.subType)->ty.emplace<BoundTypeVar>(*instantiated);
@ -270,7 +270,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull<const Const
if (get<FreeTypeVar>(operandType))
return block(operandType, constraint);
LUAU_ASSERT(get<BlockedTypeVar>(c.resultType));
lluz_ASSERT(get<BlockedTypeVar>(c.resultType));
if (isNumber(operandType) || get<AnyTypeVar>(operandType) || get<ErrorTypeVar>(operandType))
{
@ -278,7 +278,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull<const Const
return true;
}
LUAU_ASSERT(0); // TODO metatable handling
lluz_ASSERT(0); // TODO metatable handling
return false;
}
@ -364,7 +364,7 @@ void ConstraintSolver::unblock_(BlockedConstraintId progressed)
// `blockedConstraints` desynchronized at some point. This is problematic
// because we rely on this count being correct to skip over blocked
// constraints.
LUAU_ASSERT(count > 0);
lluz_ASSERT(count > 0);
count -= 1;
}
@ -415,4 +415,4 @@ void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack)
u.log.commit();
}
} // namespace Luau
} // namespace lluz

View file

@ -1,16 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ConstraintSolverLogger.h"
#include "lluz/ConstraintSolverLogger.h"
namespace Luau
namespace lluz
{
static std::string dumpScopeAndChildren(const Scope* scope, ToStringOptions& opts)
static std::string dumpScopeAndChildren(const Scope2* scope, ToStringOptions& opts)
{
std::string output = "{\"bindings\":{";
bool comma = false;
for (const auto& [name, binding] : scope->bindings)
for (const auto& [name, type] : scope->bindings)
{
if (comma)
output += ",";
@ -19,7 +19,7 @@ static std::string dumpScopeAndChildren(const Scope* scope, ToStringOptions& opt
output += name.c_str();
output += "\": \"";
ToStringResult result = toStringDetailed(binding.typeId, opts);
ToStringResult result = toStringDetailed(type, opts);
opts.nameMap = std::move(result.nameMap);
output += result.name;
output += "\"";
@ -30,7 +30,7 @@ static std::string dumpScopeAndChildren(const Scope* scope, ToStringOptions& opt
output += "},\"children\":[";
comma = false;
for (const Scope* child : scope->children)
for (const Scope2* child : scope->children)
{
if (comma)
output += ",";
@ -96,7 +96,7 @@ std::string ConstraintSolverLogger::compileOutput()
return output;
}
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope2* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
{
std::string snapshot = "{\"type\":\"boundary\",\"rootScope\":";
@ -109,9 +109,9 @@ void ConstraintSolverLogger::captureBoundarySnapshot(const Scope* rootScope, std
}
void ConstraintSolverLogger::prepareStepSnapshot(
const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
const Scope2* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
{
// LUAU_ASSERT(!preparedSnapshot);
// lluz_ASSERT(!preparedSnapshot);
std::string snapshot = "{\"type\":\"step\",\"rootScope\":";
@ -136,4 +136,4 @@ void ConstraintSolverLogger::commitPreparedStepSnapshot()
}
}
} // namespace Luau
} // namespace lluz

View file

@ -1,12 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/BuiltinDefinitions.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/BuiltinDefinitions.h"
LUAU_FASTFLAG(LuauUnknownAndNeverType)
#include "..\..\..\..\Security\XorString.h"
namespace Luau
lluz_FASTFLAG(LluCheckLenMT)
namespace lluz
{
static const std::string kBuiltinDefinitionLuaSrc = R"BUILTIN_SRC(
static const std::string kBuiltinDefinitionLuaSrc = XorStr(R"BUILTIN_SRC(
declare bit32: {
band: (...number) -> number,
@ -116,13 +118,14 @@ declare function typeof<T>(value: T): string
-- `assert` has a magic function attached that will give more detailed type information
declare function assert<T>(value: T, errorMessage: string?): T
declare function error<T>(message: T, level: number?)
declare function tostring<T>(value: T): string
declare function tonumber<T>(value: T, radix: number?): number?
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
declare function rawset<K, V>(tab: {[K]: V}, k: K, v: V): {[K]: V}
declare function rawlen<K, V>(obj: {[K]: V} | string): number
declare function setfenv<T..., R...>(target: number | (T...) -> R..., env: {[string]: any}): ((T...) -> R...)?
@ -199,19 +202,17 @@ declare utf8: {
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
)BUILTIN_SRC";
)BUILTIN_SRC");
std::string getBuiltinDefinitionSource()
{
std::string result = kBuiltinDefinitionLuaSrc;
if (FFlag::LuauUnknownAndNeverType)
result += "declare function error<T>(message: T, level: number?): never\n";
else
result += "declare function error<T>(message: T, level: number?)\n";
// TODO: move this into kBuiltinDefinitionLuaSrc
if (FFlag::LluCheckLenMT)
result += XorStr("declare function rawlen<K, V>(obj: {[K]: V} | string): number\n");
return result;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,14 +1,14 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Error.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Error.h"
#include "Luau/Clone.h"
#include "Luau/StringUtils.h"
#include "Luau/ToString.h"
#include "lluz/Clone.h"
#include "lluz/StringUtils.h"
#include "lluz/ToString.h"
#include <stdexcept>
LUAU_FASTFLAGVARIABLE(LuauTypeMismatchModuleNameResolution, false)
LUAU_FASTFLAGVARIABLE(LuauUseInternalCompilerErrorException, false)
lluz_FASTFLAGVARIABLE(LluTypeMismatchModuleNameResolution, false)
lluz_FASTFLAGVARIABLE(LluUseInternalCompilerErrorException, false)
static std::string wrongNumberOfArgsString(size_t expectedCount, size_t actualCount, const char* argPrefix = nullptr, bool isVariadic = false)
{
@ -47,17 +47,17 @@ static std::string wrongNumberOfArgsString(size_t expectedCount, size_t actualCo
return s;
}
namespace Luau
namespace lluz
{
struct ErrorConverter
{
FileResolver* fileResolver = nullptr;
std::string operator()(const Luau::TypeMismatch& tm) const
std::string operator()(const lluz::TypeMismatch& tm) const
{
std::string givenTypeName = Luau::toString(tm.givenType);
std::string wantedTypeName = Luau::toString(tm.wantedType);
std::string givenTypeName = lluz::toString(tm.givenType);
std::string wantedTypeName = lluz::toString(tm.wantedType);
std::string result;
@ -67,7 +67,7 @@ struct ErrorConverter
{
if (auto wantedDefinitionModule = getDefinitionModuleName(tm.wantedType))
{
if (FFlag::LuauTypeMismatchModuleNameResolution && fileResolver != nullptr)
if (FFlag::LluTypeMismatchModuleNameResolution && fileResolver != nullptr)
{
std::string givenModuleName = fileResolver->getHumanReadableModuleName(*givenDefinitionModule);
std::string wantedModuleName = fileResolver->getHumanReadableModuleName(*wantedDefinitionModule);
@ -93,13 +93,13 @@ struct ErrorConverter
if (!tm.reason.empty())
result += tm.reason + " ";
if (FFlag::LuauTypeMismatchModuleNameResolution)
if (FFlag::LluTypeMismatchModuleNameResolution)
{
result += Luau::toString(*tm.error, TypeErrorToStringOptions{fileResolver});
result += lluz::toString(*tm.error, TypeErrorToStringOptions{fileResolver});
}
else
{
result += Luau::toString(*tm.error);
result += lluz::toString(*tm.error);
}
}
else if (!tm.reason.empty())
@ -110,65 +110,65 @@ struct ErrorConverter
return result;
}
std::string operator()(const Luau::UnknownSymbol& e) const
std::string operator()(const lluz::UnknownSymbol& e) const
{
switch (e.context)
{
case UnknownSymbol::Binding:
return "Unknown global '" + e.name + "'";
return XorStr("Unknown global '") + e.name + "'";
case UnknownSymbol::Type:
return "Unknown type '" + e.name + "'";
return XorStr("Unknown type '") + e.name + "'";
case UnknownSymbol::Generic:
return "Unknown generic '" + e.name + "'";
return XorStr("Unknown generic '") + e.name + "'";
}
LUAU_ASSERT(!"Unexpected context for UnknownSymbol");
return "";
lluz_ASSERT(!XorStr("Unexpected context for UnknownSymbol"));
return XorStr("");
}
std::string operator()(const Luau::UnknownProperty& e) const
std::string operator()(const lluz::UnknownProperty& e) const
{
TypeId t = follow(e.table);
if (get<TableTypeVar>(t))
return "Key '" + e.key + "' not found in table '" + Luau::toString(t) + "'";
return XorStr("Key '") + e.key + "' not found in table '" + lluz::toString(t) + "'";
else if (get<ClassTypeVar>(t))
return "Key '" + e.key + "' not found in class '" + Luau::toString(t) + "'";
return XorStr("Key '") + e.key + "' not found in class '" + lluz::toString(t) + "'";
else
return "Type '" + Luau::toString(e.table) + "' does not have key '" + e.key + "'";
return XorStr("Type '") + lluz::toString(e.table) + "' does not have key '" + e.key + "'";
}
std::string operator()(const Luau::NotATable& e) const
std::string operator()(const lluz::NotATable& e) const
{
return "Expected type table, got '" + Luau::toString(e.ty) + "' instead";
return XorStr("Expected type table, got '") + lluz::toString(e.ty) + "' instead";
}
std::string operator()(const Luau::CannotExtendTable& e) const
std::string operator()(const lluz::CannotExtendTable& e) const
{
switch (e.context)
{
case Luau::CannotExtendTable::Property:
return "Cannot add property '" + e.prop + "' to table '" + Luau::toString(e.tableType) + "'";
case Luau::CannotExtendTable::Metatable:
return "Cannot add metatable to table '" + Luau::toString(e.tableType) + "'";
case Luau::CannotExtendTable::Indexer:
return "Cannot add indexer to table '" + Luau::toString(e.tableType) + "'";
case lluz::CannotExtendTable::Property:
return XorStr("Cannot add property '") + e.prop + "' to table '" + lluz::toString(e.tableType) + "'";
case lluz::CannotExtendTable::Metatable:
return XorStr("Cannot add metatable to table '") + lluz::toString(e.tableType) + "'";
case lluz::CannotExtendTable::Indexer:
return XorStr("Cannot add indexer to table '") + lluz::toString(e.tableType) + "'";
}
LUAU_ASSERT(!"Unknown context");
return "";
lluz_ASSERT(!XorStr("Unknown context"));
return XorStr("");
}
std::string operator()(const Luau::OnlyTablesCanHaveMethods& e) const
std::string operator()(const lluz::OnlyTablesCanHaveMethods& e) const
{
return "Cannot add method to non-table type '" + Luau::toString(e.tableType) + "'";
return XorStr("Cannot add method to non-table type '") + lluz::toString(e.tableType) + "'";
}
std::string operator()(const Luau::DuplicateTypeDefinition& e) const
std::string operator()(const lluz::DuplicateTypeDefinition& e) const
{
return "Redefinition of type '" + e.name + "', previously defined at line " + std::to_string(e.previousLocation.begin.line + 1);
return XorStr("Redefinition of type '") + e.name + "', previously defined at line " + std::to_string(e.previousLocation.begin.line + 1);
}
std::string operator()(const Luau::CountMismatch& e) const
std::string operator()(const lluz::CountMismatch& e) const
{
const std::string expectedS = e.expected == 1 ? "" : "s";
const std::string actualS = e.actual == 1 ? "" : "s";
@ -177,45 +177,45 @@ struct ErrorConverter
switch (e.context)
{
case CountMismatch::Return:
return "Expected to return " + std::to_string(e.expected) + " value" + expectedS + ", but " + std::to_string(e.actual) + " " +
return XorStr("Expected to return ") + std::to_string(e.expected) + " value" + expectedS + ", but " + std::to_string(e.actual) + " " +
actualVerb + " returned here";
case CountMismatch::Result:
// It is alright if right hand side produces more values than the
// left hand side accepts. In this context consider only the opposite case.
return "Function only returns " + std::to_string(e.expected) + " value" + expectedS + ". " + std::to_string(e.actual) +
return XorStr("Function only returns ") + std::to_string(e.expected) + " value" + expectedS + ". " + std::to_string(e.actual) +
" are required here";
case CountMismatch::Arg:
return "Argument count mismatch. Function " + wrongNumberOfArgsString(e.expected, e.actual, /*argPrefix*/ nullptr, e.isVariadic);
return XorStr("Argument count mismatch. Function ") + wrongNumberOfArgsString(e.expected, e.actual, /*argPrefix*/ nullptr, e.isVariadic);
}
LUAU_ASSERT(!"Unknown context");
return "";
lluz_ASSERT(!XorStr("Unknown context"));
return XorStr("");
}
std::string operator()(const Luau::FunctionDoesNotTakeSelf&) const
std::string operator()(const lluz::FunctionDoesNotTakeSelf&) const
{
return std::string("This function does not take self. Did you mean to use a dot instead of a colon?");
return std::string(XorStr("This function does not take self. Did you mean to use a dot instead of a colon?"));
}
std::string operator()(const Luau::FunctionRequiresSelf& e) const
std::string operator()(const lluz::FunctionRequiresSelf& e) const
{
return "This function must be called with self. Did you mean to use a colon instead of a dot?";
return XorStr("This function must be called with self. Did you mean to use a colon instead of a dot?");
}
std::string operator()(const Luau::OccursCheckFailed&) const
std::string operator()(const lluz::OccursCheckFailed&) const
{
return "Type contains a self-recursive construct that cannot be resolved";
return XorStr("Type contains a self-recursive construct that cannot be resolved");
}
std::string operator()(const Luau::UnknownRequire& e) const
std::string operator()(const lluz::UnknownRequire& e) const
{
if (e.modulePath.empty())
return "Unknown require: unsupported path";
return XorStr("Unknown require: unsupported path");
else
return "Unknown require: " + e.modulePath;
return XorStr("Unknown require: ") + e.modulePath;
}
std::string operator()(const Luau::IncorrectGenericParameterCount& e) const
std::string operator()(const lluz::IncorrectGenericParameterCount& e) const
{
std::string name = e.name;
if (!e.typeFun.typeParams.empty() || !e.typeFun.typePackParams.empty())
@ -246,29 +246,29 @@ struct ErrorConverter
}
if (e.typeFun.typeParams.size() != e.actualParameters)
return "Generic type '" + name + "' " +
return XorStr("Generic type '") + name + "' " +
wrongNumberOfArgsString(e.typeFun.typeParams.size(), e.actualParameters, "type", !e.typeFun.typePackParams.empty());
return "Generic type '" + name + "' " +
return XorStr("Generic type '") + name + "' " +
wrongNumberOfArgsString(e.typeFun.typePackParams.size(), e.actualPackParameters, "type pack", /*isVariadic*/ false);
}
std::string operator()(const Luau::SyntaxError& e) const
std::string operator()(const lluz::SyntaxError& e) const
{
return e.message;
}
std::string operator()(const Luau::CodeTooComplex&) const
std::string operator()(const lluz::CodeTooComplex&) const
{
return "Code is too complex to typecheck! Consider simplifying the code around this area";
return XorStr("Code is too complex to typecheck! Consider simplifying the code around this area");
}
std::string operator()(const Luau::UnificationTooComplex&) const
std::string operator()(const lluz::UnificationTooComplex&) const
{
return "Internal error: Code is too complex to typecheck! Consider adding type annotations around this area";
return XorStr("Internal error: Code is too complex to typecheck! Consider adding type annotations around this area");
}
std::string operator()(const Luau::UnknownPropButFoundLikeProp& e) const
std::string operator()(const lluz::UnknownPropButFoundLikeProp& e) const
{
std::string candidatesSuggestion = "Did you mean ";
if (e.candidates.size() != 1)
@ -297,34 +297,34 @@ struct ErrorConverter
return s;
}
std::string operator()(const Luau::GenericError& e) const
std::string operator()(const lluz::GenericError& e) const
{
return e.message;
}
std::string operator()(const Luau::InternalError& e) const
std::string operator()(const lluz::InternalError& e) const
{
return e.message;
}
std::string operator()(const Luau::CannotCallNonFunction& e) const
std::string operator()(const lluz::CannotCallNonFunction& e) const
{
return "Cannot call non-function " + toString(e.ty);
return XorStr("Cannot call non-function ") + toString(e.ty);
}
std::string operator()(const Luau::ExtraInformation& e) const
std::string operator()(const lluz::ExtraInformation& e) const
{
return e.message;
}
std::string operator()(const Luau::DeprecatedApiUsed& e) const
std::string operator()(const lluz::DeprecatedApiUsed& e) const
{
return "The property ." + e.symbol + " is deprecated. Use ." + e.useInstead + " instead.";
return XorStr("The property .") + e.symbol + " is deprecated. Use ." + e.useInstead + " instead.";
}
std::string operator()(const Luau::ModuleHasCyclicDependency& e) const
std::string operator()(const lluz::ModuleHasCyclicDependency& e) const
{
if (e.cycle.empty())
return "Cyclic module dependency detected";
return XorStr("Cyclic module dependency detected");
std::string s = "Cyclic module dependency: ";
@ -342,17 +342,17 @@ struct ErrorConverter
return s;
}
std::string operator()(const Luau::FunctionExitsWithoutReturning& e) const
std::string operator()(const lluz::FunctionExitsWithoutReturning& e) const
{
return "Not all codepaths in this function return '" + toString(e.expectedReturnType) + "'.";
return XorStr("Not all codepaths in this function return '") + toString(e.expectedReturnType) + "'.";
}
std::string operator()(const Luau::IllegalRequire& e) const
std::string operator()(const lluz::IllegalRequire& e) const
{
return "Cannot require module " + e.moduleName + ": " + e.reason;
return XorStr("Cannot require module ") + e.moduleName + ": " + e.reason;
}
std::string operator()(const Luau::MissingProperties& e) const
std::string operator()(const lluz::MissingProperties& e) const
{
std::string s = "Table type '" + toString(e.subType) + "' not compatible with type '" + toString(e.superType) + "' because the former";
@ -385,21 +385,21 @@ struct ErrorConverter
return s;
}
std::string operator()(const Luau::DuplicateGenericParameter& e) const
std::string operator()(const lluz::DuplicateGenericParameter& e) const
{
return "Duplicate type parameter '" + e.parameterName + "'";
return XorStr("Duplicate type parameter '") + e.parameterName + "'";
}
std::string operator()(const Luau::CannotInferBinaryOperation& e) const
std::string operator()(const lluz::CannotInferBinaryOperation& e) const
{
std::string ss = "Unknown type used in " + toString(e.op);
switch (e.kind)
{
case Luau::CannotInferBinaryOperation::Comparison:
case lluz::CannotInferBinaryOperation::Comparison:
ss += " comparison";
break;
case Luau::CannotInferBinaryOperation::Operation:
case lluz::CannotInferBinaryOperation::Operation:
ss += " operation";
}
@ -409,28 +409,28 @@ struct ErrorConverter
return ss;
}
std::string operator()(const Luau::SwappedGenericTypeParameter& e) const
std::string operator()(const lluz::SwappedGenericTypeParameter& e) const
{
switch (e.kind)
{
case Luau::SwappedGenericTypeParameter::Type:
return "Variadic type parameter '" + e.name + "...' is used as a regular generic type; consider changing '" + e.name + "...' to '" +
case lluz::SwappedGenericTypeParameter::Type:
return XorStr("Variadic type parameter '") + e.name + "...' is used as a regular generic type; consider changing '" + e.name + "...' to '" +
e.name + "' in the generic argument list";
case Luau::SwappedGenericTypeParameter::Pack:
return "Generic type '" + e.name + "' is used as a variadic type parameter; consider changing '" + e.name + "' to '" + e.name +
case lluz::SwappedGenericTypeParameter::Pack:
return XorStr("Generic type '") + e.name + "' is used as a variadic type parameter; consider changing '" + e.name + "' to '" + e.name +
"...' in the generic argument list";
default:
LUAU_ASSERT(!"Unknown kind");
return "";
lluz_ASSERT(!XorStr("Unknown kind"));
return XorStr("");
}
}
std::string operator()(const Luau::OptionalValueAccess& e) const
std::string operator()(const lluz::OptionalValueAccess& e) const
{
return "Value of type '" + toString(e.optional) + "' could be nil";
return XorStr("Value of type '") + toString(e.optional) + "' could be nil";
}
std::string operator()(const Luau::MissingUnionProperty& e) const
std::string operator()(const lluz::MissingUnionProperty& e) const
{
std::string ss = "Key '" + e.key + "' is missing from ";
@ -450,12 +450,12 @@ struct ErrorConverter
std::string operator()(const TypesAreUnrelated& e) const
{
return "Cannot cast '" + toString(e.left) + "' into '" + toString(e.right) + "' because the types are unrelated";
return XorStr("Cannot cast '") + toString(e.left) + "' into '" + toString(e.right) + "' because the types are unrelated";
}
std::string operator()(const NormalizationTooComplex&) const
{
return "Code is too complex to typecheck! Consider simplifying the code around this area";
return XorStr("Code is too complex to typecheck! Consider simplifying the code around this area");
}
};
@ -463,15 +463,15 @@ struct InvalidNameChecker
{
std::string invalidName = "%error-id%";
bool operator()(const Luau::UnknownProperty& e) const
bool operator()(const lluz::UnknownProperty& e) const
{
return e.key == invalidName;
}
bool operator()(const Luau::CannotExtendTable& e) const
bool operator()(const lluz::CannotExtendTable& e) const
{
return e.prop == invalidName;
}
bool operator()(const Luau::DuplicateTypeDefinition& e) const
bool operator()(const lluz::DuplicateTypeDefinition& e) const
{
return e.name == invalidName;
}
@ -723,19 +723,19 @@ std::string toString(const TypeError& error)
std::string toString(const TypeError& error, TypeErrorToStringOptions options)
{
ErrorConverter converter{options.fileResolver};
return Luau::visit(converter, error.data);
return lluz::visit(converter, error.data);
}
bool containsParseErrorName(const TypeError& error)
{
return Luau::visit(InvalidNameChecker{}, error.data);
return lluz::visit(InvalidNameChecker{}, error.data);
}
template<typename T>
void copyError(T& e, TypeArena& destArena, CloneState cloneState)
{
auto clone = [&](auto&& ty) {
return ::Luau::clone(ty, destArena, cloneState);
return ::lluz::clone(ty, destArena, cloneState);
};
auto visitErrorData = [&](auto&& e) {
@ -867,7 +867,7 @@ void copyError(T& e, TypeArena& destArena, CloneState cloneState)
{
}
else
static_assert(always_false_v<T>, "Non-exhaustive type switch");
static_assert(always_false_v<T>, XorStr("Non-exhaustive type switch"));
}
void copyErrors(ErrorVec& errors, TypeArena& destArena)
@ -878,8 +878,8 @@ void copyErrors(ErrorVec& errors, TypeArena& destArena)
copyError(e, destArena, cloneState);
};
LUAU_ASSERT(!destArena.typeVars.isFrozen());
LUAU_ASSERT(!destArena.typePacks.isFrozen());
lluz_ASSERT(!destArena.typeVars.isFrozen());
lluz_ASSERT(!destArena.typePacks.isFrozen());
for (TypeError& error : errors)
visit(visitErrorData, error.data);
@ -887,7 +887,7 @@ void copyErrors(ErrorVec& errors, TypeArena& destArena)
void InternalErrorReporter::ice(const std::string& message, const Location& location)
{
if (FFlag::LuauUseInternalCompilerErrorException)
if (FFlag::LluUseInternalCompilerErrorException)
{
InternalCompilerError error(message, moduleName, location);
@ -909,7 +909,7 @@ void InternalErrorReporter::ice(const std::string& message, const Location& loca
void InternalErrorReporter::ice(const std::string& message)
{
if (FFlag::LuauUseInternalCompilerErrorException)
if (FFlag::LluUseInternalCompilerErrorException)
{
InternalCompilerError error(message, moduleName);
@ -934,4 +934,4 @@ const char* InternalCompilerError::what() const throw()
return this->message.data();
}
} // namespace Luau
} // namespace lluz

View file

@ -1,33 +1,33 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Frontend.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Frontend.h"
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/Config.h"
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/ConstraintSolver.h"
#include "Luau/FileResolver.h"
#include "Luau/Parser.h"
#include "Luau/Scope.h"
#include "Luau/StringUtils.h"
#include "Luau/TimeTrace.h"
#include "Luau/TypeChecker2.h"
#include "Luau/TypeInfer.h"
#include "Luau/Variant.h"
#include "lluz/Clone.h"
#include "lluz/Common.h"
#include "lluz/Config.h"
#include "lluz/ConstraintGraphBuilder.h"
#include "lluz/ConstraintSolver.h"
#include "lluz/FileResolver.h"
#include "lluz/Parser.h"
#include "lluz/Scope.h"
#include "lluz/StringUtils.h"
#include "lluz/TimeTrace.h"
#include "lluz/TypeChecker2.h"
#include "lluz/TypeInfer.h"
#include "lluz/Variant.h"
#include <algorithm>
#include <chrono>
#include <stdexcept>
LUAU_FASTINT(LuauTypeInferIterationLimit)
LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
LUAU_FASTFLAGVARIABLE(LuauAutocompleteDynamicLimits, false)
LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100)
LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false)
lluz_FASTINT(LluTypeInferIterationLimit)
lluz_FASTINT(LluTarjanChildLimit)
lluz_FASTFLAG(LluInferInNoCheckMode)
lluz_FASTFLAGVARIABLE(LluKnowsTheDataModel3, false)
lluz_FASTFLAGVARIABLE(LlluztocompleteDynamicLimits, false)
lluz_FASTINTVARIABLE(LlluztocompleteCheckTimeoutMs, 100)
lluz_FASTFLAGVARIABLE(DebugLluDeferredConstraintResolution, false)
namespace Luau
namespace lluz
{
std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments)
@ -37,13 +37,13 @@ std::optional<Mode> parseMode(const std::vector<HotComment>& hotcomments)
if (!hc.header)
continue;
if (hc.content == "nocheck")
if (hc.content == XorStr("nocheck"))
return Mode::NoCheck;
if (hc.content == "nonstrict")
if (hc.content == XorStr("nonstrict"))
return Mode::Nonstrict;
if (hc.content == "strict")
if (hc.content == XorStr("strict"))
return Mode::Strict;
}
@ -79,20 +79,20 @@ static void generateDocumentationSymbols(TypeId ty, const std::string& rootName)
LoadDefinitionFileResult loadDefinitionFile(TypeChecker& typeChecker, ScopePtr targetScope, std::string_view source, const std::string& packageName)
{
LUAU_TIMETRACE_SCOPE("loadDefinitionFile", "Frontend");
lluz_TIMETRACE_SCOPE(XorStr("loadDefinitionFile", "Frontend"));
Luau::Allocator allocator;
Luau::AstNameTable names(allocator);
lluz::Allocator allocator;
lluz::AstNameTable names(allocator);
ParseOptions options;
options.allowDeclarationSyntax = true;
Luau::ParseResult parseResult = Luau::Parser::parse(source.data(), source.size(), names, allocator, options);
lluz::ParseResult parseResult = lluz::Parser::parse(source.data(), source.size(), names, allocator, options);
if (parseResult.errors.size() > 0)
return LoadDefinitionFileResult{false, parseResult, nullptr};
Luau::SourceModule module;
lluz::SourceModule module;
module.root = parseResult.root;
module.mode = Mode::Definition;
@ -169,7 +169,7 @@ std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleN
auto it = segments.begin();
if (*it == "script" && !currentModuleName.empty())
if (*it == XorStr("script") && !currentModuleName.empty())
{
result = split(currentModuleName, '/');
++it;
@ -177,13 +177,13 @@ std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleN
for (; it != segments.end(); ++it)
{
if (result.size() > 1 && *it == "Parent")
if (result.size() > 1 && *it == XorStr("Parent"))
result.pop_back();
else
result.push_back(*it);
}
return join(result, "/");
return join(result, XorStr("/"));
}
std::optional<std::string> pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& pathExpr)
@ -271,7 +271,7 @@ std::vector<RequireCycle> getRequireCycles(
if (top == nullptr)
{
// special marker for post-order processing
LUAU_ASSERT(!path.empty());
lluz_ASSERT(!path.empty());
top = path.back();
path.pop_back();
@ -353,8 +353,8 @@ FrontendModuleResolver::FrontendModuleResolver(Frontend* frontend)
CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOptions> optionOverride)
{
LUAU_TIMETRACE_SCOPE("Frontend::check", "Frontend");
LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
lluz_TIMETRACE_SCOPE(XorStr("Frontend::check", "Frontend"));
lluz_TIMETRACE_ARGUMENT("name", name.c_str());
FrontendOptions frontendOptions = optionOverride.value_or(options);
CheckResult checkResult;
@ -386,17 +386,17 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
// Keep track of which AST nodes we've reported cycles in
std::unordered_set<AstNode*> reportedCycles;
double autocompleteTimeLimit = FInt::LuauAutocompleteCheckTimeoutMs / 1000.0;
double autocompleteTimeLimit = FInt::LlluztocompleteCheckTimeoutMs / 1000.0;
for (const ModuleName& moduleName : buildQueue)
{
LUAU_ASSERT(sourceNodes.count(moduleName));
lluz_ASSERT(sourceNodes.count(moduleName));
SourceNode& sourceNode = sourceNodes[moduleName];
if (!sourceNode.hasDirtyModule(frontendOptions.forAutocomplete))
continue;
LUAU_ASSERT(sourceModules.count(moduleName));
lluz_ASSERT(sourceModules.count(moduleName));
SourceModule& sourceModule = sourceModules[moduleName];
const Config& config = configResolver->getConfig(moduleName);
@ -430,20 +430,20 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
else
typeCheckerForAutocomplete.finishTime = std::nullopt;
if (FFlag::LuauAutocompleteDynamicLimits)
if (FFlag::LlluztocompleteDynamicLimits)
{
// TODO: This is a dirty ad hoc solution for autocomplete timeouts
// We are trying to dynamically adjust our existing limits to lower total typechecking time under the limit
// so that we'll have type information for the whole file at lower quality instead of a full abort in the middle
if (FInt::LuauTarjanChildLimit > 0)
if (FInt::LluTarjanChildLimit > 0)
typeCheckerForAutocomplete.instantiationChildLimit =
std::max(1, int(FInt::LuauTarjanChildLimit * sourceNode.autocompleteLimitsMult));
std::max(1, int(FInt::LluTarjanChildLimit * sourceNode.autocompleteLimitsMult));
else
typeCheckerForAutocomplete.instantiationChildLimit = std::nullopt;
if (FInt::LuauTypeInferIterationLimit > 0)
if (FInt::LluTypeInferIterationLimit > 0)
typeCheckerForAutocomplete.unifierIterationLimit =
std::max(1, int(FInt::LuauTypeInferIterationLimit * sourceNode.autocompleteLimitsMult));
std::max(1, int(FInt::LluTypeInferIterationLimit * sourceNode.autocompleteLimitsMult));
else
typeCheckerForAutocomplete.unifierIterationLimit = std::nullopt;
}
@ -457,10 +457,10 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
{
checkResult.timeoutHits.push_back(moduleName);
if (FFlag::LuauAutocompleteDynamicLimits)
if (FFlag::LlluztocompleteDynamicLimits)
sourceNode.autocompleteLimitsMult = sourceNode.autocompleteLimitsMult / 2.0;
}
else if (FFlag::LuauAutocompleteDynamicLimits && duration < autocompleteTimeLimit / 2.0)
else if (FFlag::LlluztocompleteDynamicLimits && duration < autocompleteTimeLimit / 2.0)
{
sourceNode.autocompleteLimitsMult = std::min(sourceNode.autocompleteLimitsMult * 2.0, 1.0);
}
@ -474,7 +474,7 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
typeChecker.requireCycles = requireCycles;
ModulePtr module = FFlag::DebugLuauDeferredConstraintResolution ? check(sourceModule, mode, environmentScope)
ModulePtr module = FFlag::DebugLluDeferredConstraintResolution ? check(sourceModule, mode, environmentScope)
: typeChecker.check(sourceModule, mode, environmentScope);
stats.timeCheck += getTimestamp() - timestamp;
@ -529,8 +529,8 @@ CheckResult Frontend::check(const ModuleName& name, std::optional<FrontendOption
bool Frontend::parseGraph(std::vector<ModuleName>& buildQueue, CheckResult& checkResult, const ModuleName& root, bool forAutocomplete)
{
LUAU_TIMETRACE_SCOPE("Frontend::parseGraph", "Frontend");
LUAU_TIMETRACE_ARGUMENT("root", root.c_str());
lluz_TIMETRACE_SCOPE(XorStr("Frontend::parseGraph", "Frontend"));
lluz_TIMETRACE_ARGUMENT("root", root.c_str());
// https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
enum Mark
@ -559,14 +559,14 @@ bool Frontend::parseGraph(std::vector<ModuleName>& buildQueue, CheckResult& chec
if (top == nullptr)
{
// special marker for post-order processing
LUAU_ASSERT(!path.empty());
lluz_ASSERT(!path.empty());
top = path.back();
path.pop_back();
// note: topseen ref gets invalidated in any seen[] access, beware - only one seen[] access per iteration!
Mark& topseen = seen[top];
LUAU_ASSERT(topseen == Temporary);
lluz_ASSERT(topseen == Temporary);
topseen = Permanent;
buildQueue.push_back(top->name);
@ -648,10 +648,10 @@ ScopePtr Frontend::getModuleEnvironment(const SourceModule& module, const Config
return result;
}
LintResult Frontend::lint(const ModuleName& name, std::optional<Luau::LintOptions> enabledLintWarnings)
LintResult Frontend::lint(const ModuleName& name, std::optional<lluz::LintOptions> enabledLintWarnings)
{
LUAU_TIMETRACE_SCOPE("Frontend::lint", "Frontend");
LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
lluz_TIMETRACE_SCOPE(XorStr("Frontend::lint", "Frontend"));
lluz_TIMETRACE_ARGUMENT("name", name.c_str());
CheckResult checkResult;
auto [_sourceNode, sourceModule] = getSourceNode(checkResult, name);
@ -662,10 +662,33 @@ LintResult Frontend::lint(const ModuleName& name, std::optional<Luau::LintOption
return lint(*sourceModule, enabledLintWarnings);
}
LintResult Frontend::lint(const SourceModule& module, std::optional<Luau::LintOptions> enabledLintWarnings)
std::pair<SourceModule, LintResult> Frontend::lintFragment(std::string_view source, std::optional<lluz::LintOptions> enabledLintWarnings)
{
LUAU_TIMETRACE_SCOPE("Frontend::lint", "Frontend");
LUAU_TIMETRACE_ARGUMENT("module", module.name.c_str());
lluz_TIMETRACE_SCOPE(XorStr("Frontend::lintFragment", "Frontend"));
const Config& config = configResolver->getConfig(XorStr(""));
SourceModule sourceModule = parse(ModuleName{}, source, config.parseOptions);
uint64_t ignoreLints = LintWarning::parseMask(sourceModule.hotcomments);
lluz::LintOptions lintOptions = enabledLintWarnings.value_or(config.enabledLint);
lintOptions.warningMask &= ~ignoreLints;
double timestamp = getTimestamp();
std::vector<LintWarning> warnings = lluz::lint(sourceModule.root, *sourceModule.names.get(), typeChecker.globalScope, nullptr,
sourceModule.hotcomments, enabledLintWarnings.value_or(config.enabledLint));
stats.timeLint += getTimestamp() - timestamp;
return {std::move(sourceModule), classifyLints(warnings, config)};
}
LintResult Frontend::lint(const SourceModule& module, std::optional<lluz::LintOptions> enabledLintWarnings)
{
lluz_TIMETRACE_SCOPE(XorStr("Frontend::lint", "Frontend"));
lluz_TIMETRACE_ARGUMENT("module", module.name.c_str());
const Config& config = configResolver->getConfig(module.name);
@ -677,12 +700,12 @@ LintResult Frontend::lint(const SourceModule& module, std::optional<Luau::LintOp
Mode mode = module.mode.value_or(config.mode);
if (mode != Mode::NoCheck)
{
options.disableWarning(Luau::LintWarning::Code_UnknownGlobal);
options.disableWarning(lluz::LintWarning::Code_UnknownGlobal);
}
if (mode == Mode::Strict)
{
options.disableWarning(Luau::LintWarning::Code_ImplicitReturn);
options.disableWarning(lluz::LintWarning::Code_ImplicitReturn);
}
ScopePtr environmentScope = getModuleEnvironment(module, config);
@ -691,7 +714,7 @@ LintResult Frontend::lint(const SourceModule& module, std::optional<Luau::LintOp
double timestamp = getTimestamp();
std::vector<LintWarning> warnings = Luau::lint(module.root, *module.names, environmentScope, modulePtr.get(), module.hotcomments, options);
std::vector<LintWarning> warnings = lluz::lint(module.root, *module.names, environmentScope, modulePtr.get(), module.hotcomments, options);
stats.timeLint += getTimestamp() - timestamp;
@ -729,7 +752,7 @@ void Frontend::markDirty(const ModuleName& name, std::vector<ModuleName>* marked
ModuleName next = std::move(queue.back());
queue.pop_back();
LUAU_ASSERT(sourceNodes.count(next) > 0);
lluz_ASSERT(sourceNodes.count(next) > 0);
SourceNode& sourceNode = sourceNodes[next];
if (markedDirty)
@ -766,35 +789,35 @@ const SourceModule* Frontend::getSourceModule(const ModuleName& moduleName) cons
return const_cast<Frontend*>(this)->getSourceModule(moduleName);
}
NotNull<Scope> Frontend::getGlobalScope()
NotNull<Scope2> Frontend::getGlobalScope2()
{
if (!globalScope)
if (!globalScope2)
{
const SingletonTypes& singletonTypes = getSingletonTypes();
globalScope = std::make_unique<Scope>(singletonTypes.anyTypePack);
globalScope->typeBindings["nil"] = singletonTypes.nilType;
globalScope->typeBindings["number"] = singletonTypes.numberType;
globalScope->typeBindings["string"] = singletonTypes.stringType;
globalScope->typeBindings["boolean"] = singletonTypes.booleanType;
globalScope->typeBindings["thread"] = singletonTypes.threadType;
globalScope2 = std::make_unique<Scope2>();
globalScope2->typeBindings["nil"] = singletonTypes.nilType;
globalScope2->typeBindings["number"] = singletonTypes.numberType;
globalScope2->typeBindings["string"] = singletonTypes.stringType;
globalScope2->typeBindings["boolean"] = singletonTypes.booleanType;
globalScope2->typeBindings["thread"] = singletonTypes.threadType;
}
return NotNull(globalScope.get());
return NotNull(globalScope2.get());
}
ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope)
{
ModulePtr result = std::make_shared<Module>();
ConstraintGraphBuilder cgb{sourceModule.name, &result->internalTypes, NotNull(&iceHandler), getGlobalScope()};
ConstraintGraphBuilder cgb{sourceModule.name, &result->internalTypes, NotNull(&iceHandler), getGlobalScope2()};
cgb.visit(sourceModule.root);
result->errors = std::move(cgb.errors);
ConstraintSolver cs{&result->internalTypes, NotNull(cgb.rootScope)};
cs.run();
result->scopes = std::move(cgb.scopes);
result->scope2s = std::move(cgb.scopes);
result->astTypes = std::move(cgb.astTypes);
result->astTypePacks = std::move(cgb.astTypePacks);
result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes);
@ -803,7 +826,7 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const Sco
result->clonePublicInterface(iceHandler);
Luau::check(sourceModule, result.get());
lluz::check(sourceModule, result.get());
return result;
}
@ -811,8 +834,8 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, const Sco
// Read AST into sourceModules if necessary. Trace require()s. Report parse errors.
std::pair<SourceNode*, SourceModule*> Frontend::getSourceNode(CheckResult& checkResult, const ModuleName& name)
{
LUAU_TIMETRACE_SCOPE("Frontend::getSourceNode", "Frontend");
LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
lluz_TIMETRACE_SCOPE(XorStr("Frontend::getSourceNode", "Frontend"));
lluz_TIMETRACE_ARGUMENT("name", name.c_str());
auto it = sourceNodes.find(name);
if (it != sourceNodes.end() && !it->second.hasDirtySourceModule())
@ -822,7 +845,7 @@ std::pair<SourceNode*, SourceModule*> Frontend::getSourceNode(CheckResult& check
return {&it->second, &moduleIt->second};
else
{
LUAU_ASSERT(!"Everything in sourceNodes should also be in sourceModules");
lluz_ASSERT(!XorStr("Everything in sourceNodes should also be in sourceModules"));
return {&it->second, nullptr};
}
}
@ -884,19 +907,19 @@ std::pair<SourceNode*, SourceModule*> Frontend::getSourceNode(CheckResult& check
* If the file has syntax errors, we report them and synthesize an empty AST if it's not available.
* This suppresses the Unknown require error and allows us to make a best effort to typecheck code that require()s
* something that has broken syntax.
* We also translate Luau::ParseError into a Luau::TypeError so that we can use a vector<TypeError> to describe the
* We also translate lluz::ParseError into a lluz::TypeError so that we can use a vector<TypeError> to describe the
* result of the check()
*/
SourceModule Frontend::parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions)
{
LUAU_TIMETRACE_SCOPE("Frontend::parse", "Frontend");
LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
lluz_TIMETRACE_SCOPE(XorStr("Frontend::parse", "Frontend"));
lluz_TIMETRACE_ARGUMENT("name", name.c_str());
SourceModule sourceModule;
double timestamp = getTimestamp();
auto parseResult = Luau::Parser::parse(src.data(), src.size(), *sourceModule.names, *sourceModule.allocator, parseOptions);
auto parseResult = lluz::Parser::parse(src.data(), src.size(), *sourceModule.names, *sourceModule.allocator, parseOptions);
stats.timeParse += getTimestamp() - timestamp;
stats.files++;
@ -935,7 +958,7 @@ std::optional<ModuleInfo> FrontendModuleResolver::resolveModuleInfo(const Module
{
// CLI-43699
// If we can't find the current module name, that's because we bypassed the frontend's initializer
// and called typeChecker.check directly.
// and called typeChecker.check directly. (This is done by autocompleteSource, for example).
// In that case, requires will always fail.
return std::nullopt;
}
@ -970,7 +993,7 @@ std::string FrontendModuleResolver::getHumanReadableModuleName(const ModuleName&
ScopePtr Frontend::addEnvironment(const std::string& environmentName)
{
LUAU_ASSERT(environments.count(environmentName) == 0);
lluz_ASSERT(environments.count(environmentName) == 0);
if (environments.count(environmentName) == 0)
{
@ -984,14 +1007,14 @@ ScopePtr Frontend::addEnvironment(const std::string& environmentName)
ScopePtr Frontend::getEnvironmentScope(const std::string& environmentName)
{
LUAU_ASSERT(environments.count(environmentName) > 0);
lluz_ASSERT(environments.count(environmentName) > 0);
return environments[environmentName];
}
void Frontend::registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)> applicator)
{
LUAU_ASSERT(builtinDefinitions.count(name) == 0);
lluz_ASSERT(builtinDefinitions.count(name) == 0);
if (builtinDefinitions.count(name) == 0)
builtinDefinitions[name] = applicator;
@ -999,7 +1022,7 @@ void Frontend::registerBuiltinDefinition(const std::string& name, std::function<
void Frontend::applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName)
{
LUAU_ASSERT(builtinDefinitions.count(definitionName) > 0);
lluz_ASSERT(builtinDefinitions.count(definitionName) > 0);
if (builtinDefinitions.count(definitionName) > 0)
builtinDefinitions[definitionName](typeChecker, getEnvironmentScope(environmentName));
@ -1033,4 +1056,4 @@ void Frontend::clear()
requireTrace.clear();
}
} // namespace Luau
} // namespace lluz

View file

@ -1,10 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Common.h"
#include "Luau/Instantiation.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeArena.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Common.h"
#include "lluz/Instantiation.h"
#include "lluz/TxnLog.h"
#include "lluz/TypeArena.h"
namespace Luau
namespace lluz
{
bool Instantiation::isDirty(TypeId ty)
@ -38,7 +38,7 @@ bool Instantiation::ignoreChildren(TypeId ty)
TypeId Instantiation::clean(TypeId ty)
{
const FunctionTypeVar* ftv = log->getMutable<FunctionTypeVar>(ty);
LUAU_ASSERT(ftv);
lluz_ASSERT(ftv);
FunctionTypeVar clone = FunctionTypeVar{level, ftv->argTypes, ftv->retTypes, ftv->definition, ftv->hasSelf};
clone.magicFunction = ftv->magicFunction;
@ -60,7 +60,7 @@ TypeId Instantiation::clean(TypeId ty)
TypePackId Instantiation::clean(TypePackId tp)
{
LUAU_ASSERT(false);
lluz_ASSERT(false);
return tp;
}
@ -104,7 +104,7 @@ bool ReplaceGenerics::isDirty(TypePackId tp)
TypeId ReplaceGenerics::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
lluz_ASSERT(isDirty(ty));
if (const TableTypeVar* ttv = log->getMutable<TableTypeVar>(ty))
{
TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, level, TableState::Free};
@ -117,8 +117,8 @@ TypeId ReplaceGenerics::clean(TypeId ty)
TypePackId ReplaceGenerics::clean(TypePackId tp)
{
LUAU_ASSERT(isDirty(tp));
lluz_ASSERT(isDirty(tp));
return addTypePack(TypePackVar(FreeTypePack{level}));
}
} // namespace Luau
} // namespace lluz

View file

@ -1,8 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/IostreamHelpers.h"
#include "Luau/ToString.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/IostreamHelpers.h"
#include "lluz/ToString.h"
namespace Luau
namespace lluz
{
std::ostream& operator<<(std::ostream& stream, const Position& position)
@ -189,7 +189,7 @@ static void errorToString(std::ostream& stream, const T& err)
else if constexpr (std::is_same_v<T, NormalizationTooComplex>)
stream << "NormalizationTooComplex { }";
else
static_assert(always_false_v<T>, "Non-exhaustive type switch");
static_assert(always_false_v<T>, XorStr("Non-exhaustive type switch"));
}
std::ostream& operator<<(std::ostream& stream, const TypeErrorData& data)
@ -221,4 +221,4 @@ std::ostream& operator<<(std::ostream& stream, const TypePackVar& tv)
return stream << toString(tv);
}
} // namespace Luau
} // namespace lluz

View file

@ -1,12 +1,11 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/JsonEncoder.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/JsonEncoder.h"
#include "Luau/Ast.h"
#include "Luau/ParseResult.h"
#include "Luau/StringUtils.h"
#include "Luau/Common.h"
#include "lluz/Ast.h"
#include "lluz/StringUtils.h"
#include "lluz/Common.h"
namespace Luau
namespace lluz
{
struct AstJsonEncoder : public AstVisitor
@ -22,7 +21,7 @@ struct AstJsonEncoder : public AstVisitor
std::string str()
{
return join(chunks, "");
return join(chunks, XorStr(""));
}
bool pushComma()
@ -76,58 +75,49 @@ struct AstJsonEncoder : public AstVisitor
writeRaw(std::string_view{&c, 1});
}
void writeType(std::string_view propValue)
{
write("type", propValue);
}
template<typename T>
void write(std::string_view propName, const T& value)
{
if (comma)
writeRaw(",");
writeRaw(XorStr(","));
comma = true;
writeRaw("\"");
writeRaw(XorStr("\""));
writeRaw(propName);
writeRaw("\":");
writeRaw(XorStr("\":"));
write(value);
}
void write(bool b)
{
if (b)
writeRaw("true");
writeRaw(XorStr("true"));
else
writeRaw("false");
writeRaw(XorStr("false"));
}
void write(double d)
{
char b[32];
snprintf(b, sizeof(b), "%.17g", d);
char b[256];
sprintf(b, "%g", d);
writeRaw(b);
}
void writeString(std::string_view sv)
{
// TODO escape more accurately?
writeRaw("\"");
writeRaw(XorStr("\""));
for (char c : sv)
{
if (c == '"')
writeRaw("\\\"");
else if (c == '\\')
writeRaw("\\\\");
else if (c < ' ')
writeRaw(format("\\u%04x", c));
else if (c == '\n')
writeRaw("\\n");
writeRaw(XorStr("\\\""));
else if (c == '\0')
writeRaw(XorStr("\\\0"));
else
writeRaw(c);
}
writeRaw("\"");
writeRaw(XorStr("\""));
}
void write(char c)
@ -160,7 +150,7 @@ struct AstJsonEncoder : public AstVisitor
}
void write(std::nullptr_t)
{
writeRaw("null");
writeRaw(XorStr("null"));
}
void write(std::string_view str)
{
@ -171,42 +161,41 @@ struct AstJsonEncoder : public AstVisitor
if (name)
write(*name);
else
writeRaw("null");
writeRaw(XorStr("null"));
}
void write(AstName name)
{
writeString(name.value ? name.value : "");
writeString(name.value ? name.value : XorStr(""));
}
void write(const Position& position)
{
write(position.line);
writeRaw(",");
writeRaw(XorStr(","));
write(position.column);
}
void write(const Location& location)
{
writeRaw("\"");
writeRaw(XorStr("\""));
write(location.begin);
writeRaw(" - ");
writeRaw(XorStr(" - "));
write(location.end);
writeRaw("\"");
writeRaw(XorStr("\""));
}
void write(AstLocal* local)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
if (local->annotation != nullptr)
write("luauType", local->annotation);
write("type", local->annotation);
else
write("luauType", nullptr);
write("type", nullptr);
write("name", local->name);
writeType("AstLocal");
write("location", local->location);
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void writeNode(AstNode* node)
@ -217,13 +206,13 @@ struct AstJsonEncoder : public AstVisitor
template<typename F>
void writeNode(AstNode* node, std::string_view name, F&& f)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
writeType(name);
write("type", name);
writeNode(node);
f();
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void write(AstNode* node)
@ -286,18 +275,18 @@ struct AstJsonEncoder : public AstVisitor
template<typename T>
void write(AstArray<T> arr)
{
writeRaw("[");
writeRaw(XorStr("["));
bool comma = false;
for (const auto& a : arr)
{
if (comma)
writeRaw(",");
writeRaw(XorStr(","));
else
comma = true;
write(a);
}
writeRaw("]");
writeRaw(XorStr("]"));
}
void write(AstArray<char> arr)
@ -362,43 +351,40 @@ struct AstJsonEncoder : public AstVisitor
if (typeList)
write(*typeList);
else
writeRaw("null");
writeRaw(XorStr("null"));
}
void write(const AstTypeList& typeList)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
writeType("AstTypeList");
write("types", typeList.types);
if (typeList.tailType)
write("tailType", typeList.tailType);
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void write(const AstGenericType& genericType)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
writeType("AstGenericType");
write("name", genericType.name);
if (genericType.defaultValue)
write("luauType", genericType.defaultValue);
write("type", genericType.defaultValue);
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void write(const AstGenericTypePack& genericTypePack)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
writeType("AstGenericTypePack");
write("name", genericTypePack.name);
if (genericTypePack.defaultValue)
write("luauType", genericTypePack.defaultValue);
write("type", genericTypePack.defaultValue);
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void write(AstExprTable::Item::Kind kind)
@ -406,19 +392,18 @@ struct AstJsonEncoder : public AstVisitor
switch (kind)
{
case AstExprTable::Item::List:
return writeString("item");
return writeString(XorStr("item"));
case AstExprTable::Item::Record:
return writeString("record");
return writeString(XorStr("record"));
case AstExprTable::Item::General:
return writeString("general");
return writeString(XorStr("general"));
}
}
void write(const AstExprTable::Item& item)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
writeType("AstExprTableItem");
write("kind", item.kind);
switch (item.kind)
{
@ -431,18 +416,7 @@ struct AstJsonEncoder : public AstVisitor
break;
}
popComma(c);
writeRaw("}");
}
void write(class AstExprIfElse* node)
{
writeNode(node, "AstExprIfElse", [&]() {
PROP(condition);
PROP(hasThen);
PROP(trueExpr);
PROP(hasElse);
PROP(falseExpr);
});
writeRaw(XorStr("}"));
}
void write(class AstExprTable* node)
@ -457,11 +431,11 @@ struct AstJsonEncoder : public AstVisitor
switch (op)
{
case AstExprUnary::Not:
return writeString("Not");
return writeString(XorStr("not"));
case AstExprUnary::Minus:
return writeString("Minus");
return writeString(XorStr("minus"));
case AstExprUnary::Len:
return writeString("Len");
return writeString(XorStr("len"));
}
}
@ -478,35 +452,35 @@ struct AstJsonEncoder : public AstVisitor
switch (op)
{
case AstExprBinary::Add:
return writeString("Add");
return writeString(XorStr("Add"));
case AstExprBinary::Sub:
return writeString("Sub");
return writeString(XorStr("Sub"));
case AstExprBinary::Mul:
return writeString("Mul");
return writeString(XorStr("Mul"));
case AstExprBinary::Div:
return writeString("Div");
return writeString(XorStr("Div"));
case AstExprBinary::Mod:
return writeString("Mod");
return writeString(XorStr("Mod"));
case AstExprBinary::Pow:
return writeString("Pow");
return writeString(XorStr("Pow"));
case AstExprBinary::Concat:
return writeString("Concat");
return writeString(XorStr("Concat"));
case AstExprBinary::CompareNe:
return writeString("CompareNe");
return writeString(XorStr("CompareNe"));
case AstExprBinary::CompareEq:
return writeString("CompareEq");
return writeString(XorStr("CompareEq"));
case AstExprBinary::CompareLt:
return writeString("CompareLt");
return writeString(XorStr("CompareLt"));
case AstExprBinary::CompareLe:
return writeString("CompareLe");
return writeString(XorStr("CompareLe"));
case AstExprBinary::CompareGt:
return writeString("CompareGt");
return writeString(XorStr("CompareGt"));
case AstExprBinary::CompareGe:
return writeString("CompareGe");
return writeString(XorStr("CompareGe"));
case AstExprBinary::And:
return writeString("And");
return writeString(XorStr("And"));
case AstExprBinary::Or:
return writeString("Or");
return writeString(XorStr("Or"));
}
}
@ -538,18 +512,18 @@ struct AstJsonEncoder : public AstVisitor
void write(class AstStatBlock* node)
{
writeNode(node, "AstStatBlock", [&]() {
writeRaw(",\"body\":[");
writeRaw(XorStr(",\"body\":["));
bool comma = false;
for (AstStat* stat : node->body)
{
if (comma)
writeRaw(",");
writeRaw(XorStr(","));
else
comma = true;
write(stat);
}
writeRaw("]");
writeRaw(XorStr("]"));
});
}
@ -567,7 +541,7 @@ struct AstJsonEncoder : public AstVisitor
void write(class AstStatWhile* node)
{
writeNode(node, "AstStatWhile", [&]() {
writeNode(node, "AtStatWhile", [&]() {
PROP(condition);
PROP(body);
PROP(hasDo);
@ -707,13 +681,12 @@ struct AstJsonEncoder : public AstVisitor
void write(const AstDeclaredClassProp& prop)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
write("name", prop.name);
writeType("AstDeclaredClassProp");
write("luauType", prop.ty);
write("type", prop.ty);
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void write(class AstStatDeclareClass* node)
@ -754,16 +727,15 @@ struct AstJsonEncoder : public AstVisitor
void write(const AstTableProp& prop)
{
writeRaw("{");
writeRaw(XorStr("{"));
bool c = pushComma();
write("name", prop.name);
writeType("AstTableProp");
write("location", prop.location);
write("propType", prop.type);
write("type", prop.type);
popComma(c);
writeRaw("}");
writeRaw(XorStr("}"));
}
void write(class AstTypeTable* node)
@ -773,24 +745,6 @@ struct AstJsonEncoder : public AstVisitor
PROP(indexer);
});
}
void write(struct AstTableIndexer* indexer)
{
if (indexer)
{
writeRaw("{");
bool c = pushComma();
write("location", indexer->location);
write("indexType", indexer->indexType);
write("resultType", indexer->resultType);
popComma(c);
writeRaw("}");
}
else
{
writeRaw("null");
}
}
void write(class AstTypeFunction* node)
{
@ -882,12 +836,6 @@ struct AstJsonEncoder : public AstVisitor
return false;
}
bool visit(class AstExprIfElse* node) override
{
write(node);
return false;
}
bool visit(class AstExprLocal* node) override
{
write(node);
@ -1145,42 +1093,6 @@ struct AstJsonEncoder : public AstVisitor
write(node);
return false;
}
void writeComments(std::vector<Comment> commentLocations)
{
bool commentComma = false;
for (Comment comment : commentLocations)
{
if (commentComma)
{
writeRaw(",");
}
else
{
commentComma = true;
}
writeRaw("{");
bool c = pushComma();
switch (comment.type)
{
case Lexeme::Comment:
writeType("Comment");
break;
case Lexeme::BlockComment:
writeType("BlockComment");
break;
case Lexeme::BrokenComment:
writeType("BrokenComment");
break;
default:
break;
}
write("location", comment.location);
popComma(c);
writeRaw("}");
}
}
};
std::string toJson(AstNode* node)
@ -1190,15 +1102,4 @@ std::string toJson(AstNode* node)
return encoder.str();
}
std::string toJson(AstNode* node, const std::vector<Comment>& commentLocations)
{
AstJsonEncoder encoder;
encoder.writeRaw(R"({"root":)");
node->visit(&encoder);
encoder.writeRaw(R"(,"commentLocations":[)");
encoder.writeComments(commentLocations);
encoder.writeRaw("]}");
return encoder.str();
}
} // namespace Luau
} // namespace lluz

View file

@ -1,16 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/LValue.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/LValue.h"
#include "Luau/Ast.h"
#include "lluz/Ast.h"
#include <vector>
namespace Luau
namespace lluz
{
bool Field::operator==(const Field& rhs) const
{
LUAU_ASSERT(parent && rhs.parent);
lluz_ASSERT(parent && rhs.parent);
return key == rhs.key && (parent == rhs.parent || *parent == *rhs.parent);
}
@ -34,7 +34,7 @@ size_t LValueHasher::operator()(const LValue& lvalue) const
else if (auto symbol = get<Symbol>(*current))
acc ^= std::hash<Symbol>{}(*symbol) << 1;
else
LUAU_ASSERT(!"Hash not accumulated for this new LValue alternative.");
lluz_ASSERT(!XorStr("Hash not accumulated for this new LValue alternative."));
current = baseof(*current);
}
@ -48,7 +48,7 @@ const LValue* baseof(const LValue& lvalue)
return field->parent.get();
auto symbol = get<Symbol>(lvalue);
LUAU_ASSERT(symbol);
lluz_ASSERT(symbol);
return nullptr; // Base of root is null.
}
@ -84,7 +84,7 @@ Symbol getBaseSymbol(const LValue& lvalue)
current = baseof(*current);
const Symbol* symbol = get<Symbol>(*current);
LUAU_ASSERT(symbol);
lluz_ASSERT(symbol);
return *symbol;
}
@ -104,4 +104,4 @@ void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty)
refis[lvalue] = ty;
}
} // namespace Luau
} // namespace lluz

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,24 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Module.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Module.h"
#include "Luau/Clone.h"
#include "Luau/Common.h"
#include "Luau/ConstraintGraphBuilder.h"
#include "Luau/Normalize.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
#include "Luau/VisitTypeVar.h"
#include "lluz/Clone.h"
#include "lluz/Common.h"
#include "lluz/ConstraintGraphBuilder.h"
#include "lluz/Normalize.h"
#include "lluz/RecursionCounter.h"
#include "lluz/Scope.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
#include "lluz/VisitTypeVar.h"
#include <algorithm>
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false);
lluz_FASTFLAG(LluLowerBoundsCalculation);
lluz_FASTFLAG(LluNormalizeFlagIsConservative);
lluz_FASTFLAG(DebugLluDeferredConstraintResolution);
namespace Luau
namespace lluz
{
static bool contains(Position pos, Comment comment)
@ -95,66 +94,59 @@ Module::~Module()
void Module::clonePublicInterface(InternalErrorReporter& ice)
{
LUAU_ASSERT(interfaceTypes.typeVars.empty());
LUAU_ASSERT(interfaceTypes.typePacks.empty());
lluz_ASSERT(interfaceTypes.typeVars.empty());
lluz_ASSERT(interfaceTypes.typePacks.empty());
CloneState cloneState;
ScopePtr moduleScope = getModuleScope();
ScopePtr moduleScope = FFlag::DebugLluDeferredConstraintResolution ? nullptr : getModuleScope();
Scope2* moduleScope2 = FFlag::DebugLluDeferredConstraintResolution ? getModuleScope2() : nullptr;
TypePackId returnType = moduleScope->returnType;
std::optional<TypePackId> varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
TypePackId returnType = FFlag::DebugLluDeferredConstraintResolution ? moduleScope2->returnType : moduleScope->returnType;
std::optional<TypePackId> varargPack = FFlag::DebugLluDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack;
std::unordered_map<Name, TypeFun>* exportedTypeBindings =
FFlag::DebugLuauDeferredConstraintResolution ? nullptr : &moduleScope->exportedTypeBindings;
FFlag::DebugLluDeferredConstraintResolution ? nullptr : &moduleScope->exportedTypeBindings;
returnType = clone(returnType, interfaceTypes, cloneState);
moduleScope->returnType = returnType;
if (varargPack)
if (moduleScope)
{
varargPack = clone(*varargPack, interfaceTypes, cloneState);
moduleScope->varargPack = varargPack;
moduleScope->returnType = returnType;
if (varargPack)
{
varargPack = clone(*varargPack, interfaceTypes, cloneState);
moduleScope->varargPack = varargPack;
}
}
else
{
lluz_ASSERT(moduleScope2);
moduleScope2->returnType = returnType; // TODO varargPack
}
if (FFlag::LluLowerBoundsCalculation)
{
normalize(returnType, interfaceTypes, ice);
if (varargPack)
normalize(*varargPack, interfaceTypes, ice);
}
ForceNormal forceNormal{&interfaceTypes};
if (FFlag::LuauLowerBoundsCalculation)
{
normalize(returnType, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(returnType);
if (varargPack)
{
normalize(*varargPack, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(*varargPack);
}
}
if (exportedTypeBindings)
{
for (auto& [name, tf] : *exportedTypeBindings)
{
tf = clone(tf, interfaceTypes, cloneState);
if (FFlag::LuauLowerBoundsCalculation)
if (FFlag::LluLowerBoundsCalculation)
{
normalize(tf.type, interfaceTypes, ice);
if (FFlag::LuauNormalizeFlagIsConservative)
if (FFlag::LluNormalizeFlagIsConservative)
{
// 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.
forceNormal.traverse(tf.type);
for (GenericTypeDefinition param : tf.typeParams)
{
forceNormal.traverse(param.ty);
if (param.defaultValue)
{
normalize(*param.defaultValue, interfaceTypes, ice);
forceNormal.traverse(*param.defaultValue);
}
}
}
}
}
@ -173,13 +165,8 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
for (auto& [name, ty] : declaredGlobals)
{
ty = clone(ty, interfaceTypes, cloneState);
if (FFlag::LuauLowerBoundsCalculation)
{
if (FFlag::LluLowerBoundsCalculation)
normalize(ty, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(ty);
}
}
freeze(internalTypes);
@ -188,8 +175,14 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
ScopePtr Module::getModuleScope() const
{
LUAU_ASSERT(!scopes.empty());
lluz_ASSERT(!scopes.empty());
return scopes.front().second;
}
} // namespace Luau
Scope2* Module::getModuleScope2() const
{
lluz_ASSERT(!scope2s.empty());
return scope2s.front().second.get();
}
} // namespace lluz

View file

@ -1,24 +1,23 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Normalize.h"
#include "lluz/Normalize.h"
#include <algorithm>
#include "Luau/Clone.h"
#include "Luau/Unifier.h"
#include "Luau/VisitTypeVar.h"
#include "lluz/Clone.h"
#include "lluz/Unifier.h"
#include "lluz/VisitTypeVar.h"
LUAU_FASTFLAGVARIABLE(DebugLuauCopyBeforeNormalizing, false)
lluz_FASTFLAGVARIABLE(DebugLluCopyBeforeNormalizing, false)
// This could theoretically be 2000 on amd64, but x86 requires this.
LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200);
LUAU_FASTFLAGVARIABLE(LuauNormalizeCombineTableFix, false);
LUAU_FASTFLAGVARIABLE(LuauNormalizeFlagIsConservative, false);
LUAU_FASTFLAGVARIABLE(LuauFixNormalizationOfCyclicUnions, false);
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAG(LuauQuantifyConstrained)
lluz_FASTINTVARIABLE(LluNormalizeIterationLimit, 1200);
lluz_FASTFLAGVARIABLE(LluNormalizeCombineTableFix, false);
lluz_FASTFLAGVARIABLE(LluNormalizeFlagIsConservative, false);
lluz_FASTFLAGVARIABLE(LluNormalizeCombineEqFix, false);
lluz_FASTFLAG(LluQuantifyConstrained)
namespace Luau
namespace lluz
{
namespace
@ -86,10 +85,10 @@ static bool areNormal_(const T& t, const std::unordered_set<void*>& seen, Intern
int count = 0;
auto isNormal = [&](TypeId ty) {
++count;
if (count >= FInt::LuauNormalizeIterationLimit)
ice.ice("Luau::areNormal hit iteration limit");
if (count >= FInt::LluNormalizeIterationLimit)
ice.ice(XorStr("lluz::areNormal hit iteration limit"));
if (FFlag::LuauNormalizeFlagIsConservative)
if (FFlag::LluNormalizeFlagIsConservative)
return ty->normal;
else
{
@ -129,7 +128,7 @@ static bool areNormal(TypePackId tp, const std::unordered_set<void*>& seen, Inte
#define CHECK_ITERATION_LIMIT(...) \
do \
{ \
if (iterationLimit > FInt::LuauNormalizeIterationLimit) \
if (iterationLimit > FInt::LluNormalizeIterationLimit) \
{ \
limitExceeded = true; \
return __VA_ARGS__; \
@ -155,7 +154,7 @@ struct Normalize final : TypeVarVisitor
bool visit(TypeId ty, const FreeTypeVar&) override
{
LUAU_ASSERT(!ty->normal);
lluz_ASSERT(!ty->normal);
return false;
}
@ -167,7 +166,7 @@ struct Normalize final : TypeVarVisitor
return false;
// It should never be the case that this TypeVar is normal, but is bound to a non-normal type, except in nontrivial cases.
LUAU_ASSERT(!ty->normal || ty->normal == btv.boundTo->normal);
lluz_ASSERT(!ty->normal || ty->normal == btv.boundTo->normal);
asMutable(ty)->normal = btv.boundTo->normal;
return !ty->normal;
@ -175,7 +174,7 @@ struct Normalize final : TypeVarVisitor
bool visit(TypeId ty, const PrimitiveTypeVar&) override
{
LUAU_ASSERT(ty->normal);
lluz_ASSERT(ty->normal);
return false;
}
@ -183,6 +182,7 @@ struct Normalize final : TypeVarVisitor
{
if (!ty->normal)
asMutable(ty)->normal = true;
return false;
}
@ -193,24 +193,10 @@ struct Normalize final : TypeVarVisitor
return false;
}
bool visit(TypeId ty, const UnknownTypeVar&) override
{
if (!ty->normal)
asMutable(ty)->normal = true;
return false;
}
bool visit(TypeId ty, const NeverTypeVar&) override
{
if (!ty->normal)
asMutable(ty)->normal = true;
return false;
}
bool visit(TypeId ty, const ConstrainedTypeVar& ctvRef) override
{
CHECK_ITERATION_LIMIT(false);
LUAU_ASSERT(!ty->normal);
lluz_ASSERT(!ty->normal);
ConstrainedTypeVar* ctv = const_cast<ConstrainedTypeVar*>(&ctvRef);
@ -222,7 +208,7 @@ struct Normalize final : TypeVarVisitor
std::vector<TypeId> newParts = normalizeUnion(parts);
if (FFlag::LuauQuantifyConstrained)
if (FFlag::LluQuantifyConstrained)
{
ctv->parts = std::move(newParts);
}
@ -294,7 +280,7 @@ struct Normalize final : TypeVarVisitor
}
// An unsealed table can never be normal, ditto for free tables iff the type it is bound to is also not normal.
if (FFlag::LuauQuantifyConstrained)
if (FFlag::LluQuantifyConstrained)
{
if (ttv.state == TableState::Generic || ttv.state == TableState::Sealed || (ttv.state == TableState::Free && follow(ty)->normal))
asMutable(ty)->normal = normal;
@ -329,7 +315,7 @@ struct Normalize final : TypeVarVisitor
bool visit(TypeId ty, const AnyTypeVar&) override
{
LUAU_ASSERT(ty->normal);
lluz_ASSERT(ty->normal);
return false;
}
@ -341,23 +327,17 @@ struct Normalize final : TypeVarVisitor
return false;
UnionTypeVar* utv = &const_cast<UnionTypeVar&>(utvRef);
// TODO: Clip tempOptions and optionsRef when clipping FFlag::LuauFixNormalizationOfCyclicUnions
std::vector<TypeId> tempOptions;
if (!FFlag::LuauFixNormalizationOfCyclicUnions)
tempOptions = std::move(utv->options);
std::vector<TypeId>& optionsRef = FFlag::LuauFixNormalizationOfCyclicUnions ? utv->options : tempOptions;
std::vector<TypeId> options = std::move(utv->options);
// We might transmute, so it's not safe to rely on the builtin traversal logic of visitTypeVar
for (TypeId option : optionsRef)
for (TypeId option : options)
traverse(option);
std::vector<TypeId> newOptions = normalizeUnion(optionsRef);
std::vector<TypeId> newOptions = normalizeUnion(options);
const bool normal = areNormal(newOptions, seen, ice);
LUAU_ASSERT(!newOptions.empty());
lluz_ASSERT(!newOptions.empty());
if (newOptions.size() == 1)
*asMutable(ty) = BoundTypeVar{newOptions[0]};
@ -378,106 +358,51 @@ struct Normalize final : TypeVarVisitor
IntersectionTypeVar* itv = &const_cast<IntersectionTypeVar&>(itvRef);
if (FFlag::LuauFixNormalizationOfCyclicUnions)
std::vector<TypeId> oldParts = std::move(itv->parts);
for (TypeId part : oldParts)
traverse(part);
std::vector<TypeId> tables;
for (TypeId part : oldParts)
{
std::vector<TypeId> oldParts = itv->parts;
IntersectionTypeVar newIntersection;
for (TypeId part : oldParts)
traverse(part);
std::vector<TypeId> tables;
for (TypeId part : oldParts)
part = follow(part);
if (get<TableTypeVar>(part))
tables.push_back(part);
else
{
part = follow(part);
if (get<TableTypeVar>(part))
tables.push_back(part);
else
{
Replacer replacer{&arena, nullptr, nullptr}; // FIXME this is super super WEIRD
combineIntoIntersection(replacer, &newIntersection, part);
}
}
// Don't allocate a new table if there's just one in the intersection.
if (tables.size() == 1)
newIntersection.parts.push_back(tables[0]);
else if (!tables.empty())
{
const TableTypeVar* first = get<TableTypeVar>(tables[0]);
LUAU_ASSERT(first);
TypeId newTable = arena.addType(TableTypeVar{first->state, first->level});
TableTypeVar* ttv = getMutable<TableTypeVar>(newTable);
for (TypeId part : tables)
{
// Intuition: If combineIntoTable() needs to clone a table, any references to 'part' are cyclic and need
// to be rewritten to point at 'newTable' in the clone.
Replacer replacer{&arena, part, newTable};
combineIntoTable(replacer, ttv, part);
}
newIntersection.parts.push_back(newTable);
}
itv->parts = std::move(newIntersection.parts);
asMutable(ty)->normal = areNormal(itv->parts, seen, ice);
if (itv->parts.size() == 1)
{
TypeId part = itv->parts[0];
*asMutable(ty) = BoundTypeVar{part};
Replacer replacer{&arena, nullptr, nullptr}; // FIXME this is super super WEIRD
combineIntoIntersection(replacer, itv, part);
}
}
else
// Don't allocate a new table if there's just one in the intersection.
if (tables.size() == 1)
itv->parts.push_back(tables[0]);
else if (!tables.empty())
{
std::vector<TypeId> oldParts = std::move(itv->parts);
const TableTypeVar* first = get<TableTypeVar>(tables[0]);
lluz_ASSERT(first);
for (TypeId part : oldParts)
traverse(part);
std::vector<TypeId> tables;
for (TypeId part : oldParts)
TypeId newTable = arena.addType(TableTypeVar{first->state, first->level});
TableTypeVar* ttv = getMutable<TableTypeVar>(newTable);
for (TypeId part : tables)
{
part = follow(part);
if (get<TableTypeVar>(part))
tables.push_back(part);
else
{
Replacer replacer{&arena, nullptr, nullptr}; // FIXME this is super super WEIRD
combineIntoIntersection(replacer, itv, part);
}
// Intuition: If combineIntoTable() needs to clone a table, any references to 'part' are cyclic and need
// to be rewritten to point at 'newTable' in the clone.
Replacer replacer{&arena, part, newTable};
combineIntoTable(replacer, ttv, part);
}
// Don't allocate a new table if there's just one in the intersection.
if (tables.size() == 1)
itv->parts.push_back(tables[0]);
else if (!tables.empty())
{
const TableTypeVar* first = get<TableTypeVar>(tables[0]);
LUAU_ASSERT(first);
itv->parts.push_back(newTable);
}
TypeId newTable = arena.addType(TableTypeVar{first->state, first->level});
TableTypeVar* ttv = getMutable<TableTypeVar>(newTable);
for (TypeId part : tables)
{
// Intuition: If combineIntoTable() needs to clone a table, any references to 'part' are cyclic and need
// to be rewritten to point at 'newTable' in the clone.
Replacer replacer{&arena, part, newTable};
combineIntoTable(replacer, ttv, part);
}
asMutable(ty)->normal = areNormal(itv->parts, seen, ice);
itv->parts.push_back(newTable);
}
asMutable(ty)->normal = areNormal(itv->parts, seen, ice);
if (itv->parts.size() == 1)
{
TypeId part = itv->parts[0];
*asMutable(ty) = BoundTypeVar{part};
}
if (itv->parts.size() == 1)
{
TypeId part = itv->parts[0];
*asMutable(ty) = BoundTypeVar{part};
}
return false;
@ -491,13 +416,7 @@ struct Normalize final : TypeVarVisitor
std::vector<TypeId> result;
for (TypeId part : options)
{
// AnyTypeVar always win the battle no matter what we do, so we're done.
if (FFlag::LuauUnknownAndNeverType && get<AnyTypeVar>(follow(part)))
return {part};
combineIntoUnion(result, part);
}
return result;
}
@ -508,17 +427,7 @@ struct Normalize final : TypeVarVisitor
if (auto utv = get<UnionTypeVar>(ty))
{
for (TypeId t : utv)
{
// AnyTypeVar always win the battle no matter what we do, so we're done.
if (FFlag::LuauUnknownAndNeverType && get<AnyTypeVar>(t))
{
result = {t};
return;
}
combineIntoUnion(result, t);
}
return;
}
@ -623,12 +532,12 @@ struct Normalize final : TypeVarVisitor
// so if you increase the size of a stack frame, you'll need to decrease the limit.
CHECK_ITERATION_LIMIT();
LUAU_ASSERT(table);
lluz_ASSERT(table);
ty = follow(ty);
TableTypeVar* tyTable = getMutable<TableTypeVar>(ty);
LUAU_ASSERT(tyTable);
lluz_ASSERT(tyTable);
for (const auto& [propName, prop] : tyTable->props)
{
@ -652,24 +561,6 @@ struct Normalize final : TypeVarVisitor
table->props.insert({propName, prop});
}
if (FFlag::LuauFixNormalizationOfCyclicUnions)
{
if (tyTable->indexer)
{
if (table->indexer)
{
table->indexer->indexType = combine(replacer, replacer.smartClone(tyTable->indexer->indexType), table->indexer->indexType);
table->indexer->indexResultType =
combine(replacer, replacer.smartClone(tyTable->indexer->indexResultType), table->indexer->indexResultType);
}
else
{
table->indexer =
TableIndexer{replacer.smartClone(tyTable->indexer->indexType), replacer.smartClone(tyTable->indexer->indexResultType)};
}
}
}
table->state = combineTableStates(table->state, tyTable->state);
table->level = max(table->level, tyTable->level);
}
@ -680,14 +571,15 @@ struct Normalize final : TypeVarVisitor
*/
TypeId combine(Replacer& replacer, TypeId a, TypeId b)
{
b = follow(b);
if (FFlag::LluNormalizeCombineEqFix)
b = follow(b);
if (FFlag::LuauNormalizeCombineTableFix && a == b)
if (FFlag::LluNormalizeCombineTableFix && a == b)
return a;
if (!get<IntersectionTypeVar>(a) && !get<TableTypeVar>(a))
{
if (!FFlag::LuauNormalizeCombineTableFix && a == b)
if (!FFlag::LluNormalizeCombineTableFix && a == b)
return a;
else
return arena.addType(IntersectionTypeVar{{a, b}});
@ -700,14 +592,14 @@ struct Normalize final : TypeVarVisitor
}
else if (auto ttv = getMutable<TableTypeVar>(a))
{
if (FFlag::LuauNormalizeCombineTableFix && !get<TableTypeVar>(b))
if (FFlag::LluNormalizeCombineTableFix && !get<TableTypeVar>(FFlag::LluNormalizeCombineEqFix ? b : follow(b)))
return arena.addType(IntersectionTypeVar{{a, b}});
combineIntoTable(replacer, ttv, b);
return a;
}
LUAU_ASSERT(!"Impossible");
LUAU_UNREACHABLE();
lluz_ASSERT(!XorStr("Impossible"));
lluz_UNREACHABLE();
}
};
@ -719,7 +611,7 @@ struct Normalize final : TypeVarVisitor
std::pair<TypeId, bool> normalize(TypeId ty, TypeArena& arena, InternalErrorReporter& ice)
{
CloneState state;
if (FFlag::DebugLuauCopyBeforeNormalizing)
if (FFlag::DebugLluCopyBeforeNormalizing)
(void)clone(ty, arena, state);
Normalize n{arena, ice};
@ -743,7 +635,7 @@ std::pair<TypeId, bool> normalize(TypeId ty, const ModulePtr& module, InternalEr
std::pair<TypePackId, bool> normalize(TypePackId tp, TypeArena& arena, InternalErrorReporter& ice)
{
CloneState state;
if (FFlag::DebugLuauCopyBeforeNormalizing)
if (FFlag::DebugLluCopyBeforeNormalizing)
(void)clone(tp, arena, state);
Normalize n{arena, ice};
@ -757,4 +649,4 @@ std::pair<TypePackId, bool> normalize(TypePackId tp, const ModulePtr& module, In
return normalize(tp, module->internalTypes, ice);
}
} // namespace Luau
} // namespace lluz

View file

@ -1,28 +1,28 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Quantify.h"
#include "lluz/Quantify.h"
#include "Luau/Scope.h"
#include "Luau/Substitution.h"
#include "Luau/TxnLog.h"
#include "Luau/VisitTypeVar.h"
#include "lluz/Scope.h"
#include "lluz/Substitution.h"
#include "lluz/TxnLog.h"
#include "lluz/VisitTypeVar.h"
LUAU_FASTFLAG(LuauAlwaysQuantify);
LUAU_FASTFLAG(DebugLuauSharedSelf)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauQuantifyConstrained, false)
lluz_FASTFLAG(LluAlwaysQuantify);
lluz_FASTFLAG(DebugLluSharedSelf)
lluz_FASTFLAG(DebugLluDeferredConstraintResolution);
lluz_FASTFLAGVARIABLE(LluQuantifyConstrained, false)
namespace Luau
namespace lluz
{
/// @return true if outer encloses inner
static bool subsumes(Scope* outer, Scope* inner)
static bool subsumes(Scope2* outer, Scope2* inner)
{
while (inner)
{
if (inner == outer)
return true;
inner = inner->parent.get();
inner = inner->parent;
}
return false;
@ -33,30 +33,30 @@ struct Quantifier final : TypeVarOnceVisitor
TypeLevel level;
std::vector<TypeId> generics;
std::vector<TypePackId> genericPacks;
Scope* scope = nullptr;
Scope2* scope = nullptr;
bool seenGenericType = false;
bool seenMutableType = false;
explicit Quantifier(TypeLevel level)
: level(level)
{
LUAU_ASSERT(!FFlag::DebugLuauDeferredConstraintResolution);
lluz_ASSERT(!FFlag::DebugLluDeferredConstraintResolution);
}
explicit Quantifier(Scope* scope)
explicit Quantifier(Scope2* scope)
: scope(scope)
{
LUAU_ASSERT(FFlag::DebugLuauDeferredConstraintResolution);
lluz_ASSERT(FFlag::DebugLluDeferredConstraintResolution);
}
/// @return true if outer encloses inner
bool subsumes(Scope* outer, Scope* inner)
bool subsumes(Scope2* outer, Scope2* inner)
{
while (inner)
{
if (inner == outer)
return true;
inner = inner->parent.get();
inner = inner->parent;
}
return false;
@ -66,10 +66,10 @@ struct Quantifier final : TypeVarOnceVisitor
{
seenMutableType = true;
if (FFlag::DebugLuauDeferredConstraintResolution ? !subsumes(scope, ftv.scope) : !level.subsumes(ftv.level))
if (FFlag::DebugLluDeferredConstraintResolution ? !subsumes(scope, ftv.scope) : !level.subsumes(ftv.level))
return false;
if (FFlag::DebugLuauDeferredConstraintResolution)
if (FFlag::DebugLluDeferredConstraintResolution)
*asMutable(ty) = GenericTypeVar{scope};
else
*asMutable(ty) = GenericTypeVar{level};
@ -81,13 +81,13 @@ struct Quantifier final : TypeVarOnceVisitor
bool visit(TypeId ty, const ConstrainedTypeVar&) override
{
if (FFlag::LuauQuantifyConstrained)
if (FFlag::LluQuantifyConstrained)
{
ConstrainedTypeVar* ctv = getMutable<ConstrainedTypeVar>(ty);
seenMutableType = true;
if (FFlag::DebugLuauDeferredConstraintResolution ? !subsumes(scope, ctv->scope) : !level.subsumes(ctv->level))
if (FFlag::DebugLluDeferredConstraintResolution ? !subsumes(scope, ctv->scope) : !level.subsumes(ctv->level))
return false;
std::vector<TypeId> opts = std::move(ctv->parts);
@ -109,7 +109,7 @@ struct Quantifier final : TypeVarOnceVisitor
bool visit(TypeId ty, const TableTypeVar&) override
{
LUAU_ASSERT(getMutable<TableTypeVar>(ty));
lluz_ASSERT(getMutable<TableTypeVar>(ty));
TableTypeVar& ttv = *getMutable<TableTypeVar>(ty);
if (ttv.state == TableState::Generic)
@ -118,13 +118,13 @@ struct Quantifier final : TypeVarOnceVisitor
if (ttv.state == TableState::Free)
seenMutableType = true;
if (!FFlag::LuauQuantifyConstrained)
if (!FFlag::LluQuantifyConstrained)
{
if (ttv.state == TableState::Sealed || ttv.state == TableState::Generic)
return false;
}
if (FFlag::DebugLuauDeferredConstraintResolution ? !subsumes(scope, ttv.scope) : !level.subsumes(ttv.level))
if (FFlag::DebugLluDeferredConstraintResolution ? !subsumes(scope, ttv.scope) : !level.subsumes(ttv.level))
{
if (ttv.state == TableState::Unsealed)
seenMutableType = true;
@ -148,7 +148,7 @@ struct Quantifier final : TypeVarOnceVisitor
{
seenMutableType = true;
if (FFlag::DebugLuauDeferredConstraintResolution ? !subsumes(scope, ftp.scope) : !level.subsumes(ftp.level))
if (FFlag::DebugLluDeferredConstraintResolution ? !subsumes(scope, ftp.scope) : !level.subsumes(ftp.level))
return false;
*asMutable(tp) = GenericTypePack{level};
@ -159,7 +159,7 @@ struct Quantifier final : TypeVarOnceVisitor
void quantify(TypeId ty, TypeLevel level)
{
if (FFlag::DebugLuauSharedSelf)
if (FFlag::DebugLluSharedSelf)
{
ty = follow(ty);
@ -177,7 +177,7 @@ void quantify(TypeId ty, TypeLevel level)
if (!ftv || !ftv->hasSelf)
continue;
if (Luau::first(ftv->argTypes) == ttv->selfTy)
if (lluz::first(ftv->argTypes) == ttv->selfTy)
{
ftv->generics.insert(ftv->generics.end(), selfQ.generics.begin(), selfQ.generics.end());
ftv->genericPacks.insert(ftv->genericPacks.end(), selfQ.genericPacks.begin(), selfQ.genericPacks.end());
@ -202,8 +202,8 @@ void quantify(TypeId ty, TypeLevel level)
q.traverse(ty);
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
LUAU_ASSERT(ftv);
if (FFlag::LuauAlwaysQuantify)
lluz_ASSERT(ftv);
if (FFlag::LluAlwaysQuantify)
{
ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
@ -216,14 +216,14 @@ void quantify(TypeId ty, TypeLevel level)
}
}
void quantify(TypeId ty, Scope* scope)
void quantify(TypeId ty, Scope2* scope)
{
Quantifier q{scope};
q.traverse(ty);
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
LUAU_ASSERT(ftv);
if (FFlag::LuauAlwaysQuantify)
lluz_ASSERT(ftv);
if (FFlag::LluAlwaysQuantify)
{
ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
@ -240,11 +240,11 @@ void quantify(TypeId ty, Scope* scope)
struct PureQuantifier : Substitution
{
Scope* scope;
Scope2* scope;
std::vector<TypeId> insertedGenerics;
std::vector<TypePackId> insertedGenericPacks;
PureQuantifier(TypeArena* arena, Scope* scope)
PureQuantifier(TypeArena* arena, Scope2* scope)
: Substitution(TxnLog::empty(), arena)
, scope(scope)
{
@ -252,7 +252,7 @@ struct PureQuantifier : Substitution
bool isDirty(TypeId ty) override
{
LUAU_ASSERT(ty == follow(ty));
lluz_ASSERT(ty == follow(ty));
if (auto ftv = get<FreeTypeVar>(ty))
{
@ -288,7 +288,7 @@ struct PureQuantifier : Substitution
{
TypeId result = arena->addType(TableTypeVar{});
TableTypeVar* resultTable = getMutable<TableTypeVar>(result);
LUAU_ASSERT(resultTable);
lluz_ASSERT(resultTable);
*resultTable = *ttv;
resultTable->scope = nullptr;
@ -322,14 +322,14 @@ struct PureQuantifier : Substitution
}
};
TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope)
TypeId quantify(TypeArena* arena, TypeId ty, Scope2* scope)
{
PureQuantifier quantifier{arena, scope};
std::optional<TypeId> result = quantifier.substitute(ty);
LUAU_ASSERT(result);
lluz_ASSERT(result);
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(*result);
LUAU_ASSERT(ftv);
lluz_ASSERT(ftv);
ftv->generics.insert(ftv->generics.end(), quantifier.insertedGenerics.begin(), quantifier.insertedGenerics.end());
ftv->genericPacks.insert(ftv->genericPacks.end(), quantifier.insertedGenericPacks.begin(), quantifier.insertedGenericPacks.end());
ftv->hasNoGenerics = ftv->generics.empty() && ftv->genericPacks.empty();
@ -337,4 +337,4 @@ TypeId quantify(TypeArena* arena, TypeId ty, Scope* scope)
return *result;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,10 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/RequireTracer.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/RequireTracer.h"
#include "Luau/Ast.h"
#include "Luau/Module.h"
#include "lluz/Ast.h"
#include "lluz/Module.h"
namespace Luau
namespace lluz
{
struct RequireTracer : AstVisitor
@ -27,7 +27,7 @@ struct RequireTracer : AstVisitor
{
AstExprGlobal* global = expr->func->as<AstExprGlobal>();
if (global && global->name == "require" && expr->args.size >= 1)
if (global && global->name == XorStr("require") && expr->args.size >= 1)
requireCalls.push_back(expr);
return true;
@ -163,4 +163,4 @@ RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root,
return result;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,8 +1,8 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Scope.h"
#include "lluz/Scope.h"
namespace Luau
namespace lluz
{
Scope::Scope(TypePackId returnType)
@ -21,6 +21,22 @@ Scope::Scope(const ScopePtr& parent, int subLevel)
level.subLevel = subLevel;
}
std::optional<TypeId> Scope::lookup(const Symbol& name)
{
Scope* scope = this;
while (scope)
{
auto it = scope->bindings.find(name);
if (it != scope->bindings.end())
return it->second.typeId;
scope = scope->parent.get();
}
return std::nullopt;
}
std::optional<TypeFun> Scope::lookupType(const Name& name)
{
const Scope* scope = this;
@ -105,51 +121,51 @@ std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bo
return std::nullopt;
}
std::optional<TypeId> Scope::lookup(Symbol sym)
std::optional<TypeId> Scope2::lookup(Symbol sym)
{
Scope* s = this;
Scope2* s = this;
while (true)
{
auto it = s->bindings.find(sym);
if (it != s->bindings.end())
return it->second.typeId;
return it->second;
if (s->parent)
s = s->parent.get();
s = s->parent;
else
return std::nullopt;
}
}
std::optional<TypeId> Scope::lookupTypeBinding(const Name& name)
std::optional<TypeId> Scope2::lookupTypeBinding(const Name& name)
{
Scope* s = this;
Scope2* s = this;
while (s)
{
auto it = s->typeBindings.find(name);
if (it != s->typeBindings.end())
return it->second;
s = s->parent.get();
s = s->parent;
}
return std::nullopt;
}
std::optional<TypePackId> Scope::lookupTypePackBinding(const Name& name)
std::optional<TypePackId> Scope2::lookupTypePackBinding(const Name& name)
{
Scope* s = this;
Scope2* s = this;
while (s)
{
auto it = s->typePackBindings.find(name);
if (it != s->typePackBindings.end())
return it->second;
s = s->parent.get();
s = s->parent;
}
return std::nullopt;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,24 +1,22 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Substitution.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Substitution.h"
#include "Luau/Common.h"
#include "Luau/Clone.h"
#include "Luau/TxnLog.h"
#include "lluz/Common.h"
#include "lluz/Clone.h"
#include "lluz/TxnLog.h"
#include <algorithm>
#include <stdexcept>
LUAU_FASTFLAGVARIABLE(LuauAnyificationMustClone, false)
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
LUAU_FASTFLAG(LuauUnknownAndNeverType)
lluz_FASTFLAG(LluLowerBoundsCalculation)
lluz_FASTINTVARIABLE(LluTarjanChildLimit, 10000)
namespace Luau
namespace lluz
{
void Tarjan::visitChildren(TypeId ty, int index)
{
LUAU_ASSERT(ty == log->follow(ty));
lluz_ASSERT(ty == log->follow(ty));
if (ignoreChildren(ty))
return;
@ -33,7 +31,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
}
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
{
LUAU_ASSERT(!ttv->boundTo);
lluz_ASSERT(!ttv->boundTo);
for (const auto& [name, prop] : ttv->props)
visitChild(prop.type);
if (ttv->indexer)
@ -72,7 +70,7 @@ void Tarjan::visitChildren(TypeId ty, int index)
void Tarjan::visitChildren(TypePackId tp, int index)
{
LUAU_ASSERT(tp == log->follow(tp));
lluz_ASSERT(tp == log->follow(tp));
if (ignoreChildren(tp))
return;
@ -156,7 +154,7 @@ TarjanResult Tarjan::loop()
if (currEdge == -1)
{
++childCount;
if (childLimit > 0 && (FFlag::LuauUnknownAndNeverType ? childLimit <= childCount : childLimit < childCount))
if (childLimit > 0 && childLimit < childCount)
return TarjanResult::TooManyChildren;
stack.push_back(index);
@ -186,7 +184,7 @@ TarjanResult Tarjan::loop()
else if (auto tp = edgesTp[currEdge])
std::tie(childIndex, fresh) = indexify(tp);
else
LUAU_ASSERT(false);
lluz_ASSERT(false);
if (fresh)
{
@ -245,7 +243,7 @@ TarjanResult Tarjan::visitRoot(TypeId ty)
{
childCount = 0;
if (childLimit == 0)
childLimit = FInt::LuauTarjanChildLimit;
childLimit = FInt::LluTarjanChildLimit;
ty = log->follow(ty);
@ -258,7 +256,7 @@ TarjanResult Tarjan::visitRoot(TypePackId tp)
{
childCount = 0;
if (childLimit == 0)
childLimit = FInt::LuauTarjanChildLimit;
childLimit = FInt::LluTarjanChildLimit;
tp = log->follow(tp);
@ -433,17 +431,14 @@ TypePackId Substitution::replace(TypePackId tp)
void Substitution::replaceChildren(TypeId ty)
{
if (BoundTypeVar* btv = log->getMutable<BoundTypeVar>(ty); FFlag::LuauLowerBoundsCalculation && btv)
if (BoundTypeVar* btv = log->getMutable<BoundTypeVar>(ty); FFlag::LluLowerBoundsCalculation && btv)
btv->boundTo = replace(btv->boundTo);
LUAU_ASSERT(ty == log->follow(ty));
lluz_ASSERT(ty == log->follow(ty));
if (ignoreChildren(ty))
return;
if (FFlag::LuauAnyificationMustClone && ty->owningArena != arena)
return;
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
{
ftv->argTypes = replace(ftv->argTypes);
@ -451,7 +446,7 @@ void Substitution::replaceChildren(TypeId ty)
}
else if (TableTypeVar* ttv = getMutable<TableTypeVar>(ty))
{
LUAU_ASSERT(!ttv->boundTo);
lluz_ASSERT(!ttv->boundTo);
for (auto& [name, prop] : ttv->props)
prop.type = replace(prop.type);
if (ttv->indexer)
@ -490,14 +485,11 @@ void Substitution::replaceChildren(TypeId ty)
void Substitution::replaceChildren(TypePackId tp)
{
LUAU_ASSERT(tp == log->follow(tp));
lluz_ASSERT(tp == log->follow(tp));
if (ignoreChildren(tp))
return;
if (FFlag::LuauAnyificationMustClone && tp->owningArena != arena)
return;
if (TypePack* tpp = getMutable<TypePack>(tp))
{
for (TypeId& tv : tpp->head)
@ -511,4 +503,4 @@ void Substitution::replaceChildren(TypePackId tp)
}
}
} // namespace Luau
} // namespace lluz

View file

@ -1,9 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Symbol.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Symbol.h"
#include "Luau/Common.h"
#include "lluz/Common.h"
namespace Luau
namespace lluz
{
std::string toString(const Symbol& name)
@ -11,8 +11,8 @@ std::string toString(const Symbol& name)
if (name.local)
return name.local->name.value;
LUAU_ASSERT(name.global.value);
lluz_ASSERT(name.global.value);
return name.global.value;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,15 +1,15 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ToDot.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/ToDot.h"
#include "Luau/ToString.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
#include "Luau/StringUtils.h"
#include "lluz/ToString.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
#include "lluz/StringUtils.h"
#include <unordered_map>
#include <unordered_set>
namespace Luau
namespace lluz
{
namespace
@ -106,12 +106,12 @@ void StateDot::startNode(int index)
void StateDot::finishNode()
{
formatAppend(result, "];\n");
formatAppend(result, XorStr("];\n"));
}
void StateDot::startNodeLabel()
{
formatAppend(result, "label=\"");
formatAppend(result, XorStr("label=\""));
}
void StateDot::finishNodeLabel(TypeId ty)
@ -153,8 +153,8 @@ void StateDot::visitChildren(TypeId ty, int index)
finishNodeLabel(ty);
finishNode();
visitChild(ftv->argTypes, index, "arg");
visitChild(ftv->retTypes, index, "ret");
visitChild(ftv->argTypes, index, XorStr("arg"));
visitChild(ftv->retTypes, index, XorStr("ret"));
}
else if (const TableTypeVar* ttv = get<TableTypeVar>(ty))
{
@ -168,20 +168,20 @@ void StateDot::visitChildren(TypeId ty, int index)
finishNode();
if (ttv->boundTo)
return visitChild(*ttv->boundTo, index, "boundTo");
return visitChild(*ttv->boundTo, index, XorStr("boundTo"));
for (const auto& [name, prop] : ttv->props)
visitChild(prop.type, index, name.c_str());
if (ttv->indexer)
{
visitChild(ttv->indexer->indexType, index, "[index]");
visitChild(ttv->indexer->indexResultType, index, "[value]");
visitChild(ttv->indexer->indexType, index, XorStr("[index]"));
visitChild(ttv->indexer->indexResultType, index, XorStr("[value]"));
}
for (TypeId itp : ttv->instantiatedTypeParams)
visitChild(itp, index, "typeParam");
visitChild(itp, index, XorStr("typeParam"));
for (TypePackId itp : ttv->instantiatedTypePackParams)
visitChild(itp, index, "typePackParam");
visitChild(itp, index, XorStr("typePackParam"));
}
else if (const MetatableTypeVar* mtv = get<MetatableTypeVar>(ty))
{
@ -189,8 +189,8 @@ void StateDot::visitChildren(TypeId ty, int index)
finishNodeLabel(ty);
finishNode();
visitChild(mtv->table, index, "table");
visitChild(mtv->metatable, index, "metatable");
visitChild(mtv->table, index, XorStr("table"));
visitChild(mtv->metatable, index, XorStr("metatable"));
}
else if (const UnionTypeVar* utv = get<UnionTypeVar>(ty))
{
@ -262,10 +262,10 @@ void StateDot::visitChildren(TypeId ty, int index)
visitChild(prop.type, index, name.c_str());
if (ctv->parent)
visitChild(*ctv->parent, index, "[parent]");
visitChild(*ctv->parent, index, XorStr("[parent]"));
if (ctv->metatable)
visitChild(*ctv->metatable, index, "[metatable]");
visitChild(*ctv->metatable, index, XorStr("[metatable]"));
}
else if (const SingletonTypeVar* stv = get<SingletonTypeVar>(ty))
{
@ -283,7 +283,7 @@ void StateDot::visitChildren(TypeId ty, int index)
res += bs->value ? "true" : "false";
}
else
LUAU_ASSERT(!"unknown singleton type");
lluz_ASSERT(!XorStr("unknown singleton type"));
formatAppend(result, "SingletonTypeVar %s", res.c_str());
finishNodeLabel(ty);
@ -291,7 +291,7 @@ void StateDot::visitChildren(TypeId ty, int index)
}
else
{
LUAU_ASSERT(!"unknown type kind");
lluz_ASSERT(!XorStr("unknown type kind"));
finishNodeLabel(ty);
finishNode();
}
@ -323,7 +323,7 @@ void StateDot::visitChildren(TypePackId tp, int index)
for (TypeId tv : tpp->head)
visitChild(tv, index);
if (tpp->tail)
visitChild(*tpp->tail, index, "tail");
visitChild(*tpp->tail, index, XorStr("tail"));
}
else if (const VariadicTypePack* vtp = get<VariadicTypePack>(tp))
{
@ -356,7 +356,7 @@ void StateDot::visitChildren(TypePackId tp, int index)
}
else
{
LUAU_ASSERT(!"unknown type pack kind");
lluz_ASSERT(!XorStr("unknown type pack kind"));
finishNodeLabel(tp);
finishNode();
}
@ -406,4 +406,4 @@ void dumpDot(TypePackId tp)
printf("%s\n", toDot(tp).c_str());
}
} // namespace Luau
} // namespace lluz

View file

@ -1,27 +1,26 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ToString.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/ToString.h"
#include "Luau/Scope.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
#include "Luau/VisitTypeVar.h"
#include "lluz/Scope.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
#include "lluz/VisitTypeVar.h"
#include <algorithm>
#include <stdexcept>
LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAGVARIABLE(LuauSpecialTypesAsterisked, false)
lluz_FASTFLAG(LluLowerBoundsCalculation)
/*
* Prefix generic typenames with gen-
* Additionally, free types will be prefixed with free- and suffixed with their level. eg free-a-4
* Fair warning: Setting this will break a lot of Luau unit tests.
* Fair warning: Setting this will break a lot of lluz unit tests.
*/
LUAU_FASTFLAGVARIABLE(DebugLuauVerboseTypeNames, false)
lluz_FASTFLAGVARIABLE(DebugLluVerboseTypeNames, false)
lluz_FASTFLAGVARIABLE(LluToStringTableBracesNewlines, false)
namespace Luau
namespace lluz
{
namespace
@ -97,7 +96,7 @@ void findCyclicTypes(std::set<TypeId>& cycles, std::set<TypePackId>& cycleTPs, T
} // namespace
static std::pair<bool, std::optional<Luau::Name>> canUseTypeNameInScope(ScopePtr scope, const std::string& name)
static std::pair<bool, std::optional<lluz::Name>> canUseTypeNameInScope(ScopePtr scope, const std::string& name)
{
for (ScopePtr curr = scope; curr; curr = curr->parent)
{
@ -215,7 +214,7 @@ struct StringifierState
void emit(TypeLevel level)
{
emit(std::to_string(level.level));
emit("-");
emit(XorStr("-"));
emit(std::to_string(level.subLevel));
}
@ -245,9 +244,9 @@ struct StringifierState
void newline()
{
if (!opts.useLineBreaks)
return emit(" ");
return emit(XorStr(" "));
emit("\n");
emit(XorStr("\n"));
emitIndentation();
}
@ -278,10 +277,7 @@ struct TypeVarStringifier
if (tv->ty.valueless_by_exception())
{
state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("* VALUELESS BY EXCEPTION *");
else
state.emit("< VALUELESS BY EXCEPTION >");
state.emit(XorStr("< VALUELESS BY EXCEPTION >"));
return;
}
@ -292,7 +288,7 @@ struct TypeVarStringifier
return;
}
Luau::visit(
lluz::visit(
[this, tv](auto&& t)
{
return (*this)(tv, t);
@ -309,14 +305,14 @@ struct TypeVarStringifier
return;
if (types.size() || typePacks.size())
state.emit("<");
state.emit(XorStr("<"));
bool first = true;
for (TypeId ty : types)
{
if (!first)
state.emit(", ");
state.emit(XorStr(", "));
first = false;
stringify(ty);
@ -330,35 +326,35 @@ struct TypeVarStringifier
continue;
if (!first)
state.emit(", ");
state.emit(XorStr(", "));
else
first = false;
bool wrap = !singleTp && get<TypePack>(follow(tp));
if (wrap)
state.emit("(");
state.emit(XorStr("("));
stringify(tp);
if (wrap)
state.emit(")");
state.emit(XorStr(")"));
}
if (types.size() || typePacks.size())
state.emit(">");
state.emit(XorStr(">"));
}
void operator()(TypeId ty, const Unifiable::Free& ftv)
{
state.result.invalid = true;
if (FFlag::DebugLuauVerboseTypeNames)
state.emit("free-");
if (FFlag::DebugLluVerboseTypeNames)
state.emit(XorStr("free-"));
state.emit(state.getName(ty));
if (FFlag::DebugLuauVerboseTypeNames)
if (FFlag::DebugLluVerboseTypeNames)
{
state.emit("-");
state.emit(XorStr("-"));
state.emit(ftv.level);
}
}
@ -384,10 +380,10 @@ struct TypeVarStringifier
{
state.result.invalid = true;
state.emit("[");
if (FFlag::DebugLuauVerboseTypeNames)
state.emit(XorStr("["));
if (FFlag::DebugLluVerboseTypeNames)
state.emit(ctv.level);
state.emit("[");
state.emit(XorStr("["));
bool first = true;
for (TypeId ty : ctv.parts)
@ -395,19 +391,19 @@ struct TypeVarStringifier
if (first)
first = false;
else
state.emit("|");
state.emit(XorStr("|"));
stringify(ty);
}
state.emit("]]");
state.emit(XorStr("]]"));
}
void operator()(TypeId, const BlockedTypeVar& btv)
{
state.emit("*blocked-");
state.emit(XorStr("*blocked-"));
state.emit(btv.index);
state.emit("*");
state.emit(XorStr("*"));
}
void operator()(TypeId, const PrimitiveTypeVar& ptv)
@ -415,40 +411,40 @@ struct TypeVarStringifier
switch (ptv.type)
{
case PrimitiveTypeVar::NilType:
state.emit("nil");
state.emit(XorStr("nil"));
return;
case PrimitiveTypeVar::Boolean:
state.emit("boolean");
state.emit(XorStr("boolean"));
return;
case PrimitiveTypeVar::Number:
state.emit("number");
state.emit(XorStr("number"));
return;
case PrimitiveTypeVar::String:
state.emit("string");
state.emit(XorStr("string"));
return;
case PrimitiveTypeVar::Thread:
state.emit("thread");
state.emit(XorStr("thread"));
return;
default:
LUAU_ASSERT(!"Unknown primitive type");
lluz_ASSERT(!XorStr("Unknown primitive type"));
throw std::runtime_error("Unknown primitive type " + std::to_string(ptv.type));
}
}
void operator()(TypeId, const SingletonTypeVar& stv)
{
if (const BooleanSingleton* bs = Luau::get<BooleanSingleton>(&stv))
state.emit(bs->value ? "true" : "false");
else if (const StringSingleton* ss = Luau::get<StringSingleton>(&stv))
if (const BooleanSingleton* bs = lluz::get<BooleanSingleton>(&stv))
state.emit(bs->value ? XorStr("true" : "false"));
else if (const StringSingleton* ss = lluz::get<StringSingleton>(&stv))
{
state.emit("\"");
state.emit(XorStr("\""));
state.emit(escape(ss->value));
state.emit("\"");
state.emit(XorStr("\""));
}
else
{
LUAU_ASSERT(!"Unknown singleton type");
throw std::runtime_error("Unknown singleton type");
lluz_ASSERT(!XorStr("Unknown singleton type"));
throw std::runtime_error(XorStr("Unknown singleton type"));
}
}
@ -457,47 +453,44 @@ struct TypeVarStringifier
if (state.hasSeen(&ftv))
{
state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
state.emit(XorStr("<CYCLE>"));
return;
}
// We should not be respecting opts.hideNamedFunctionTypeParameters here.
if (ftv.generics.size() > 0 || ftv.genericPacks.size() > 0)
{
state.emit("<");
state.emit(XorStr("<"));
bool comma = false;
for (auto it = ftv.generics.begin(); it != ftv.generics.end(); ++it)
{
if (comma)
state.emit(", ");
state.emit(XorStr(", "));
comma = true;
stringify(*it);
}
for (auto it = ftv.genericPacks.begin(); it != ftv.genericPacks.end(); ++it)
{
if (comma)
state.emit(", ");
state.emit(XorStr(", "));
comma = true;
stringify(*it);
}
state.emit(">");
state.emit(XorStr(">"));
}
state.emit("(");
state.emit(XorStr("("));
if (state.opts.functionTypeArguments)
stringify(ftv.argTypes, ftv.argNames);
else
stringify(ftv.argTypes);
state.emit(") -> ");
state.emit(XorStr(") -> "));
bool plural = true;
if (FFlag::LuauLowerBoundsCalculation)
if (FFlag::LluLowerBoundsCalculation)
{
auto retBegin = begin(ftv.retTypes);
auto retEnd = end(ftv.retTypes);
@ -518,12 +511,12 @@ struct TypeVarStringifier
}
if (plural)
state.emit("(");
state.emit(XorStr("("));
stringify(ftv.retTypes);
if (plural)
state.emit(")");
state.emit(XorStr(")"));
state.unsee(&ftv);
}
@ -548,7 +541,7 @@ struct TypeVarStringifier
if (moduleName)
{
state.emit(*moduleName);
state.emit(".");
state.emit(XorStr("."));
}
}
@ -568,10 +561,7 @@ struct TypeVarStringifier
if (state.hasSeen(&ttv))
{
state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
state.emit(XorStr("<CYCLE>"));
return;
}
@ -581,31 +571,63 @@ struct TypeVarStringifier
{
case TableState::Sealed:
state.result.invalid = true;
openbrace = "{|";
closedbrace = "|}";
if (FFlag::LluToStringTableBracesNewlines)
{
openbrace = "{|";
closedbrace = "|}";
}
else
{
openbrace = "{| ";
closedbrace = " |}";
}
break;
case TableState::Unsealed:
openbrace = "{";
closedbrace = "}";
if (FFlag::LluToStringTableBracesNewlines)
{
openbrace = "{";
closedbrace = "}";
}
else
{
openbrace = "{ ";
closedbrace = " }";
}
break;
case TableState::Free:
state.result.invalid = true;
openbrace = "{-";
closedbrace = "-}";
if (FFlag::LluToStringTableBracesNewlines)
{
openbrace = "{-";
closedbrace = "-}";
}
else
{
openbrace = "{- ";
closedbrace = " -}";
}
break;
case TableState::Generic:
state.result.invalid = true;
openbrace = "{+";
closedbrace = "+}";
if (FFlag::LluToStringTableBracesNewlines)
{
openbrace = "{+";
closedbrace = "+}";
}
else
{
openbrace = "{+ ";
closedbrace = " +}";
}
break;
}
// If this appears to be an array, we want to stringify it using the {T} syntax.
if (ttv.indexer && ttv.props.empty() && isNumber(ttv.indexer->indexType))
{
state.emit("{");
state.emit(XorStr("{"));
stringify(ttv.indexer->indexResultType);
state.emit("}");
state.emit(XorStr("}"));
return;
}
@ -615,10 +637,11 @@ struct TypeVarStringifier
bool comma = false;
if (ttv.indexer)
{
state.newline();
state.emit("[");
if (FFlag::LluToStringTableBracesNewlines)
state.newline();
state.emit(XorStr("["));
stringify(ttv.indexer->indexType);
state.emit("]: ");
state.emit(XorStr("]: "));
stringify(ttv.indexer->indexResultType);
comma = true;
}
@ -629,19 +652,21 @@ struct TypeVarStringifier
{
if (comma)
{
state.emit(",");
state.emit(XorStr(","));
state.newline();
}
else
else if (FFlag::LluToStringTableBracesNewlines)
{
state.newline();
}
size_t length = state.result.name.length() - oldLength;
if (state.opts.maxTableLength > 0 && (length - 2 * index) >= state.opts.maxTableLength)
{
state.emit("... ");
state.emit(XorStr("... "));
state.emit(std::to_string(ttv.props.size() - index));
state.emit(" more ...");
state.emit(XorStr(" more ..."));
break;
}
@ -649,21 +674,24 @@ struct TypeVarStringifier
state.emit(name);
else
{
state.emit("[\"");
state.emit(XorStr("[\""));
state.emit(escape(name));
state.emit("\"]");
state.emit(XorStr("\"]"));
}
state.emit(": ");
state.emit(XorStr(": "));
stringify(prop.type);
comma = true;
++index;
}
state.dedent();
if (comma)
state.newline();
else
state.emit(" ");
if (FFlag::LluToStringTableBracesNewlines)
{
if (comma)
state.newline();
else
state.emit(XorStr(" "));
}
state.emit(closedbrace);
state.unsee(&ttv);
@ -678,12 +706,12 @@ struct TypeVarStringifier
return;
}
state.emit("{ @metatable ");
state.emit(XorStr("{ @metatable "));
stringify(mtv.metatable);
state.emit(",");
state.emit(XorStr(","));
state.newline();
stringify(mtv.table);
state.emit(" }");
state.emit(XorStr(" }"));
}
void operator()(TypeId, const ClassTypeVar& ctv)
@ -693,7 +721,7 @@ struct TypeVarStringifier
void operator()(TypeId, const AnyTypeVar&)
{
state.emit("any");
state.emit(XorStr("any"));
}
void operator()(TypeId, const UnionTypeVar& uv)
@ -701,10 +729,7 @@ struct TypeVarStringifier
if (state.hasSeen(&uv))
{
state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
state.emit(XorStr("<CYCLE>"));
return;
}
@ -726,12 +751,12 @@ struct TypeVarStringifier
bool needParens = !state.cycleNames.count(el) && (get<IntersectionTypeVar>(el) || get<FunctionTypeVar>(el));
if (needParens)
state.emit("(");
state.emit(XorStr("("));
stringify(el);
if (needParens)
state.emit(")");
state.emit(XorStr(")"));
results.push_back(std::move(state.result.name));
state.result.name = std::move(saved);
@ -742,7 +767,7 @@ struct TypeVarStringifier
std::sort(results.begin(), results.end());
if (optional && results.size() > 1)
state.emit("(");
state.emit(XorStr("("));
bool first = true;
for (std::string& ss : results)
@ -750,7 +775,7 @@ struct TypeVarStringifier
if (!first)
{
state.newline();
state.emit("| ");
state.emit(XorStr("| "));
}
state.emit(ss);
first = false;
@ -771,10 +796,7 @@ struct TypeVarStringifier
if (state.hasSeen(&uv))
{
state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLE*");
else
state.emit("<CYCLE>");
state.emit(XorStr("<CYCLE>"));
return;
}
@ -788,12 +810,12 @@ struct TypeVarStringifier
bool needParens = !state.cycleNames.count(el) && (get<UnionTypeVar>(el) || get<FunctionTypeVar>(el));
if (needParens)
state.emit("(");
state.emit(XorStr("("));
stringify(el);
if (needParens)
state.emit(")");
state.emit(XorStr(")"));
results.push_back(std::move(state.result.name));
state.result.name = std::move(saved);
@ -809,7 +831,7 @@ struct TypeVarStringifier
if (!first)
{
state.newline();
state.emit("& ");
state.emit(XorStr("& "));
}
state.emit(ss);
first = false;
@ -819,28 +841,16 @@ struct TypeVarStringifier
void operator()(TypeId, const ErrorTypeVar& tv)
{
state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
else
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
state.emit(XorStr("*unknown*"));
}
void operator()(TypeId, const LazyTypeVar& ltv)
{
state.result.invalid = true;
state.emit("lazy?");
state.emit(XorStr("lazy?"));
}
void operator()(TypeId, const UnknownTypeVar& ttv)
{
state.emit("unknown");
}
void operator()(TypeId, const NeverTypeVar& ttv)
{
state.emit("never");
}
};
}; // namespace
struct TypePackStringifier
{
@ -876,10 +886,7 @@ struct TypePackStringifier
if (tp->ty.valueless_by_exception())
{
state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("* VALUELESS TP BY EXCEPTION *");
else
state.emit("< VALUELESS TP BY EXCEPTION >");
state.emit(XorStr("< VALUELESS TP BY EXCEPTION >"));
return;
}
@ -890,7 +897,7 @@ struct TypePackStringifier
return;
}
Luau::visit(
lluz::visit(
[this, tp](auto&& t)
{
return (*this)(tp, t);
@ -903,10 +910,7 @@ struct TypePackStringifier
if (state.hasSeen(&tp))
{
state.result.cycle = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*CYCLETP*");
else
state.emit("<CYCLETP>");
state.emit(XorStr("<CYCLETP>"));
return;
}
@ -917,13 +921,13 @@ struct TypePackStringifier
if (first)
first = false;
else
state.emit(", ");
state.emit(XorStr(", "));
// Do not respect opts.namedFunctionOverrideArgNames here
if (elemIndex < elemNames.size() && elemNames[elemIndex])
{
state.emit(elemNames[elemIndex]->name);
state.emit(": ");
state.emit(XorStr(": "));
}
elemIndex++;
@ -934,12 +938,12 @@ struct TypePackStringifier
if (tp.tail && !isEmpty(*tp.tail))
{
TypePackId tail = follow(*tp.tail);
if (auto vtp = get<VariadicTypePack>(tail); !vtp || (!FFlag::DebugLuauVerboseTypeNames && !vtp->hidden))
if (auto vtp = get<VariadicTypePack>(tail); !vtp || (!FFlag::DebugLluVerboseTypeNames && !vtp->hidden))
{
if (first)
first = false;
else
state.emit(", ");
state.emit(XorStr(", "));
stringify(tail);
}
@ -951,29 +955,21 @@ struct TypePackStringifier
void operator()(TypePackId, const Unifiable::Error& error)
{
state.result.error = true;
if (FFlag::LuauSpecialTypesAsterisked)
state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*");
else
state.emit(FFlag::LuauUnknownAndNeverType ? "<error-type>" : "*unknown*");
state.emit(XorStr("*unknown*"));
}
void operator()(TypePackId, const VariadicTypePack& pack)
{
state.emit("...");
if (FFlag::DebugLuauVerboseTypeNames && pack.hidden)
{
if (FFlag::LuauSpecialTypesAsterisked)
state.emit("*hidden*");
else
state.emit("<hidden>");
}
state.emit(XorStr("..."));
if (FFlag::DebugLluVerboseTypeNames && pack.hidden)
state.emit(XorStr("<hidden>"));
stringify(pack.ty);
}
void operator()(TypePackId tp, const GenericTypePack& pack)
{
if (FFlag::DebugLuauVerboseTypeNames)
state.emit("gen-");
if (FFlag::DebugLluVerboseTypeNames)
state.emit(XorStr("gen-"));
if (pack.explicitName)
{
state.usedNames.insert(pack.name);
@ -984,23 +980,23 @@ struct TypePackStringifier
{
state.emit(state.getName(tp));
}
state.emit("...");
state.emit(XorStr("..."));
}
void operator()(TypePackId tp, const FreeTypePack& pack)
{
state.result.invalid = true;
if (FFlag::DebugLuauVerboseTypeNames)
state.emit("free-");
if (FFlag::DebugLluVerboseTypeNames)
state.emit(XorStr("free-"));
state.emit(state.getName(tp));
if (FFlag::DebugLuauVerboseTypeNames)
if (FFlag::DebugLluVerboseTypeNames)
{
state.emit("-");
state.emit(XorStr("-"));
state.emit(pack.level);
}
state.emit("...");
state.emit(XorStr("..."));
}
void operator()(TypePackId, const BoundTypePack& btv)
@ -1097,7 +1093,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
result.invalid = true;
if (moduleName)
result.name = format("%s.", moduleName->c_str());
result.name = format(XorStr("%s."), moduleName->c_str());
}
result.name += ttv->name ? *ttv->name : *ttv->syntheticName;
@ -1128,7 +1124,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
if (!state.cycleNames.empty())
{
result.cycle = true;
state.emit(" where ");
state.emit(XorStr(" where "));
}
state.exhaustive = true;
@ -1144,11 +1140,11 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
for (const auto& [cycleTy, name] : sortedCycleNames)
{
if (semi)
state.emit(" ; ");
state.emit(XorStr(" ; "));
state.emit(name);
state.emit(" = ");
Luau::visit(
state.emit(XorStr(" = "));
lluz::visit(
[&tvs, cycleTy = cycleTy](auto&& t)
{
return tvs(cycleTy, t);
@ -1161,11 +1157,7 @@ ToStringResult toStringDetailed(TypeId ty, const ToStringOptions& opts)
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
{
result.truncated = true;
if (FFlag::LuauSpecialTypesAsterisked)
result.name += "... *TRUNCATED*";
else
result.name += "... <TRUNCATED>";
result.name += "... <TRUNCATED>";
}
return result;
@ -1205,7 +1197,7 @@ ToStringResult toStringDetailed(TypePackId tp, const ToStringOptions& opts)
if (!cycles.empty())
{
result.cycle = true;
state.emit(" where ");
state.emit(XorStr(" where "));
}
state.exhaustive = true;
@ -1221,11 +1213,11 @@ ToStringResult toStringDetailed(TypePackId tp, const ToStringOptions& opts)
for (const auto& [cycleTy, name] : sortedCycleNames)
{
if (semi)
state.emit(" ; ");
state.emit(XorStr(" ; "));
state.emit(name);
state.emit(" = ");
Luau::visit(
state.emit(XorStr(" = "));
lluz::visit(
[&tvs, cycleTy = cycleTy](auto t)
{
return tvs(cycleTy, t);
@ -1236,12 +1228,7 @@ ToStringResult toStringDetailed(TypePackId tp, const ToStringOptions& opts)
}
if (opts.maxTypeLength > 0 && result.name.length() > opts.maxTypeLength)
{
if (FFlag::LuauSpecialTypesAsterisked)
result.name += "... *TRUNCATED*";
else
result.name += "... <TRUNCATED>";
}
result.name += "... <TRUNCATED>";
return result;
}
@ -1277,7 +1264,7 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
if (!opts.hideNamedFunctionTypeParameters)
tvs.stringify(ftv.generics, ftv.genericPacks);
state.emit("(");
state.emit(XorStr("("));
auto argPackIter = begin(ftv.argTypes);
@ -1294,21 +1281,21 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
}
if (!first)
state.emit(", ");
state.emit(XorStr(", "));
first = false;
// We don't respect opts.functionTypeArguments
if (idx < opts.namedFunctionOverrideArgNames.size())
{
state.emit(opts.namedFunctionOverrideArgNames[idx] + ": ");
state.emit(opts.namedFunctionOverrideArgNames[idx] + XorStr(": "));
}
else if (idx < ftv.argNames.size() && ftv.argNames[idx])
{
state.emit(ftv.argNames[idx]->name + ": ");
state.emit(ftv.argNames[idx]->name + XorStr(": "));
}
else
{
state.emit("_: ");
state.emit(XorStr("_: "));
}
tvs.stringify(*argPackIter);
@ -1321,9 +1308,9 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
if (auto vtp = get<VariadicTypePack>(*argPackIter.tail()); !vtp || !vtp->hidden)
{
if (!first)
state.emit(", ");
state.emit(XorStr(", "));
state.emit("...: ");
state.emit(XorStr("...: "));
if (vtp)
tvs.stringify(vtp->ty);
@ -1332,19 +1319,19 @@ std::string toStringNamedFunction(const std::string& funcName, const FunctionTyp
}
}
state.emit("): ");
state.emit(XorStr("): "));
size_t retSize = size(ftv.retTypes);
bool hasTail = !finite(ftv.retTypes);
bool wrap = get<TypePack>(follow(ftv.retTypes)) && (hasTail ? retSize != 0 : retSize != 1);
if (wrap)
state.emit("(");
state.emit(XorStr("("));
tvs.stringify(ftv.retTypes);
if (wrap)
state.emit(")");
state.emit(XorStr(")"));
return result.name;
}
@ -1457,10 +1444,10 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts)
{
ToStringResult namedStr = toStringDetailed(c.namedType, opts);
opts.nameMap = std::move(namedStr.nameMap);
return "@name(" + namedStr.name + ") = " + c.name;
return XorStr("@name(") + namedStr.name + ") = " + c.name;
}
else
static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
static_assert(always_false_v<T>, XorStr("Non-exhaustive constraint switch"));
};
return visit(go, constraint.c);
@ -1476,4 +1463,4 @@ std::string dump(const Constraint& c)
return s;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,5 +1,5 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TopoSortStatements.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TopoSortStatements.h"
/* Decide the order in which we typecheck Lua statements in a block.
*
@ -26,10 +26,10 @@
* 3. Cyclic dependencies can be resolved by picking an arbitrary statement to check first.
*/
#include "Luau/Ast.h"
#include "Luau/DenseHash.h"
#include "Luau/Common.h"
#include "Luau/StringUtils.h"
#include "lluz/Ast.h"
#include "lluz/DenseHash.h"
#include "lluz/Common.h"
#include "lluz/StringUtils.h"
#include <algorithm>
#include <deque>
@ -40,7 +40,7 @@
#include <stdexcept>
#include <optional>
namespace Luau
namespace lluz
{
// For some reason, natvis interacts really poorly with anonymous data types
@ -127,7 +127,7 @@ std::optional<Identifier> mkName(const AstExprIndexName& expr)
Identifier mkName(const AstExprError& expr)
{
return {format("error#%d", expr.messageIndex), nullptr};
return {format(XorStr("error#%d"), expr.messageIndex), nullptr};
}
std::optional<Identifier> mkName(const AstExpr& expr)
@ -147,9 +147,9 @@ std::optional<Identifier> mkName(const AstExpr& expr)
Identifier mkName(const AstStatFunction& function)
{
auto name = mkName(*function.name);
LUAU_ASSERT(bool(name));
lluz_ASSERT(bool(name));
if (!name)
throw std::runtime_error("Internal error: Function declaration has a bad name");
throw std::runtime_error(XorStr("Internal error: Function declaration has a bad name"));
return *name;
}
@ -255,7 +255,7 @@ struct ArcCollector : public AstVisitor
{
auto name = mkName(*node->name);
if (!name)
throw std::runtime_error("Internal error: AstStatFunction has a bad name");
throw std::runtime_error(XorStr("Internal error: AstStatFunction has a bad name"));
add(*name);
return true;
@ -382,14 +382,14 @@ void prune(Node* next)
for (const auto& node : next->provides)
{
auto it = node->depends.find(next);
LUAU_ASSERT(it != node->depends.end());
lluz_ASSERT(it != node->depends.end());
node->depends.erase(it);
}
for (const auto& node : next->depends)
{
auto it = node->provides.find(next);
LUAU_ASSERT(it != node->provides.end());
lluz_ASSERT(it != node->provides.end());
node->provides.erase(it);
}
}
@ -439,7 +439,7 @@ void drain(NodeList& Q, std::vector<AstStat*>& result, Node* target)
if (isBlockTerminator(*iter->get()->element))
continue;
LUAU_ASSERT(allArcs.end() != allArcs.find(iter->get()));
lluz_ASSERT(allArcs.end() != allArcs.find(iter->get()));
const Arcs& arcs = allArcs[iter->get()];
if (arcs.depends.empty())
@ -463,7 +463,7 @@ void drain(NodeList& Q, std::vector<AstStat*>& result, Node* target)
if (allArcs.end() != it)
{
auto i2 = it->second.depends.find(nextNode.get());
LUAU_ASSERT(i2 != it->second.depends.end());
lluz_ASSERT(i2 != it->second.depends.end());
it->second.depends.erase(i2);
}
}
@ -474,7 +474,7 @@ void drain(NodeList& Q, std::vector<AstStat*>& result, Node* target)
if (allArcs.end() != it)
{
auto i2 = it->second.provides.find(nextNode.get());
LUAU_ASSERT(i2 != it->second.provides.end());
lluz_ASSERT(i2 != it->second.provides.end());
it->second.provides.erase(i2);
}
}
@ -576,4 +576,4 @@ void toposort(std::vector<AstStat*>& stats)
std::swap(stats, result);
}
} // namespace Luau
} // namespace lluz

View file

@ -1,9 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Transpiler.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Transpiler.h"
#include "Luau/Parser.h"
#include "Luau/StringUtils.h"
#include "Luau/Common.h"
#include "lluz/Parser.h"
#include "lluz/StringUtils.h"
#include "lluz/Common.h"
#include <algorithm>
#include <memory>
@ -32,7 +32,7 @@ const std::vector<std::string> keywords = {"and", "break", "do", "else", "elseif
} // namespace
namespace Luau
namespace lluz
{
struct Writer
@ -175,7 +175,7 @@ public:
if (first)
first = !first;
else
writer.symbol(",");
writer.symbol(XorStr(","));
}
private:
@ -200,7 +200,7 @@ struct Printer
writer.identifier(local.name.value);
if (writeTypes && local.annotation)
{
writer.symbol(":");
writer.symbol(XorStr(":"));
visualizeTypeAnnotation(*local.annotation);
}
}
@ -211,23 +211,23 @@ struct Printer
if (const AstTypePackVariadic* variadicTp = annotation.as<AstTypePackVariadic>())
{
if (!forVarArg)
writer.symbol("...");
writer.symbol(XorStr("..."));
visualizeTypeAnnotation(*variadicTp->variadicType);
}
else if (const AstTypePackGeneric* genericTp = annotation.as<AstTypePackGeneric>())
{
writer.symbol(genericTp->genericName.value);
writer.symbol("...");
writer.symbol(XorStr("..."));
}
else if (const AstTypePackExplicit* explicitTp = annotation.as<AstTypePackExplicit>())
{
LUAU_ASSERT(!forVarArg);
lluz_ASSERT(!forVarArg);
visualizeTypeList(explicitTp->typeList, true);
}
else
{
LUAU_ASSERT(!"Unknown TypePackAnnotation kind");
lluz_ASSERT(!XorStr("Unknown TypePackAnnotation kind"));
}
}
@ -236,13 +236,13 @@ struct Printer
size_t typeCount = list.types.size + (list.tailType != nullptr ? 1 : 0);
if (typeCount == 0)
{
writer.symbol("(");
writer.symbol(")");
writer.symbol(XorStr("("));
writer.symbol(XorStr(")"));
}
else if (typeCount == 1)
{
if (unconditionallyParenthesize)
writer.symbol("(");
writer.symbol(XorStr("("));
// Only variadic tail
if (list.types.size == 0)
@ -255,11 +255,11 @@ struct Printer
}
if (unconditionallyParenthesize)
writer.symbol(")");
writer.symbol(XorStr(")"));
}
else
{
writer.symbol("(");
writer.symbol(XorStr("("));
bool first = true;
for (const auto& el : list.types)
@ -267,18 +267,18 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualizeTypeAnnotation(*el);
}
if (list.tailType)
{
writer.symbol(",");
writer.symbol(XorStr(","));
visualizeTypePackAnnotation(*list.tailType, false);
}
writer.symbol(")");
writer.symbol(XorStr(")"));
}
}
@ -296,32 +296,32 @@ struct Printer
if (const auto& a = expr.as<AstExprGroup>())
{
writer.symbol("(");
writer.symbol(XorStr("("));
visualize(*a->expr);
writer.symbol(")");
writer.symbol(XorStr(")"));
}
else if (expr.is<AstExprConstantNil>())
{
writer.keyword("nil");
writer.keyword(XorStr("nil"));
}
else if (const auto& a = expr.as<AstExprConstantBool>())
{
if (a->value)
writer.keyword("true");
writer.keyword(XorStr("true"));
else
writer.keyword("false");
writer.keyword(XorStr("false"));
}
else if (const auto& a = expr.as<AstExprConstantNumber>())
{
if (isinf(a->value))
{
if (a->value > 0)
writer.literal("1e500");
writer.literal(XorStr("1e500"));
else
writer.literal("-1e500");
writer.literal(XorStr("-1e500"));
}
else if (isnan(a->value))
writer.literal("0/0");
writer.literal(XorStr("0/0"));
else
{
if (isIntegerish(a->value))
@ -348,12 +348,12 @@ struct Printer
}
else if (expr.is<AstExprVarargs>())
{
writer.symbol("...");
writer.symbol(XorStr("..."));
}
else if (const auto& a = expr.as<AstExprCall>())
{
visualize(*a->func);
writer.symbol("(");
writer.symbol(XorStr("("));
bool first = true;
for (const auto& arg : a->args)
@ -361,12 +361,12 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*arg);
}
writer.symbol(")");
writer.symbol(XorStr(")"));
}
else if (const auto& a = expr.as<AstExprIndexName>())
{
@ -377,18 +377,18 @@ struct Printer
else if (const auto& a = expr.as<AstExprIndexExpr>())
{
visualize(*a->expr);
writer.symbol("[");
writer.symbol(XorStr("["));
visualize(*a->index);
writer.symbol("]");
writer.symbol(XorStr("]"));
}
else if (const auto& a = expr.as<AstExprFunction>())
{
writer.keyword("function");
writer.keyword(XorStr("function"));
visualizeFunctionBody(*a);
}
else if (const auto& a = expr.as<AstExprTable>())
{
writer.symbol("{");
writer.symbol(XorStr("{"));
bool first = true;
@ -397,7 +397,7 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
switch (item.kind)
{
@ -410,22 +410,22 @@ struct Printer
advance(item.key->location.begin);
writer.identifier(std::string_view(value.data, value.size));
writer.maybeSpace(item.value->location.begin, 1);
writer.symbol("=");
writer.symbol(XorStr("="));
}
break;
case AstExprTable::Item::General:
{
writer.symbol("[");
writer.symbol(XorStr("["));
visualize(*item.key);
writer.symbol("]");
writer.symbol(XorStr("]"));
writer.maybeSpace(item.value->location.begin, 1);
writer.symbol("=");
writer.symbol(XorStr("="));
}
break;
default:
LUAU_ASSERT(!"Unknown table item kind");
lluz_ASSERT(!XorStr("Unknown table item kind"));
}
advance(item.value->location.begin);
@ -438,7 +438,7 @@ struct Printer
advance(endPos);
writer.symbol("}");
writer.symbol(XorStr("}"));
advance(expr.location.end);
}
else if (const auto& a = expr.as<AstExprUnary>())
@ -446,13 +446,13 @@ struct Printer
switch (a->op)
{
case AstExprUnary::Not:
writer.keyword("not");
writer.keyword(XorStr("not"));
break;
case AstExprUnary::Minus:
writer.symbol("-");
writer.symbol(XorStr("-"));
break;
case AstExprUnary::Len:
writer.symbol("#");
writer.symbol(XorStr("#"));
break;
}
visualize(*a->expr);
@ -498,34 +498,34 @@ struct Printer
if (writeTypes)
{
writer.maybeSpace(a->annotation->location.begin, 2);
writer.symbol("::");
writer.symbol(XorStr("::"));
visualizeTypeAnnotation(*a->annotation);
}
}
else if (const auto& a = expr.as<AstExprIfElse>())
{
writer.keyword("if");
writer.keyword(XorStr("if"));
visualize(*a->condition);
writer.keyword("then");
writer.keyword(XorStr("then"));
visualize(*a->trueExpr);
writer.keyword("else");
writer.keyword(XorStr("else"));
visualize(*a->falseExpr);
}
else if (const auto& a = expr.as<AstExprError>())
{
writer.symbol("(error-expr");
writer.symbol(XorStr("(error-expr"));
for (size_t i = 0; i < a->expressions.size; i++)
{
writer.symbol(i == 0 ? ": " : ", ");
writer.symbol(i == 0 ? XorStr(": " : ", "));
visualize(*a->expressions.data[i]);
}
writer.symbol(")");
writer.symbol(XorStr(")"));
}
else
{
LUAU_ASSERT(!"Unknown AstExpr");
lluz_ASSERT(!XorStr("Unknown AstExpr"));
}
}
@ -535,7 +535,7 @@ struct Printer
if (endPos.column >= 3)
endPos.column -= 3;
advance(endPos);
writer.keyword("end");
writer.keyword(XorStr("end"));
}
void advance(const Position& newPos)
@ -549,7 +549,7 @@ struct Printer
if (const auto& block = program.as<AstStatBlock>())
{
writer.keyword("do");
writer.keyword(XorStr("do"));
for (const auto& s : block->body)
visualize(*s);
writer.advance(block->location.end);
@ -557,33 +557,33 @@ struct Printer
}
else if (const auto& a = program.as<AstStatIf>())
{
writer.keyword("if");
writer.keyword(XorStr("if"));
visualizeElseIf(*a);
}
else if (const auto& a = program.as<AstStatWhile>())
{
writer.keyword("while");
writer.keyword(XorStr("while"));
visualize(*a->condition);
writer.keyword("do");
writer.keyword(XorStr("do"));
visualizeBlock(*a->body);
writeEnd(program.location);
}
else if (const auto& a = program.as<AstStatRepeat>())
{
writer.keyword("repeat");
writer.keyword(XorStr("repeat"));
visualizeBlock(*a->body);
if (a->condition->location.begin.column > 5)
writer.advance(Position{a->condition->location.begin.line, a->condition->location.begin.column - 6});
writer.keyword("until");
writer.keyword(XorStr("until"));
visualize(*a->condition);
}
else if (program.is<AstStatBreak>())
writer.keyword("break");
writer.keyword(XorStr("break"));
else if (program.is<AstStatContinue>())
writer.keyword("continue");
writer.keyword(XorStr("continue"));
else if (const auto& a = program.as<AstStatReturn>())
{
writer.keyword("return");
writer.keyword(XorStr("return"));
bool first = true;
for (const auto& expr : a->list)
@ -591,7 +591,7 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*expr);
}
}
@ -601,7 +601,7 @@ struct Printer
}
else if (const auto& a = program.as<AstStatLocal>())
{
writer.keyword("local");
writer.keyword(XorStr("local"));
bool first = true;
for (const auto& local : a->vars)
@ -609,7 +609,7 @@ struct Printer
if (first)
first = false;
else
writer.write(",");
writer.write(XorStr(","));
visualize(*local);
}
@ -621,36 +621,36 @@ struct Printer
{
first = false;
writer.maybeSpace(value->location.begin, 2);
writer.symbol("=");
writer.symbol(XorStr("="));
}
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*value);
}
}
else if (const auto& a = program.as<AstStatFor>())
{
writer.keyword("for");
writer.keyword(XorStr("for"));
visualize(*a->var);
writer.symbol("=");
writer.symbol(XorStr("="));
visualize(*a->from);
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*a->to);
if (a->step)
{
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*a->step);
}
writer.keyword("do");
writer.keyword(XorStr("do"));
visualizeBlock(*a->body);
writeEnd(program.location);
}
else if (const auto& a = program.as<AstStatForIn>())
{
writer.keyword("for");
writer.keyword(XorStr("for"));
bool first = true;
for (const auto& var : a->vars)
@ -658,12 +658,12 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*var);
}
writer.keyword("in");
writer.keyword(XorStr("in"));
first = true;
for (const auto& val : a->values)
@ -671,12 +671,12 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*val);
}
writer.keyword("do");
writer.keyword(XorStr("do"));
visualizeBlock(*a->body);
@ -690,7 +690,7 @@ struct Printer
if (first)
first = false;
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*var);
}
@ -700,11 +700,11 @@ struct Printer
if (first)
{
writer.maybeSpace(value->location.begin, 1);
writer.symbol("=");
writer.symbol(XorStr("="));
first = false;
}
else
writer.symbol(",");
writer.symbol(XorStr(","));
visualize(*value);
}
@ -717,47 +717,47 @@ struct Printer
{
case AstExprBinary::Add:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("+=");
writer.symbol(XorStr("+="));
break;
case AstExprBinary::Sub:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("-=");
writer.symbol(XorStr("-="));
break;
case AstExprBinary::Mul:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("*=");
writer.symbol(XorStr("*="));
break;
case AstExprBinary::Div:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("/=");
writer.symbol(XorStr("/="));
break;
case AstExprBinary::Mod:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("%=");
writer.symbol(XorStr("%="));
break;
case AstExprBinary::Pow:
writer.maybeSpace(a->value->location.begin, 2);
writer.symbol("^=");
writer.symbol(XorStr("^="));
break;
case AstExprBinary::Concat:
writer.maybeSpace(a->value->location.begin, 3);
writer.symbol("..=");
writer.symbol(XorStr("..="));
break;
default:
LUAU_ASSERT(!"Unexpected compound assignment op");
lluz_ASSERT(!XorStr("Unexpected compound assignment op"));
}
visualize(*a->value);
}
else if (const auto& a = program.as<AstStatFunction>())
{
writer.keyword("function");
writer.keyword(XorStr("function"));
visualize(*a->name);
visualizeFunctionBody(*a->func);
}
else if (const auto& a = program.as<AstStatLocalFunction>())
{
writer.keyword("local function");
writer.keyword(XorStr("local function"));
advance(a->name->location.begin);
writer.identifier(a->name->name.value);
visualizeFunctionBody(*a->func);
@ -767,13 +767,13 @@ struct Printer
if (writeTypes)
{
if (a->exported)
writer.keyword("export");
writer.keyword(XorStr("export"));
writer.keyword("type");
writer.keyword(XorStr("type"));
writer.identifier(a->name.value);
if (a->generics.size > 0 || a->genericPacks.size > 0)
{
writer.symbol("<");
writer.symbol(XorStr("<"));
CommaSeparatorInserter comma(writer);
for (auto o : a->generics)
@ -786,7 +786,7 @@ struct Printer
if (o.defaultValue)
{
writer.maybeSpace(o.defaultValue->location.begin, 2);
writer.symbol("=");
writer.symbol(XorStr("="));
visualizeTypeAnnotation(*o.defaultValue);
}
}
@ -797,48 +797,48 @@ struct Printer
writer.advance(o.location.begin);
writer.identifier(o.name.value);
writer.symbol("...");
writer.symbol(XorStr("..."));
if (o.defaultValue)
{
writer.maybeSpace(o.defaultValue->location.begin, 2);
writer.symbol("=");
writer.symbol(XorStr("="));
visualizeTypePackAnnotation(*o.defaultValue, false);
}
}
writer.symbol(">");
writer.symbol(XorStr(">"));
}
writer.maybeSpace(a->type->location.begin, 2);
writer.symbol("=");
writer.symbol(XorStr("="));
visualizeTypeAnnotation(*a->type);
}
}
else if (const auto& a = program.as<AstStatError>())
{
writer.symbol("(error-stat");
writer.symbol(XorStr("(error-stat"));
for (size_t i = 0; i < a->expressions.size; i++)
{
writer.symbol(i == 0 ? ": " : ", ");
writer.symbol(i == 0 ? XorStr(": " : ", "));
visualize(*a->expressions.data[i]);
}
for (size_t i = 0; i < a->statements.size; i++)
{
writer.symbol(i == 0 && a->expressions.size == 0 ? ": " : ", ");
writer.symbol(i == 0 && a->expressions.size == 0 ? XorStr(": " : ", "));
visualize(*a->statements.data[i]);
}
writer.symbol(")");
writer.symbol(XorStr(")"));
}
else
{
LUAU_ASSERT(!"Unknown AstStat");
lluz_ASSERT(!XorStr("Unknown AstStat"));
}
if (program.hasSemicolon)
writer.symbol(";");
writer.symbol(XorStr(";"));
}
void visualizeFunctionBody(AstExprFunction& func)
@ -846,7 +846,7 @@ struct Printer
if (func.generics.size > 0 || func.genericPacks.size > 0)
{
CommaSeparatorInserter comma(writer);
writer.symbol("<");
writer.symbol(XorStr("<"));
for (const auto& o : func.generics)
{
comma();
@ -860,12 +860,12 @@ struct Printer
writer.advance(o.location.begin);
writer.identifier(o.name.value);
writer.symbol("...");
writer.symbol(XorStr("..."));
}
writer.symbol(">");
writer.symbol(XorStr(">"));
}
writer.symbol("(");
writer.symbol(XorStr("("));
CommaSeparatorInserter comma(writer);
for (size_t i = 0; i < func.args.size; ++i)
@ -878,7 +878,7 @@ struct Printer
writer.identifier(local->name.value);
if (writeTypes && local->annotation)
{
writer.symbol(":");
writer.symbol(XorStr(":"));
visualizeTypeAnnotation(*local->annotation);
}
}
@ -887,20 +887,20 @@ struct Printer
{
comma();
advance(func.varargLocation.begin);
writer.symbol("...");
writer.symbol(XorStr("..."));
if (func.varargAnnotation)
{
writer.symbol(":");
writer.symbol(XorStr(":"));
visualizeTypePackAnnotation(*func.varargAnnotation, true);
}
}
writer.symbol(")");
writer.symbol(XorStr(")"));
if (writeTypes && func.returnAnnotation)
{
writer.symbol(":");
writer.symbol(XorStr(":"));
writer.space();
visualizeTypeList(*func.returnAnnotation, false);
@ -922,13 +922,13 @@ struct Printer
if (AstStatBlock* block = stat.as<AstStatBlock>())
visualizeBlock(*block);
else
LUAU_ASSERT(!"visualizeBlock was expecting an AstStatBlock");
lluz_ASSERT(!XorStr("visualizeBlock was expecting an AstStatBlock"));
}
void visualizeElseIf(AstStatIf& elseif)
{
visualize(*elseif.condition);
writer.keyword("then");
writer.keyword(XorStr("then"));
visualizeBlock(*elseif.thenbody);
if (elseif.elsebody == nullptr)
@ -937,12 +937,12 @@ struct Printer
}
else if (auto elseifelseif = elseif.elsebody->as<AstStatIf>())
{
writer.keyword("elseif");
writer.keyword(XorStr("elseif"));
visualizeElseIf(*elseifelseif);
}
else
{
writer.keyword("else");
writer.keyword(XorStr("else"));
visualizeBlock(*elseif.elsebody);
writeEnd(elseif.location);
@ -957,14 +957,14 @@ struct Printer
if (a->prefix)
{
writer.write(a->prefix->value);
writer.symbol(".");
writer.symbol(XorStr("."));
}
writer.write(a->name.value);
if (a->parameters.size > 0 || a->hasParameterList)
{
CommaSeparatorInserter comma(writer);
writer.symbol("<");
writer.symbol(XorStr("<"));
for (auto o : a->parameters)
{
comma();
@ -975,7 +975,7 @@ struct Printer
visualizeTypePackAnnotation(*o.typePack, false);
}
writer.symbol(">");
writer.symbol(XorStr(">"));
}
}
else if (const auto& a = typeAnnotation.as<AstTypeFunction>())
@ -983,7 +983,7 @@ struct Printer
if (a->generics.size > 0 || a->genericPacks.size > 0)
{
CommaSeparatorInserter comma(writer);
writer.symbol("<");
writer.symbol(XorStr("<"));
for (const auto& o : a->generics)
{
comma();
@ -997,33 +997,33 @@ struct Printer
writer.advance(o.location.begin);
writer.identifier(o.name.value);
writer.symbol("...");
writer.symbol(XorStr("..."));
}
writer.symbol(">");
writer.symbol(XorStr(">"));
}
{
visualizeTypeList(a->argTypes, true);
}
writer.symbol("->");
writer.symbol(XorStr("->"));
visualizeTypeList(a->returnTypes, true);
}
else if (const auto& a = typeAnnotation.as<AstTypeTable>())
{
AstTypeReference* indexType = a->indexer ? a->indexer->indexType->as<AstTypeReference>() : nullptr;
if (a->props.size == 0 && indexType && indexType->name == "number")
if (a->props.size == 0 && indexType && indexType->name == XorStr("number"))
{
writer.symbol("{");
writer.symbol(XorStr("{"));
visualizeTypeAnnotation(*a->indexer->resultType);
writer.symbol("}");
writer.symbol(XorStr("}"));
}
else
{
CommaSeparatorInserter comma(writer);
writer.symbol("{");
writer.symbol(XorStr("{"));
for (std::size_t i = 0; i < a->props.size; ++i)
{
@ -1032,28 +1032,28 @@ struct Printer
writer.identifier(a->props.data[i].name.value);
if (a->props.data[i].type)
{
writer.symbol(":");
writer.symbol(XorStr(":"));
visualizeTypeAnnotation(*a->props.data[i].type);
}
}
if (a->indexer)
{
comma();
writer.symbol("[");
writer.symbol(XorStr("["));
visualizeTypeAnnotation(*a->indexer->indexType);
writer.symbol("]");
writer.symbol(":");
writer.symbol(XorStr("]"));
writer.symbol(XorStr(":"));
visualizeTypeAnnotation(*a->indexer->resultType);
}
writer.symbol("}");
writer.symbol(XorStr("}"));
}
}
else if (auto a = typeAnnotation.as<AstTypeTypeof>())
{
writer.keyword("typeof");
writer.symbol("(");
writer.keyword(XorStr("typeof"));
writer.symbol(XorStr("("));
visualize(*a->expr);
writer.symbol(")");
writer.symbol(XorStr(")"));
}
else if (const auto& a = typeAnnotation.as<AstTypeUnion>())
{
@ -1063,24 +1063,24 @@ struct Printer
AstType* r = a->types.data[1];
auto lta = l->as<AstTypeReference>();
if (lta && lta->name == "nil")
if (lta && lta->name == XorStr("nil"))
std::swap(l, r);
// it's still possible that we had a (T | U) or (T | nil) and not (nil | T)
auto rta = r->as<AstTypeReference>();
if (rta && rta->name == "nil")
if (rta && rta->name == XorStr("nil"))
{
bool wrap = l->as<AstTypeIntersection>() || l->as<AstTypeFunction>();
if (wrap)
writer.symbol("(");
writer.symbol(XorStr("("));
visualizeTypeAnnotation(*l);
if (wrap)
writer.symbol(")");
writer.symbol(XorStr(")"));
writer.symbol("?");
writer.symbol(XorStr("?"));
return;
}
}
@ -1090,18 +1090,18 @@ struct Printer
if (i > 0)
{
writer.maybeSpace(a->types.data[i]->location.begin, 2);
writer.symbol("|");
writer.symbol(XorStr("|"));
}
bool wrap = a->types.data[i]->as<AstTypeIntersection>() || a->types.data[i]->as<AstTypeFunction>();
if (wrap)
writer.symbol("(");
writer.symbol(XorStr("("));
visualizeTypeAnnotation(*a->types.data[i]);
if (wrap)
writer.symbol(")");
writer.symbol(XorStr(")"));
}
}
else if (const auto& a = typeAnnotation.as<AstTypeIntersection>())
@ -1111,23 +1111,23 @@ struct Printer
if (i > 0)
{
writer.maybeSpace(a->types.data[i]->location.begin, 2);
writer.symbol("&");
writer.symbol(XorStr("&"));
}
bool wrap = a->types.data[i]->as<AstTypeUnion>() || a->types.data[i]->as<AstTypeFunction>();
if (wrap)
writer.symbol("(");
writer.symbol(XorStr("("));
visualizeTypeAnnotation(*a->types.data[i]);
if (wrap)
writer.symbol(")");
writer.symbol(XorStr(")"));
}
}
else if (const auto& a = typeAnnotation.as<AstTypeSingletonBool>())
{
writer.keyword(a->value ? "true" : "false");
writer.keyword(a->value ? XorStr("true" : "false"));
}
else if (const auto& a = typeAnnotation.as<AstTypeSingletonString>())
{
@ -1135,11 +1135,11 @@ struct Printer
}
else if (typeAnnotation.is<AstTypeError>())
{
writer.symbol("%error-type%");
writer.symbol(XorStr("%error-type%"));
}
else
{
LUAU_ASSERT(!"Unknown AstType");
lluz_ASSERT(!XorStr("Unknown AstType"));
}
}
};
@ -1197,7 +1197,7 @@ TranspileResult transpile(std::string_view source, ParseOptions options, bool wi
return TranspileResult{"", error.getLocation(), error.what()};
}
LUAU_ASSERT(parseResult.root);
lluz_ASSERT(parseResult.root);
if (!parseResult.root)
return TranspileResult{"", {}, "Internal error: Parser yielded empty parse tree"};
@ -1207,4 +1207,4 @@ TranspileResult transpile(std::string_view source, ParseOptions options, bool wi
return TranspileResult{transpile(*parseResult.root)};
}
} // namespace Luau
} // namespace lluz

View file

@ -1,15 +1,15 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TxnLog.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TxnLog.h"
#include "Luau/ToString.h"
#include "Luau/TypePack.h"
#include "lluz/ToString.h"
#include "lluz/TypePack.h"
#include <algorithm>
#include <stdexcept>
LUAU_FASTFLAG(LuauUnknownAndNeverType)
lluz_FASTFLAG(LluNonCopyableTypeVarFields)
namespace Luau
namespace lluz
{
const std::string nullPendingResult = "<nullptr>";
@ -81,10 +81,34 @@ void TxnLog::concat(TxnLog rhs)
void TxnLog::commit()
{
for (auto& [ty, rep] : typeVarChanges)
asMutable(ty)->reassign(rep.get()->pending);
{
if (FFlag::LluNonCopyableTypeVarFields)
{
asMutable(ty)->reassign(rep.get()->pending);
}
else
{
TypeArena* owningArena = ty->owningArena;
TypeVar* mtv = asMutable(ty);
*mtv = rep.get()->pending;
mtv->owningArena = owningArena;
}
}
for (auto& [tp, rep] : typePackChanges)
asMutable(tp)->reassign(rep.get()->pending);
{
if (FFlag::LluNonCopyableTypeVarFields)
{
asMutable(tp)->reassign(rep.get()->pending);
}
else
{
TypeArena* owningArena = tp->owningArena;
TypePackVar* mpv = asMutable(tp);
*mpv = rep.get()->pending;
mpv->owningArena = owningArena;
}
}
clear();
}
@ -158,13 +182,13 @@ void TxnLog::pushSeen(TypeOrPackId lhs, TypeOrPackId rhs)
void TxnLog::popSeen(TypeOrPackId lhs, TypeOrPackId rhs)
{
const std::pair<TypeOrPackId, TypeOrPackId> sortedPair = (lhs > rhs) ? std::make_pair(lhs, rhs) : std::make_pair(rhs, lhs);
LUAU_ASSERT(sortedPair == sharedSeen->back());
lluz_ASSERT(sortedPair == sharedSeen->back());
sharedSeen->pop_back();
}
PendingType* TxnLog::queue(TypeId ty)
{
LUAU_ASSERT(!ty->persistent);
lluz_ASSERT(!ty->persistent);
// Explicitly don't look in ancestors. If we have discovered something new
// about this type, we don't want to mutate the parent's state.
@ -172,7 +196,9 @@ PendingType* TxnLog::queue(TypeId ty)
if (!pending)
{
pending = std::make_unique<PendingType>(*ty);
pending->pending.owningArena = nullptr;
if (FFlag::LluNonCopyableTypeVarFields)
pending->pending.owningArena = nullptr;
}
return pending.get();
@ -180,7 +206,7 @@ PendingType* TxnLog::queue(TypeId ty)
PendingTypePack* TxnLog::queue(TypePackId tp)
{
LUAU_ASSERT(!tp->persistent);
lluz_ASSERT(!tp->persistent);
// Explicitly don't look in ancestors. If we have discovered something new
// about this type, we don't want to mutate the parent's state.
@ -188,7 +214,9 @@ PendingTypePack* TxnLog::queue(TypePackId tp)
if (!pending)
{
pending = std::make_unique<PendingTypePack>(*tp);
pending->pending.owningArena = nullptr;
if (FFlag::LluNonCopyableTypeVarFields)
pending->pending.owningArena = nullptr;
}
return pending.get();
@ -198,7 +226,7 @@ PendingType* TxnLog::pending(TypeId ty) const
{
// This function will technically work if `this` is nullptr, but this
// indicates a bug, so we explicitly assert.
LUAU_ASSERT(static_cast<const void*>(this) != nullptr);
lluz_ASSERT(static_cast<const void*>(this) != nullptr);
for (const TxnLog* current = this; current; current = current->parent)
{
@ -213,7 +241,7 @@ PendingTypePack* TxnLog::pending(TypePackId tp) const
{
// This function will technically work if `this` is nullptr, but this
// indicates a bug, so we explicitly assert.
LUAU_ASSERT(static_cast<const void*>(this) != nullptr);
lluz_ASSERT(static_cast<const void*>(this) != nullptr);
for (const TxnLog* current = this; current; current = current->parent)
{
@ -227,23 +255,33 @@ PendingTypePack* TxnLog::pending(TypePackId tp) const
PendingType* TxnLog::replace(TypeId ty, TypeVar replacement)
{
PendingType* newTy = queue(ty);
newTy->pending.reassign(replacement);
if (FFlag::LluNonCopyableTypeVarFields)
newTy->pending.reassign(replacement);
else
newTy->pending = replacement;
return newTy;
}
PendingTypePack* TxnLog::replace(TypePackId tp, TypePackVar replacement)
{
PendingTypePack* newTp = queue(tp);
newTp->pending.reassign(replacement);
if (FFlag::LluNonCopyableTypeVarFields)
newTp->pending.reassign(replacement);
else
newTp->pending = replacement;
return newTp;
}
PendingType* TxnLog::bindTable(TypeId ty, std::optional<TypeId> newBoundTo)
{
LUAU_ASSERT(get<TableTypeVar>(ty));
lluz_ASSERT(get<TableTypeVar>(ty));
PendingType* newTy = queue(ty);
if (TableTypeVar* ttv = Luau::getMutable<TableTypeVar>(newTy))
if (TableTypeVar* ttv = lluz::getMutable<TableTypeVar>(newTy))
ttv->boundTo = newBoundTo;
return newTy;
@ -251,37 +289,32 @@ PendingType* TxnLog::bindTable(TypeId ty, std::optional<TypeId> newBoundTo)
PendingType* TxnLog::changeLevel(TypeId ty, TypeLevel newLevel)
{
LUAU_ASSERT(get<FreeTypeVar>(ty) || get<TableTypeVar>(ty) || get<FunctionTypeVar>(ty) || get<ConstrainedTypeVar>(ty));
lluz_ASSERT(get<FreeTypeVar>(ty) || get<TableTypeVar>(ty) || get<FunctionTypeVar>(ty));
PendingType* newTy = queue(ty);
if (FreeTypeVar* ftv = Luau::getMutable<FreeTypeVar>(newTy))
if (FreeTypeVar* ftv = lluz::getMutable<FreeTypeVar>(newTy))
{
ftv->level = newLevel;
}
else if (TableTypeVar* ttv = Luau::getMutable<TableTypeVar>(newTy))
else if (TableTypeVar* ttv = lluz::getMutable<TableTypeVar>(newTy))
{
LUAU_ASSERT(ttv->state == TableState::Free || ttv->state == TableState::Generic);
lluz_ASSERT(ttv->state == TableState::Free || ttv->state == TableState::Generic);
ttv->level = newLevel;
}
else if (FunctionTypeVar* ftv = Luau::getMutable<FunctionTypeVar>(newTy))
else if (FunctionTypeVar* ftv = lluz::getMutable<FunctionTypeVar>(newTy))
{
ftv->level = newLevel;
}
else if (ConstrainedTypeVar* ctv = Luau::getMutable<ConstrainedTypeVar>(newTy))
{
if (FFlag::LuauUnknownAndNeverType)
ctv->level = newLevel;
}
return newTy;
}
PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
{
LUAU_ASSERT(get<FreeTypePack>(tp));
lluz_ASSERT(get<FreeTypePack>(tp));
PendingTypePack* newTp = queue(tp);
if (FreeTypePack* ftp = Luau::getMutable<FreeTypePack>(newTp))
if (FreeTypePack* ftp = lluz::getMutable<FreeTypePack>(newTp))
{
ftp->level = newLevel;
}
@ -291,10 +324,10 @@ PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel)
PendingType* TxnLog::changeIndexer(TypeId ty, std::optional<TableIndexer> indexer)
{
LUAU_ASSERT(get<TableTypeVar>(ty));
lluz_ASSERT(get<TableTypeVar>(ty));
PendingType* newTy = queue(ty);
if (TableTypeVar* ttv = Luau::getMutable<TableTypeVar>(newTy))
if (TableTypeVar* ttv = lluz::getMutable<TableTypeVar>(newTy))
{
ttv->indexer = indexer;
}
@ -316,7 +349,7 @@ std::optional<TypeLevel> TxnLog::getLevel(TypeId ty) const
TypeId TxnLog::follow(TypeId ty) const
{
return Luau::follow(ty, [this](TypeId ty) {
return lluz::follow(ty, [this](TypeId ty) {
PendingType* state = this->pending(ty);
if (state == nullptr)
@ -331,7 +364,7 @@ TypeId TxnLog::follow(TypeId ty) const
TypePackId TxnLog::follow(TypePackId tp) const
{
return Luau::follow(tp, [this](TypePackId tp) {
return lluz::follow(tp, [this](TypePackId tp) {
PendingTypePack* state = this->pending(tp);
if (state == nullptr)
@ -344,4 +377,4 @@ TypePackId TxnLog::follow(TypePackId tp) const
});
}
} // namespace Luau
} // namespace lluz

View file

@ -1,10 +1,10 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeArena.h"
#include "lluz/TypeArena.h"
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena, false);
lluz_FASTFLAGVARIABLE(DebugLluFreezeArena, false);
namespace Luau
namespace lluz
{
void TypeArena::clear()
@ -69,7 +69,7 @@ TypePackId TypeArena::addTypePack(TypePackVar tp)
void freeze(TypeArena& arena)
{
if (!FFlag::DebugLuauFreezeArena)
if (!FFlag::DebugLluFreezeArena)
return;
arena.typeVars.freeze();
@ -78,11 +78,11 @@ void freeze(TypeArena& arena)
void unfreeze(TypeArena& arena)
{
if (!FFlag::DebugLuauFreezeArena)
if (!FFlag::DebugLluFreezeArena)
return;
arena.typeVars.unfreeze();
arena.typePacks.unfreeze();
}
} // namespace Luau
} // namespace lluz

View file

@ -1,18 +1,18 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeAttach.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TypeAttach.h"
#include "Luau/Error.h"
#include "Luau/Module.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
#include "Luau/ToString.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "Luau/TypeVar.h"
#include "lluz/Error.h"
#include "lluz/Module.h"
#include "lluz/RecursionCounter.h"
#include "lluz/Scope.h"
#include "lluz/ToString.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypePack.h"
#include "lluz/TypeVar.h"
#include <string>
static char* allocateString(Luau::Allocator& allocator, std::string_view contents)
static char* allocateString(lluz::Allocator& allocator, std::string_view contents)
{
char* result = (char*)allocator.allocate(contents.size() + 1);
memcpy(result, contents.data(), contents.size());
@ -22,7 +22,7 @@ static char* allocateString(Luau::Allocator& allocator, std::string_view content
}
template<typename... Data>
static char* allocateString(Luau::Allocator& allocator, const char* format, Data... data)
static char* allocateString(lluz::Allocator& allocator, const char* format, Data... data)
{
int len = snprintf(nullptr, 0, format, data...);
char* result = (char*)allocator.allocate(len + 1);
@ -32,7 +32,7 @@ static char* allocateString(Luau::Allocator& allocator, const char* format, Data
using SyntheticNames = std::unordered_map<const void*, char*>;
namespace Luau
namespace lluz
{
static const char* getName(Allocator* allocator, SyntheticNames* syntheticNames, const Unifiable::Generic& gen)
@ -105,7 +105,7 @@ public:
types.size = ctv.parts.size();
types.data = static_cast<AstType**>(allocator->allocate(sizeof(AstType*) * ctv.parts.size()));
for (size_t i = 0; i < ctv.parts.size(); ++i)
types.data[i] = Luau::visit(*this, ctv.parts[i]->ty);
types.data[i] = lluz::visit(*this, ctv.parts[i]->ty);
return allocator->alloc<AstTypeIntersection>(Location(), types);
}
@ -140,7 +140,7 @@ public:
for (size_t i = 0; i < ttv.instantiatedTypeParams.size(); ++i)
{
parameters.data[i] = {Luau::visit(*this, ttv.instantiatedTypeParams[i]->ty), {}};
parameters.data[i] = {lluz::visit(*this, ttv.instantiatedTypeParams[i]->ty), {}};
}
for (size_t i = 0; i < ttv.instantiatedTypePackParams.size(); ++i)
@ -170,7 +170,7 @@ public:
char* name = allocateString(*allocator, propName);
props.data[idx].name = AstName(name);
props.data[idx].type = Luau::visit(*this, prop.type->ty);
props.data[idx].type = lluz::visit(*this, prop.type->ty);
props.data[idx].location = Location();
idx++;
}
@ -181,15 +181,15 @@ public:
RecursionCounter counter(&count);
indexer = allocator->alloc<AstTableIndexer>();
indexer->indexType = Luau::visit(*this, ttv.indexer->indexType->ty);
indexer->resultType = Luau::visit(*this, ttv.indexer->indexResultType->ty);
indexer->indexType = lluz::visit(*this, ttv.indexer->indexType->ty);
indexer->resultType = lluz::visit(*this, ttv.indexer->indexResultType->ty);
}
return allocator->alloc<AstTypeTable>(Location(), props, indexer);
}
AstType* operator()(const MetatableTypeVar& mtv)
{
return Luau::visit(*this, mtv.table->ty);
return lluz::visit(*this, mtv.table->ty);
}
AstType* operator()(const ClassTypeVar& ctv)
@ -211,7 +211,7 @@ public:
char* name = allocateString(*allocator, propName);
props.data[idx].name = AstName{name};
props.data[idx].type = Luau::visit(*this, prop.type->ty);
props.data[idx].type = lluz::visit(*this, prop.type->ty);
props.data[idx].location = Location();
idx++;
}
@ -254,7 +254,7 @@ public:
{
RecursionCounter counter(&count);
argTypes.data[i] = Luau::visit(*this, (argVector[i])->ty);
argTypes.data[i] = lluz::visit(*this, (argVector[i])->ty);
}
AstTypePack* argTailAnnotation = nullptr;
@ -283,7 +283,7 @@ public:
{
RecursionCounter counter(&count);
returnTypes.data[i] = Luau::visit(*this, (retVector[i])->ty);
returnTypes.data[i] = lluz::visit(*this, (retVector[i])->ty);
}
AstTypePack* retTailAnnotation = nullptr;
@ -303,7 +303,7 @@ public:
}
AstType* operator()(const Unifiable::Bound<TypeId>& bound)
{
return Luau::visit(*this, bound.boundTo->ty);
return lluz::visit(*this, bound.boundTo->ty);
}
AstType* operator()(const FreeTypeVar& ftv)
{
@ -316,7 +316,7 @@ public:
unionTypes.data = static_cast<AstType**>(allocator->allocate(sizeof(AstType*) * unionTypes.size));
for (size_t i = 0; i < unionTypes.size; ++i)
{
unionTypes.data[i] = Luau::visit(*this, uv.options[i]->ty);
unionTypes.data[i] = lluz::visit(*this, uv.options[i]->ty);
}
return allocator->alloc<AstTypeUnion>(Location(), unionTypes);
}
@ -327,7 +327,7 @@ public:
intersectionTypes.data = static_cast<AstType**>(allocator->allocate(sizeof(AstType*) * intersectionTypes.size));
for (size_t i = 0; i < intersectionTypes.size; ++i)
{
intersectionTypes.data[i] = Luau::visit(*this, uv.parts[i]->ty);
intersectionTypes.data[i] = lluz::visit(*this, uv.parts[i]->ty);
}
return allocator->alloc<AstTypeIntersection>(Location(), intersectionTypes);
}
@ -335,14 +335,6 @@ public:
{
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("<Lazy?>"));
}
AstType* operator()(const UnknownTypeVar& ttv)
{
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{"unknown"});
}
AstType* operator()(const NeverTypeVar& ttv)
{
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName{"never"});
}
private:
Allocator* allocator;
@ -358,14 +350,14 @@ public:
, syntheticNames(syntheticNames)
, typeVisitor(typeVisitor)
{
LUAU_ASSERT(allocator);
LUAU_ASSERT(syntheticNames);
LUAU_ASSERT(typeVisitor);
lluz_ASSERT(allocator);
lluz_ASSERT(syntheticNames);
lluz_ASSERT(typeVisitor);
}
AstTypePack* operator()(const BoundTypePack& btp) const
{
return Luau::visit(*this, btp.boundTo->ty);
return lluz::visit(*this, btp.boundTo->ty);
}
AstTypePack* operator()(const TypePack& tp) const
@ -375,12 +367,12 @@ public:
head.data = static_cast<AstType**>(allocator->allocate(sizeof(AstType*) * tp.head.size()));
for (size_t i = 0; i < tp.head.size(); i++)
head.data[i] = Luau::visit(*typeVisitor, tp.head[i]->ty);
head.data[i] = lluz::visit(*typeVisitor, tp.head[i]->ty);
AstTypePack* tail = nullptr;
if (tp.tail)
tail = Luau::visit(*this, (*tp.tail)->ty);
tail = lluz::visit(*this, (*tp.tail)->ty);
return allocator->alloc<AstTypePackExplicit>(Location(), AstTypeList{head, tail});
}
@ -390,7 +382,7 @@ public:
if (vtp.hidden)
return nullptr;
return allocator->alloc<AstTypePackVariadic>(Location(), Luau::visit(*typeVisitor, vtp.ty->ty));
return allocator->alloc<AstTypePackVariadic>(Location(), lluz::visit(*typeVisitor, vtp.ty->ty));
}
AstTypePack* operator()(const GenericTypePack& gtp) const
@ -417,13 +409,13 @@ private:
AstTypePack* TypeRehydrationVisitor::rehydrate(TypePackId tp)
{
TypePackRehydrationVisitor tprv(allocator, syntheticNames, this);
return Luau::visit(tprv, tp->ty);
return lluz::visit(tprv, tp->ty);
}
class TypeAttacher : public AstVisitor
{
public:
TypeAttacher(Module& checker, Luau::Allocator* alloc)
TypeAttacher(Module& checker, lluz::Allocator* alloc)
: module(checker)
, allocator(alloc)
{
@ -451,10 +443,10 @@ public:
{
if (!type)
return nullptr;
return Luau::visit(TypeRehydrationVisitor(allocator, &syntheticNames), (*type)->ty);
return lluz::visit(TypeRehydrationVisitor(allocator, &syntheticNames), (*type)->ty);
}
AstArray<Luau::AstType*> typeAstPack(TypePackId type)
AstArray<lluz::AstType*> typeAstPack(TypePackId type)
{
const auto& [v, tail] = flatten(type);
@ -463,7 +455,7 @@ public:
result.data = static_cast<AstType**>(allocator->allocate(sizeof(AstType*) * v.size()));
for (size_t i = 0; i < v.size(); ++i)
{
result.data[i] = Luau::visit(TypeRehydrationVisitor(allocator, &syntheticNames), v[i]->ty);
result.data[i] = lluz::visit(TypeRehydrationVisitor(allocator, &syntheticNames), v[i]->ty);
}
return result;
}
@ -549,7 +541,7 @@ void attachTypeData(SourceModule& source, Module& result)
AstType* rehydrateAnnotation(TypeId type, Allocator* allocator, const TypeRehydrationOptions& options)
{
SyntheticNames syntheticNames;
return Luau::visit(TypeRehydrationVisitor(allocator, &syntheticNames, options), type->ty);
return lluz::visit(TypeRehydrationVisitor(allocator, &syntheticNames, options), type->ty);
}
} // namespace Luau
} // namespace lluz

View file

@ -1,19 +1,19 @@
#include "Luau/TypeChecker2.h"
#include "lluz/TypeChecker2.h"
#include <algorithm>
#include "Luau/Ast.h"
#include "Luau/AstQuery.h"
#include "Luau/Clone.h"
#include "Luau/Instantiation.h"
#include "Luau/Normalize.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeUtils.h"
#include "Luau/Unifier.h"
#include "Luau/ToString.h"
#include "lluz/Ast.h"
#include "lluz/AstQuery.h"
#include "lluz/Clone.h"
#include "lluz/Instantiation.h"
#include "lluz/Normalize.h"
#include "lluz/TxnLog.h"
#include "lluz/TypeUtils.h"
#include "lluz/Unifier.h"
#include "lluz/ToString.h"
namespace Luau
namespace lluz
{
struct TypeChecker2 : public AstVisitor
@ -63,15 +63,12 @@ struct TypeChecker2 : public AstVisitor
TypeId lookupAnnotation(AstType* annotation)
{
TypeId* ty = module->astResolvedTypes.find(annotation);
LUAU_ASSERT(ty);
lluz_ASSERT(ty);
return follow(*ty);
}
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
{
if (exprs.size == 0)
return arena.addTypePack(TypePack{{}, std::nullopt});
std::vector<TypeId> head;
for (size_t i = 0; i < exprs.size - 1; ++i)
@ -83,14 +80,14 @@ struct TypeChecker2 : public AstVisitor
return arena.addTypePack(TypePack{head, tail});
}
Scope* findInnermostScope(Location location)
Scope2* findInnermostScope(Location location)
{
Scope* bestScope = module->getModuleScope().get();
Location bestLocation = module->scopes[0].first;
Scope2* bestScope = module->getModuleScope2();
Location bestLocation = module->scope2s[0].first;
for (size_t i = 0; i < module->scopes.size(); ++i)
for (size_t i = 0; i < module->scope2s.size(); ++i)
{
auto& [scopeBounds, scope] = module->scopes[i];
auto& [scopeBounds, scope] = module->scope2s[i];
if (scopeBounds.encloses(location))
{
if (scopeBounds.begin > bestLocation.begin || scopeBounds.end < bestLocation.end)
@ -184,7 +181,7 @@ struct TypeChecker2 : public AstVisitor
bool visit(AstStatReturn* ret) override
{
Scope* scope = findInnermostScope(ret->location);
Scope2* scope = findInnermostScope(ret->location);
TypePackId expectedRetType = scope->returnType;
TypeArena arena;
@ -214,13 +211,13 @@ struct TypeChecker2 : public AstVisitor
TypePackId expectedRetType = lookupPack(call);
TypeId functionType = lookupType(call->func);
TypeId instantiatedFunctionType = instantiation.substitute(functionType).value_or(nullptr);
LUAU_ASSERT(functionType);
lluz_ASSERT(functionType);
TypePack args;
for (const auto& arg : call->args)
{
TypeId argTy = module->astTypes[arg];
LUAU_ASSERT(argTy);
lluz_ASSERT(argTy);
args.head.push_back(argTy);
}
@ -243,7 +240,7 @@ struct TypeChecker2 : public AstVisitor
{
TypeId inferredFnTy = lookupType(fn);
const FunctionTypeVar* inferredFtv = get<FunctionTypeVar>(inferredFnTy);
LUAU_ASSERT(inferredFtv);
lluz_ASSERT(inferredFtv);
auto argIt = begin(inferredFtv->argTypes);
for (const auto& arg : fn->args)
@ -325,13 +322,10 @@ struct TypeChecker2 : public AstVisitor
{
pack = follow(pack);
while (true)
while (auto tp = get<TypePack>(pack))
{
auto tp = get<TypePack>(pack);
if (tp && tp->head.empty() && tp->tail)
if (tp->head.empty() && tp->tail)
pack = *tp->tail;
else
break;
}
if (auto ty = first(pack))
@ -352,7 +346,7 @@ struct TypeChecker2 : public AstVisitor
else if (get<Unifiable::Error>(pack))
return singletonTypes.errorRecoveryType();
else
ice.ice("flattenPack got a weird pack!");
ice.ice(XorStr("flattenPack got a weird pack!"));
}
bool visit(AstType* ty) override
@ -362,7 +356,7 @@ struct TypeChecker2 : public AstVisitor
bool visit(AstTypeReference* ty) override
{
Scope* scope = findInnermostScope(ty->location);
Scope2* scope = findInnermostScope(ty->location);
// TODO: Imported types
// TODO: Generic types
@ -392,4 +386,4 @@ void check(const SourceModule& sourceModule, Module* module)
sourceModule.root->visit(&typeChecker);
}
} // namespace Luau
} // namespace lluz

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,13 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypePack.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TypePack.h"
#include "Luau/TxnLog.h"
#include "lluz/TxnLog.h"
#include <stdexcept>
namespace Luau
lluz_FASTFLAG(LluNonCopyableTypeVarFields)
namespace lluz
{
TypePackVar::TypePackVar(const TypePackVariant& tp)
@ -38,10 +40,19 @@ TypePackVar& TypePackVar::operator=(TypePackVariant&& tp)
TypePackVar& TypePackVar::operator=(const TypePackVar& rhs)
{
LUAU_ASSERT(owningArena == rhs.owningArena);
LUAU_ASSERT(!rhs.persistent);
if (FFlag::LluNonCopyableTypeVarFields)
{
lluz_ASSERT(owningArena == rhs.owningArena);
lluz_ASSERT(!rhs.persistent);
reassign(rhs);
reassign(rhs);
}
else
{
ty = rhs.ty;
persistent = rhs.persistent;
owningArena = rhs.owningArena;
}
return *this;
}
@ -66,7 +77,7 @@ TypePackIterator::TypePackIterator(TypePackId typePack, const TxnLog* log)
TypePackIterator& TypePackIterator::operator++()
{
LUAU_ASSERT(tp);
lluz_ASSERT(tp);
++currentIndex;
while (tp && currentIndex >= tp->head.size())
@ -99,13 +110,13 @@ bool TypePackIterator::operator==(const TypePackIterator& rhs)
const TypeId& TypePackIterator::operator*()
{
LUAU_ASSERT(tp);
lluz_ASSERT(tp);
return tp->head[currentIndex];
}
std::optional<TypePackId> TypePackIterator::tail()
{
LUAU_ASSERT(!tp);
lluz_ASSERT(!tp);
return currentTypePack ? std::optional<TypePackId>{currentTypePack} : std::nullopt;
}
@ -227,7 +238,7 @@ TypePackId follow(TypePackId tp, std::function<TypePackId(TypePackId)> mapper)
cycleTester = nullptr;
if (tp == cycleTester)
throw std::runtime_error("Luau::follow detected a TypeVar cycle!!");
throw std::runtime_error(XorStr("lluz::follow detected a TypeVar cycle!!"));
}
}
}
@ -283,16 +294,6 @@ std::optional<TypeId> first(TypePackId tp, bool ignoreHiddenVariadics)
return std::nullopt;
}
TypePackVar* asMutable(TypePackId tp)
{
return const_cast<TypePackVar*>(tp);
}
TypePack* asMutable(const TypePack* tp)
{
return const_cast<TypePack*>(tp);
}
bool isEmpty(TypePackId tp)
{
tp = follow(tp);
@ -359,25 +360,13 @@ bool isVariadic(TypePackId tp, const TxnLog& log)
return false;
}
bool containsNever(TypePackId tp)
TypePackVar* asMutable(TypePackId tp)
{
auto it = begin(tp);
auto endIt = end(tp);
while (it != endIt)
{
if (get<NeverTypeVar>(follow(*it)))
return true;
++it;
}
if (auto tail = it.tail())
{
if (auto vtp = get<VariadicTypePack>(*tail); vtp && get<NeverTypeVar>(follow(vtp->ty)))
return true;
}
return false;
return const_cast<TypePackVar*>(tp);
}
} // namespace Luau
TypePack* asMutable(const TypePack* tp)
{
return const_cast<TypePack*>(tp);
}
} // namespace lluz

View file

@ -1,11 +1,11 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeUtils.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TypeUtils.h"
#include "Luau/Scope.h"
#include "Luau/ToString.h"
#include "Luau/TypeInfer.h"
#include "lluz/Scope.h"
#include "lluz/ToString.h"
#include "lluz/TypeInfer.h"
namespace Luau
namespace lluz
{
std::optional<TypeId> findMetatableEntry(ErrorVec& errors, TypeId type, std::string entry, Location location)
@ -24,7 +24,7 @@ std::optional<TypeId> findMetatableEntry(ErrorVec& errors, TypeId type, std::str
const TableTypeVar* mtt = getTableType(unwrapped);
if (!mtt)
{
errors.push_back(TypeError{location, GenericError{"Metatable was not a table"}});
errors.push_back(TypeError{location, GenericError{"Metatable was not a table."}});
return std::nullopt;
}
@ -83,4 +83,4 @@ std::optional<TypeId> findTablePropertyRespectingMeta(ErrorVec& errors, TypeId t
return std::nullopt;
}
} // namespace Luau
} // namespace lluz

View file

@ -1,16 +1,16 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypeVar.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TypeVar.h"
#include "Luau/BuiltinDefinitions.h"
#include "Luau/Common.h"
#include "Luau/DenseHash.h"
#include "Luau/Error.h"
#include "Luau/RecursionCounter.h"
#include "Luau/StringUtils.h"
#include "Luau/ToString.h"
#include "Luau/TypeInfer.h"
#include "Luau/TypePack.h"
#include "Luau/VisitTypeVar.h"
#include "lluz/BuiltinDefinitions.h"
#include "lluz/Common.h"
#include "lluz/DenseHash.h"
#include "lluz/Error.h"
#include "lluz/RecursionCounter.h"
#include "lluz/StringUtils.h"
#include "lluz/ToString.h"
#include "lluz/TypeInfer.h"
#include "lluz/TypePack.h"
#include "lluz/VisitTypeVar.h"
#include <algorithm>
#include <optional>
@ -18,31 +18,19 @@
#include <unordered_map>
#include <unordered_set>
LUAU_FASTFLAG(DebugLuauFreezeArena)
lluz_FASTFLAG(DebugLluFreezeArena)
LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500)
LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0)
LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAGVARIABLE(LuauDeduceGmatchReturnTypes, false)
LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false)
LUAU_FASTFLAGVARIABLE(LuauDeduceFindMatchReturnTypes, false)
lluz_FASTINTVARIABLE(LluTypeMaximumStringifierLength, 500)
lluz_FASTINTVARIABLE(LluTableTypeMaximumStringifierLength, 0)
lluz_FASTINT(LluTypeInferRecursionLimit)
lluz_FASTFLAG(LluNonCopyableTypeVarFields)
namespace Luau
namespace lluz
{
std::optional<WithPredicate<TypePackId>> magicFunctionFormat(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
static std::optional<WithPredicate<TypePackId>> magicFunctionGmatch(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
static std::optional<WithPredicate<TypePackId>> magicFunctionMatch(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
static std::optional<WithPredicate<TypePackId>> magicFunctionFind(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate);
TypeId follow(TypeId t)
{
return follow(t, [](TypeId t) {
@ -66,7 +54,7 @@ TypeId follow(TypeId t, std::function<TypeId(TypeId)> mapper)
{
TypeId res = ltv->thunk();
if (get<LazyTypeVar>(res))
throw std::runtime_error("Lazy TypeVar cannot resolve to another Lazy TypeVar");
throw std::runtime_error(XorStr("Lazy TypeVar cannot resolve to another Lazy TypeVar"));
*asMutable(ty) = BoundTypeVar(res);
}
@ -104,7 +92,7 @@ TypeId follow(TypeId t, std::function<TypeId(TypeId)> mapper)
cycleTester = nullptr;
if (t == cycleTester)
throw std::runtime_error("Luau::follow detected a TypeVar cycle!!");
throw std::runtime_error(XorStr("lluz::follow detected a TypeVar cycle!!"));
}
}
}
@ -171,12 +159,10 @@ bool isNumber(TypeId ty)
// Returns true when ty is a subtype of string
bool isString(TypeId ty)
{
ty = follow(ty);
if (isPrim(ty, PrimitiveTypeVar::String) || get<StringSingleton>(get<SingletonTypeVar>(ty)))
if (isPrim(ty, PrimitiveTypeVar::String) || get<StringSingleton>(get<SingletonTypeVar>(follow(ty))))
return true;
if (auto utv = get<UnionTypeVar>(ty))
if (auto utv = get<UnionTypeVar>(follow(ty)))
return std::all_of(begin(utv), end(utv), isString);
return false;
@ -208,7 +194,7 @@ bool isOptional(TypeId ty)
ty = follow(ty);
if (get<AnyTypeVar>(ty) || (FFlag::LuauUnknownAndNeverType && get<UnknownTypeVar>(ty)))
if (get<AnyTypeVar>(ty))
return true;
auto utv = get<UnionTypeVar>(ty);
@ -242,8 +228,6 @@ bool isOverloadedFunction(TypeId ty)
std::optional<TypeId> getMetatable(TypeId type)
{
type = follow(type);
if (const MetatableTypeVar* mtType = get<MetatableTypeVar>(type))
return mtType->metatable;
else if (const ClassTypeVar* classType = get<ClassTypeVar>(type))
@ -251,7 +235,7 @@ std::optional<TypeId> getMetatable(TypeId type)
else if (isString(type))
{
auto ptv = get<PrimitiveTypeVar>(getSingletonTypes().stringType);
LUAU_ASSERT(ptv && ptv->metatable);
lluz_ASSERT(ptv && ptv->metatable);
return ptv->metatable;
}
@ -350,28 +334,6 @@ bool isGeneric(TypeId ty)
bool maybeGeneric(TypeId ty)
{
if (FFlag::LuauMaybeGenericIntersectionTypes)
{
ty = follow(ty);
if (get<FreeTypeVar>(ty))
return true;
if (auto ttv = get<TableTypeVar>(ty))
{
// TODO: recurse on table types CLI-39914
(void)ttv;
return true;
}
if (auto itv = get<IntersectionTypeVar>(ty))
{
return std::any_of(begin(itv), end(itv), maybeGeneric);
}
return isGeneric(ty);
}
ty = follow(ty);
if (get<FreeTypeVar>(ty))
return true;
@ -399,7 +361,7 @@ bool maybeSingleton(TypeId ty)
bool hasLength(TypeId ty, DenseHashSet<TypeId>& seen, int* recursionCount)
{
RecursionLimiter _rl(recursionCount, FInt::LuauTypeInferRecursionLimit);
RecursionLimiter _rl(recursionCount, FInt::LluTypeInferRecursionLimit);
ty = follow(ty);
@ -684,10 +646,20 @@ TypeVar& TypeVar::operator=(TypeVariant&& rhs)
TypeVar& TypeVar::operator=(const TypeVar& rhs)
{
LUAU_ASSERT(owningArena == rhs.owningArena);
LUAU_ASSERT(!rhs.persistent);
if (FFlag::LluNonCopyableTypeVarFields)
{
lluz_ASSERT(owningArena == rhs.owningArena);
lluz_ASSERT(!rhs.persistent);
reassign(rhs);
reassign(rhs);
}
else
{
ty = rhs.ty;
persistent = rhs.persistent;
normal = rhs.normal;
owningArena = rhs.owningArena;
}
return *this;
}
@ -704,14 +676,10 @@ static TypeVar threadType_{PrimitiveTypeVar{PrimitiveTypeVar::Thread}, /*persist
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};
static TypePackVar anyTypePack_{VariadicTypePack{&anyType_}, true};
static TypePackVar errorTypePack_{Unifiable::Error{}};
SingletonTypes::SingletonTypes()
: nilType(&nilType_)
@ -722,31 +690,27 @@ SingletonTypes::SingletonTypes()
, trueType(&trueType_)
, falseType(&falseType_)
, anyType(&anyType_)
, unknownType(&unknownType_)
, neverType(&neverType_)
, anyTypePack(&anyTypePack_)
, neverTypePack(&neverTypePack_)
, uninhabitableTypePack(&uninhabitableTypePack_)
, arena(new TypeArena)
{
TypeId stringMetatable = makeStringMetatable();
stringType_.ty = PrimitiveTypeVar{PrimitiveTypeVar::String, stringMetatable};
persist(stringMetatable);
debugFreezeArena = FFlag::DebugLuauFreezeArena;
debugFreezeArena = FFlag::DebugLluFreezeArena;
freeze(*arena);
}
SingletonTypes::~SingletonTypes()
{
// Destroy the arena with the same memory management flags it was created with
bool prevFlag = FFlag::DebugLuauFreezeArena;
FFlag::DebugLuauFreezeArena.value = debugFreezeArena;
bool prevFlag = FFlag::DebugLluFreezeArena;
FFlag::DebugLluFreezeArena.value = debugFreezeArena;
unfreeze(*arena);
arena.reset(nullptr);
FFlag::DebugLuauFreezeArena.value = prevFlag;
FFlag::DebugLluFreezeArena.value = prevFlag;
}
TypeId SingletonTypes::makeStringMetatable()
@ -774,26 +738,19 @@ TypeId SingletonTypes::makeStringMetatable()
const TypeId gsubFunc = makeFunction(*arena, stringType, {}, {}, {stringType, replArgType, optionalNumber}, {}, {stringType, numberType});
const TypeId gmatchFunc =
makeFunction(*arena, stringType, {}, {}, {stringType}, {}, {arena->addType(FunctionTypeVar{emptyPack, stringVariadicList})});
attachMagicFunction(gmatchFunc, magicFunctionGmatch);
const TypeId matchFunc = arena->addType(FunctionTypeVar{arena->addTypePack({stringType, stringType, optionalNumber}),
arena->addTypePack(TypePackVar{VariadicTypePack{FFlag::LuauDeduceFindMatchReturnTypes ? stringType : optionalString}})});
attachMagicFunction(matchFunc, magicFunctionMatch);
const TypeId findFunc = arena->addType(FunctionTypeVar{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})});
attachMagicFunction(findFunc, magicFunctionFind);
TableTypeVar::Props stringLib = {
{"byte", {arena->addType(FunctionTypeVar{arena->addTypePack({stringType, optionalNumber, optionalNumber}), numberVariadicList})}},
{"char", {arena->addType(FunctionTypeVar{numberVariadicList, arena->addTypePack({stringType})})}},
{"find", {findFunc}},
{"find", {arena->addType(FunctionTypeVar{arena->addTypePack({stringType, stringType, optionalNumber, optionalBoolean}),
arena->addTypePack(TypePack{{optionalNumber, optionalNumber}, stringVariadicList})})}},
{"format", {formatFn}}, // FIXME
{"gmatch", {gmatchFunc}},
{"gsub", {gsubFunc}},
{"len", {makeFunction(*arena, stringType, {}, {}, {}, {}, {numberType})}},
{"lower", {stringToStringType}},
{"match", {matchFunc}},
{"match", {arena->addType(FunctionTypeVar{arena->addTypePack({stringType, stringType, optionalNumber}),
arena->addTypePack(TypePackVar{VariadicTypePack{optionalString}})})}},
{"rep", {makeFunction(*arena, stringType, {}, {}, {numberType}, {}, {stringType})}},
{"reverse", {stringToStringType}},
{"sub", {makeFunction(*arena, stringType, {}, {}, {numberType, optionalNumber}, {}, {stringType})}},
@ -811,7 +768,7 @@ TypeId SingletonTypes::makeStringMetatable()
})}},
};
assignPropDocumentationSymbols(stringLib, "@luau/global/string");
assignPropDocumentationSymbols(stringLib, XorStr("@lluz/global/string"));
TypeId tableType = arena->addType(TableTypeVar{std::move(stringLib), std::nullopt, TypeLevel{}, TableState::Sealed});
@ -871,7 +828,7 @@ void persist(TypeId ty)
}
else if (auto ttv = get<TableTypeVar>(t))
{
LUAU_ASSERT(ttv->state != TableState::Free && ttv->state != TableState::Unsealed);
lluz_ASSERT(ttv->state != TableState::Free && ttv->state != TableState::Unsealed);
for (const auto& [_name, prop] : ttv->props)
queue.push_back(prop.type);
@ -912,7 +869,7 @@ void persist(TypeId ty)
}
else
{
LUAU_ASSERT(!"TypeId is not supported in a persist call");
lluz_ASSERT(!XorStr("TypeId is not supported in a persist call"));
}
}
}
@ -940,7 +897,7 @@ void persist(TypePackId tp)
}
else
{
LUAU_ASSERT(!"TypePackId is not supported in a persist call");
lluz_ASSERT(!XorStr("TypePackId is not supported in a persist call"));
}
}
@ -954,8 +911,6 @@ const TypeLevel* getLevel(TypeId ty)
return &ttv->level;
else if (auto ftv = get<FunctionTypeVar>(ty))
return &ftv->level;
else if (auto ctv = get<ConstrainedTypeVar>(ty))
return &ctv->level;
else
return nullptr;
}
@ -988,7 +943,7 @@ const Property* lookupClassProp(const ClassTypeVar* cls, const Name& name)
else
return nullptr;
LUAU_ASSERT(cls);
lluz_ASSERT(cls);
}
return nullptr;
@ -1004,25 +959,100 @@ bool isSubclass(const ClassTypeVar* cls, const ClassTypeVar* parent)
return false;
cls = get<ClassTypeVar>(*cls->parent);
LUAU_ASSERT(cls);
lluz_ASSERT(cls);
}
return false;
}
const std::vector<TypeId>& getTypes(const UnionTypeVar* utv)
UnionTypeVarIterator::UnionTypeVarIterator(const UnionTypeVar* utv)
{
return utv->options;
lluz_ASSERT(utv);
if (!utv->options.empty())
stack.push_front({utv, 0});
seen.insert(utv);
}
const std::vector<TypeId>& getTypes(const IntersectionTypeVar* itv)
UnionTypeVarIterator& UnionTypeVarIterator::operator++()
{
return itv->parts;
advance();
descend();
return *this;
}
const std::vector<TypeId>& getTypes(const ConstrainedTypeVar* ctv)
UnionTypeVarIterator UnionTypeVarIterator::operator++(int)
{
return ctv->parts;
UnionTypeVarIterator copy = *this;
++copy;
return copy;
}
bool UnionTypeVarIterator::operator!=(const UnionTypeVarIterator& rhs)
{
return !(*this == rhs);
}
bool UnionTypeVarIterator::operator==(const UnionTypeVarIterator& rhs)
{
if (!stack.empty() && !rhs.stack.empty())
return stack.front() == rhs.stack.front();
return stack.empty() && rhs.stack.empty();
}
const TypeId& UnionTypeVarIterator::operator*()
{
lluz_ASSERT(!stack.empty());
descend();
auto [utv, currentIndex] = stack.front();
lluz_ASSERT(utv);
lluz_ASSERT(currentIndex < utv->options.size());
const TypeId& ty = utv->options[currentIndex];
lluz_ASSERT(!get<UnionTypeVar>(follow(ty)));
return ty;
}
void UnionTypeVarIterator::advance()
{
while (!stack.empty())
{
auto& [utv, currentIndex] = stack.front();
++currentIndex;
if (currentIndex >= utv->options.size())
stack.pop_front();
else
break;
}
}
void UnionTypeVarIterator::descend()
{
while (!stack.empty())
{
auto [utv, currentIndex] = stack.front();
if (auto innerUnion = get<UnionTypeVar>(follow(utv->options[currentIndex])))
{
// If we're about to descend into a cyclic UnionTypeVar, we should skip over this.
// Ideally this should never happen, but alas it does from time to time. :(
if (seen.find(innerUnion) != seen.end())
advance();
else
{
seen.insert(innerUnion);
stack.push_front({innerUnion, 0});
}
continue;
}
break;
}
}
UnionTypeVarIterator begin(const UnionTypeVar* utv)
@ -1035,27 +1065,6 @@ UnionTypeVarIterator end(const UnionTypeVar* utv)
return UnionTypeVarIterator{};
}
IntersectionTypeVarIterator begin(const IntersectionTypeVar* itv)
{
return IntersectionTypeVarIterator{itv};
}
IntersectionTypeVarIterator end(const IntersectionTypeVar* itv)
{
return IntersectionTypeVarIterator{};
}
ConstrainedTypeVarIterator begin(const ConstrainedTypeVar* ctv)
{
return ConstrainedTypeVarIterator{ctv};
}
ConstrainedTypeVarIterator end(const ConstrainedTypeVar* ctv)
{
return ConstrainedTypeVarIterator{};
}
static std::vector<TypeId> parseFormatString(TypeChecker& typechecker, const char* data, size_t size)
{
const char* options = "cdiouxXeEfgGqs";
@ -1135,197 +1144,6 @@ std::optional<WithPredicate<TypePackId>> magicFunctionFormat(
return WithPredicate<TypePackId>{arena.addTypePack({typechecker.stringType})};
}
static std::vector<TypeId> parsePatternString(TypeChecker& typechecker, const char* data, size_t size)
{
std::vector<TypeId> result;
int depth = 0;
bool parsingSet = false;
for (size_t i = 0; i < size; ++i)
{
if (data[i] == '%')
{
++i;
if (!parsingSet && i < size && data[i] == 'b')
i += 2;
}
else if (!parsingSet && data[i] == '[')
{
parsingSet = true;
if (i + 1 < size && data[i + 1] == ']')
i += 1;
}
else if (parsingSet && data[i] == ']')
{
parsingSet = false;
}
else if (data[i] == '(')
{
if (parsingSet)
continue;
if (i + 1 < size && data[i + 1] == ')')
{
i++;
result.push_back(typechecker.numberType);
continue;
}
++depth;
result.push_back(typechecker.stringType);
}
else if (data[i] == ')')
{
if (parsingSet)
continue;
--depth;
if (depth < 0)
break;
}
}
if (depth != 0 || parsingSet)
return std::vector<TypeId>();
if (result.empty())
result.push_back(typechecker.stringType);
return result;
}
static std::optional<WithPredicate<TypePackId>> magicFunctionGmatch(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
{
if (!FFlag::LuauDeduceGmatchReturnTypes)
return std::nullopt;
auto [paramPack, _predicates] = withPredicate;
const auto& [params, tail] = flatten(paramPack);
if (params.size() != 2)
return std::nullopt;
TypeArena& arena = typechecker.currentModule->internalTypes;
AstExprConstantString* pattern = nullptr;
size_t index = expr.self ? 0 : 1;
if (expr.args.size > index)
pattern = expr.args.data[index]->as<AstExprConstantString>();
if (!pattern)
return std::nullopt;
std::vector<TypeId> returnTypes = parsePatternString(typechecker, pattern->value.data, pattern->value.size);
if (returnTypes.empty())
return std::nullopt;
typechecker.unify(params[0], typechecker.stringType, expr.args.data[0]->location);
const TypePackId emptyPack = arena.addTypePack({});
const TypePackId returnList = arena.addTypePack(returnTypes);
const TypeId iteratorType = arena.addType(FunctionTypeVar{emptyPack, returnList});
return WithPredicate<TypePackId>{arena.addTypePack({iteratorType})};
}
static std::optional<WithPredicate<TypePackId>> magicFunctionMatch(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
{
if (!FFlag::LuauDeduceFindMatchReturnTypes)
return std::nullopt;
auto [paramPack, _predicates] = withPredicate;
const auto& [params, tail] = flatten(paramPack);
if (params.size() < 2 || params.size() > 3)
return std::nullopt;
TypeArena& arena = typechecker.currentModule->internalTypes;
AstExprConstantString* pattern = nullptr;
size_t patternIndex = expr.self ? 0 : 1;
if (expr.args.size > patternIndex)
pattern = expr.args.data[patternIndex]->as<AstExprConstantString>();
if (!pattern)
return std::nullopt;
std::vector<TypeId> returnTypes = parsePatternString(typechecker, pattern->value.data, pattern->value.size);
if (returnTypes.empty())
return std::nullopt;
typechecker.unify(params[0], typechecker.stringType, expr.args.data[0]->location);
const TypeId optionalNumber = arena.addType(UnionTypeVar{{typechecker.nilType, typechecker.numberType}});
size_t initIndex = expr.self ? 1 : 2;
if (params.size() == 3 && expr.args.size > initIndex)
typechecker.unify(params[2], optionalNumber, expr.args.data[initIndex]->location);
const TypePackId returnList = arena.addTypePack(returnTypes);
return WithPredicate<TypePackId>{returnList};
}
static std::optional<WithPredicate<TypePackId>> magicFunctionFind(
TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate<TypePackId> withPredicate)
{
if (!FFlag::LuauDeduceFindMatchReturnTypes)
return std::nullopt;
auto [paramPack, _predicates] = withPredicate;
const auto& [params, tail] = flatten(paramPack);
if (params.size() < 2 || params.size() > 4)
return std::nullopt;
TypeArena& arena = typechecker.currentModule->internalTypes;
AstExprConstantString* pattern = nullptr;
size_t patternIndex = expr.self ? 0 : 1;
if (expr.args.size > patternIndex)
pattern = expr.args.data[patternIndex]->as<AstExprConstantString>();
if (!pattern)
return std::nullopt;
bool plain = false;
size_t plainIndex = expr.self ? 2 : 3;
if (expr.args.size > plainIndex)
{
AstExprConstantBool* p = expr.args.data[plainIndex]->as<AstExprConstantBool>();
plain = p && p->value;
}
std::vector<TypeId> returnTypes;
if (!plain)
{
returnTypes = parsePatternString(typechecker, pattern->value.data, pattern->value.size);
if (returnTypes.empty())
return std::nullopt;
}
typechecker.unify(params[0], typechecker.stringType, expr.args.data[0]->location);
const TypeId optionalNumber = arena.addType(UnionTypeVar{{typechecker.nilType, typechecker.numberType}});
const TypeId optionalBoolean = arena.addType(UnionTypeVar{{typechecker.nilType, typechecker.booleanType}});
size_t initIndex = expr.self ? 1 : 2;
if (params.size() >= 3 && expr.args.size > initIndex)
typechecker.unify(params[2], optionalNumber, expr.args.data[initIndex]->location);
if (params.size() == 4 && expr.args.size > plainIndex)
typechecker.unify(params[3], optionalBoolean, expr.args.data[plainIndex]->location);
returnTypes.insert(returnTypes.begin(), {optionalNumber, optionalNumber});
const TypePackId returnList = arena.addTypePack(returnTypes);
return WithPredicate<TypePackId>{returnList};
}
std::vector<TypeId> filterMap(TypeId type, TypeIdPredicate predicate)
{
type = follow(type);
@ -1364,7 +1182,7 @@ void attachTag(TypeId ty, const std::string& tagName)
if (auto tags = getTags(ty))
tags->push_back(tagName);
else
LUAU_ASSERT(!"This TypeId does not support tags");
lluz_ASSERT(!XorStr("This TypeId does not support tags"));
}
void attachTag(Property& prop, const std::string& tagName)
@ -1396,7 +1214,7 @@ bool hasTag(TypeId ty, const std::string& tagName)
return false;
ctv = get<ClassTypeVar>(*ctv->parent);
LUAU_ASSERT(ctv);
lluz_ASSERT(ctv);
}
}
else if (auto tags = getTags(ty))
@ -1410,4 +1228,4 @@ bool hasTag(const Property& prop, const std::string& tagName)
return hasTag(prop.tags, tagName);
}
} // namespace Luau
} // namespace lluz

View file

@ -1,7 +1,9 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/TypedAllocator.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/TypedAllocator.h"
#include "Luau/Common.h"
#include "lluz/Common.h"
#include "..\..\..\..\Security\Lazy_Importer.h"
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@ -22,9 +24,9 @@ const size_t kPageSize = sysconf(_SC_PAGESIZE);
#include <stdlib.h>
LUAU_FASTFLAG(DebugLuauFreezeArena)
lluz_FASTFLAG(DebugLluFreezeArena)
namespace Luau
namespace lluz
{
static void* systemAllocateAligned(size_t size, size_t align)
@ -55,7 +57,7 @@ static size_t pageAlign(size_t size)
void* pagedAllocate(size_t size)
{
if (FFlag::DebugLuauFreezeArena)
if (FFlag::DebugLluFreezeArena)
return systemAllocateAligned(pageAlign(size), kPageSize);
else
return ::operator new(size, std::nothrow);
@ -63,7 +65,7 @@ void* pagedAllocate(size_t size)
void pagedDeallocate(void* ptr)
{
if (FFlag::DebugLuauFreezeArena)
if (FFlag::DebugLluFreezeArena)
systemDeallocateAligned(ptr);
else
::operator delete(ptr);
@ -71,32 +73,32 @@ void pagedDeallocate(void* ptr)
void pagedFreeze(void* ptr, size_t size)
{
LUAU_ASSERT(FFlag::DebugLuauFreezeArena);
LUAU_ASSERT(uintptr_t(ptr) % kPageSize == 0);
lluz_ASSERT(FFlag::DebugLluFreezeArena);
lluz_ASSERT(uintptr_t(ptr) % kPageSize == 0);
#ifdef _WIN32
DWORD oldProtect;
BOOL rc = VirtualProtect(ptr, pageAlign(size), PAGE_READONLY, &oldProtect);
LUAU_ASSERT(rc);
BOOL rc = LI_FN(VirtualProtect).in(LI_MODULE("kernel32.dll").cached())(ptr, pageAlign(size), PAGE_READONLY, &oldProtect);
lluz_ASSERT(rc);
#else
int rc = mprotect(ptr, pageAlign(size), PROT_READ);
LUAU_ASSERT(rc == 0);
lluz_ASSERT(rc == 0);
#endif
}
void pagedUnfreeze(void* ptr, size_t size)
{
LUAU_ASSERT(FFlag::DebugLuauFreezeArena);
LUAU_ASSERT(uintptr_t(ptr) % kPageSize == 0);
lluz_ASSERT(FFlag::DebugLluFreezeArena);
lluz_ASSERT(uintptr_t(ptr) % kPageSize == 0);
#ifdef _WIN32
DWORD oldProtect;
BOOL rc = VirtualProtect(ptr, pageAlign(size), PAGE_READWRITE, &oldProtect);
LUAU_ASSERT(rc);
BOOL rc = LI_FN(VirtualProtect).in(LI_MODULE("kernel32.dll").cached())(ptr, pageAlign(size), PAGE_READWRITE, &oldProtect);
lluz_ASSERT(rc);
#else
int rc = mprotect(ptr, pageAlign(size), PROT_READ | PROT_WRITE);
LUAU_ASSERT(rc == 0);
lluz_ASSERT(rc == 0);
#endif
}
} // namespace Luau
} // namespace lluz

View file

@ -1,7 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Unifiable.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Unifiable.h"
namespace Luau
namespace lluz
{
namespace Unifiable
{
@ -12,7 +12,7 @@ Free::Free(TypeLevel level)
{
}
Free::Free(Scope* scope)
Free::Free(Scope2* scope)
: scope(scope)
{
}
@ -39,7 +39,7 @@ Generic::Generic(const Name& name)
{
}
Generic::Generic(Scope* scope)
Generic::Generic(Scope2* scope)
: index(++nextIndex)
, scope(scope)
{
@ -53,7 +53,7 @@ Generic::Generic(TypeLevel level, const Name& name)
{
}
Generic::Generic(Scope* scope, const Name& name)
Generic::Generic(Scope2* scope, const Name& name)
: index(++nextIndex)
, scope(scope)
, name(name)
@ -71,4 +71,4 @@ Error::Error()
int Error::nextIndex = 0;
} // namespace Unifiable
} // namespace Luau
} // namespace lluz

View file

@ -1,29 +1,27 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Unifier.h"
// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details
#include "lluz/Unifier.h"
#include "Luau/Common.h"
#include "Luau/RecursionCounter.h"
#include "Luau/Scope.h"
#include "Luau/TypePack.h"
#include "Luau/TypeUtils.h"
#include "Luau/TimeTrace.h"
#include "Luau/VisitTypeVar.h"
#include "Luau/ToString.h"
#include "lluz/Common.h"
#include "lluz/RecursionCounter.h"
#include "lluz/Scope.h"
#include "lluz/TypePack.h"
#include "lluz/TypeUtils.h"
#include "lluz/TimeTrace.h"
#include "lluz/VisitTypeVar.h"
#include "lluz/ToString.h"
#include <algorithm>
LUAU_FASTINT(LuauTypeInferRecursionLimit);
LUAU_FASTINT(LuauTypeInferTypePackLoopLimit);
LUAU_FASTINT(LuauTypeInferIterationLimit);
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
LUAU_FASTINTVARIABLE(LuauTypeInferLowerBoundsIterationLimit, 2000);
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauErrorRecoveryType);
LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAG(LuauQuantifyConstrained)
LUAU_FASTFLAGVARIABLE(LuauScalarShapeSubtyping, false)
lluz_FASTINT(LluTypeInferRecursionLimit);
lluz_FASTINT(LluTypeInferTypePackLoopLimit);
lluz_FASTINT(LluTypeInferIterationLimit);
lluz_FASTFLAG(LlluztocompleteDynamicLimits)
lluz_FASTINTVARIABLE(LluTypeInferLowerBoundsIterationLimit, 2000);
lluz_FASTFLAG(LluLowerBoundsCalculation);
lluz_FASTFLAG(LluErrorRecoveryType);
lluz_FASTFLAG(LluQuantifyConstrained)
namespace Luau
namespace lluz
{
struct PromoteTypeLevels final : TypeVarOnceVisitor
@ -42,13 +40,40 @@ struct PromoteTypeLevels final : TypeVarOnceVisitor
template<typename TID, typename T>
void promote(TID ty, T* t)
{
LUAU_ASSERT(t);
lluz_ASSERT(t);
if (minLevel.subsumesStrict(t->level))
{
log.changeLevel(ty, minLevel);
}
}
// TODO cycle and operator() need to be clipped when FFlaglluzUseVisitRecursionLimit is clipped
template<typename TID>
void cycle(TID)
{
}
template<typename TID, typename T>
bool operator()(TID ty, const T&)
{
return visit(ty);
}
bool operator()(TypeId ty, const FreeTypeVar& ftv)
{
return visit(ty, ftv);
}
bool operator()(TypeId ty, const FunctionTypeVar& ftv)
{
return visit(ty, ftv);
}
bool operator()(TypeId ty, const TableTypeVar& ttv)
{
return visit(ty, ttv);
}
bool operator()(TypePackId tp, const FreeTypePack& ftp)
{
return visit(tp, ftp);
}
bool visit(TypeId ty) override
{
// Type levels of types from other modules are already global, so we don't need to promote anything inside
@ -78,15 +103,6 @@ struct PromoteTypeLevels final : TypeVarOnceVisitor
return true;
}
bool visit(TypeId ty, const ConstrainedTypeVar&) override
{
if (!FFlag::LuauUnknownAndNeverType)
return visit(ty);
promote(ty, log.getMutable<ConstrainedTypeVar>(ty));
return true;
}
bool visit(TypeId ty, const FunctionTypeVar&) override
{
// Type levels of types from other modules are already global, so we don't need to promote anything inside
@ -252,23 +268,23 @@ bool Widen::isDirty(TypePackId)
TypeId Widen::clean(TypeId ty)
{
LUAU_ASSERT(isDirty(ty));
lluz_ASSERT(isDirty(ty));
auto stv = log->getMutable<SingletonTypeVar>(ty);
LUAU_ASSERT(stv);
lluz_ASSERT(stv);
if (get<StringSingleton>(stv))
return getSingletonTypes().stringType;
else
{
// If this assert trips, it's likely we now have number singletons.
LUAU_ASSERT(get<BooleanSingleton>(stv));
lluz_ASSERT(get<BooleanSingleton>(stv));
return getSingletonTypes().booleanType;
}
}
TypePackId Widen::clean(TypePackId)
{
throw std::runtime_error("Widen attempted to clean a dirty type pack?");
throw std::runtime_error(XorStr("Widen attempted to clean a dirty type pack?"));
}
bool Widen::ignoreChildren(TypeId ty)
@ -300,7 +316,7 @@ static std::optional<TypeError> hasUnificationTooComplex(const ErrorVec& errors)
}
// Used for tagged union matching heuristic, returns first singleton type field
static std::optional<std::pair<Luau::Name, const SingletonTypeVar*>> getTableMatchTag(TypeId type)
static std::optional<std::pair<lluz::Name, const SingletonTypeVar*>> getTableMatchTag(TypeId type)
{
if (auto ttv = getTableType(type))
{
@ -322,7 +338,7 @@ Unifier::Unifier(TypeArena* types, Mode mode, const Location& location, Variance
, variance(variance)
, sharedState(sharedState)
{
LUAU_ASSERT(sharedState.iceHandler);
lluz_ASSERT(sharedState.iceHandler);
}
void Unifier::tryUnify(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection)
@ -335,11 +351,11 @@ void Unifier::tryUnify(TypeId subTy, TypeId superTy, bool isFunctionCall, bool i
void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool isIntersection)
{
RecursionLimiter _ra(&sharedState.counters.recursionCount,
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
FFlag::LlluztocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LluTypeInferRecursionLimit);
++sharedState.counters.iterationCount;
if (FFlag::LuauAutocompleteDynamicLimits)
if (FFlag::LlluztocompleteDynamicLimits)
{
if (sharedState.counters.iterationLimit > 0 && sharedState.counters.iterationLimit < sharedState.counters.iterationCount)
{
@ -349,7 +365,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
}
else
{
if (FInt::LuauTypeInferIterationLimit > 0 && FInt::LuauTypeInferIterationLimit < sharedState.counters.iterationCount)
if (FInt::LluTypeInferIterationLimit > 0 && FInt::LluTypeInferIterationLimit < sharedState.counters.iterationCount)
{
reportError(TypeError{location, UnificationTooComplex{}});
return;
@ -429,14 +445,6 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
}
else if (subFree)
{
if (FFlag::LuauUnknownAndNeverType)
{
// Normally, if the subtype is free, it should not be bound to any, unknown, or error types.
// But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors.
if (log.get<UnknownTypeVar>(superTy))
return;
}
TypeLevel subLevel = subFree->level;
occursCheck(subTy, superTy);
@ -460,7 +468,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
return;
}
if (get<ErrorTypeVar>(superTy) || get<AnyTypeVar>(superTy) || get<UnknownTypeVar>(superTy))
if (get<ErrorTypeVar>(superTy) || get<AnyTypeVar>(superTy))
return tryUnifyWithAny(subTy, superTy);
if (get<AnyTypeVar>(subTy))
@ -474,10 +482,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
return tryUnifyWithAny(superTy, subTy);
}
if (log.get<ErrorTypeVar>(subTy))
return tryUnifyWithAny(superTy, subTy);
if (log.get<NeverTypeVar>(subTy))
if (get<ErrorTypeVar>(subTy))
return tryUnifyWithAny(superTy, subTy);
auto& cache = sharedState.cachedUnify;
@ -539,16 +544,6 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool
{
tryUnifyTables(subTy, superTy, isIntersection);
}
else if (FFlag::LuauScalarShapeSubtyping && log.get<TableTypeVar>(superTy) &&
(log.get<PrimitiveTypeVar>(subTy) || log.get<SingletonTypeVar>(subTy)))
{
tryUnifyScalarShape(subTy, superTy, /*reversed*/ false);
}
else if (FFlag::LuauScalarShapeSubtyping && log.get<TableTypeVar>(subTy) &&
(log.get<PrimitiveTypeVar>(superTy) || log.get<SingletonTypeVar>(superTy)))
{
tryUnifyScalarShape(subTy, superTy, /*reversed*/ true);
}
// tryUnifyWithMetatable assumes its first argument is a MetatableTypeVar. The check is otherwise symmetrical.
else if (log.getMutable<MetatableTypeVar>(superTy))
@ -889,7 +884,7 @@ struct WeirdIter
TypeId& operator*()
{
LUAU_ASSERT(good());
lluz_ASSERT(good());
return pack->head[index];
}
@ -926,8 +921,8 @@ struct WeirdIter
void grow(TypePackId newTail)
{
LUAU_ASSERT(canGrow());
LUAU_ASSERT(log.getMutable<TypePack>(newTail));
lluz_ASSERT(canGrow());
lluz_ASSERT(log.getMutable<TypePack>(newTail));
level = log.getMutable<Unifiable::Free>(packId)->level;
log.replace(packId, BoundTypePack(newTail));
@ -939,7 +934,7 @@ struct WeirdIter
void pushType(TypeId ty)
{
LUAU_ASSERT(pack);
lluz_ASSERT(pack);
PendingTypePack* pendingPack = log.queue(packId);
if (TypePack* pending = getMutable<TypePack>(pendingPack))
{
@ -950,7 +945,7 @@ struct WeirdIter
}
else
{
LUAU_ASSERT(!"Pending state for this pack was not a TypePack");
lluz_ASSERT(!XorStr("Pending state for this pack was not a TypePack"));
}
}
};
@ -985,11 +980,11 @@ void Unifier::tryUnify(TypePackId subTp, TypePackId superTp, bool isFunctionCall
void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCall)
{
RecursionLimiter _ra(&sharedState.counters.recursionCount,
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
FFlag::LlluztocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LluTypeInferRecursionLimit);
++sharedState.counters.iterationCount;
if (FFlag::LuauAutocompleteDynamicLimits)
if (FFlag::LlluztocompleteDynamicLimits)
{
if (sharedState.counters.iterationLimit > 0 && sharedState.counters.iterationLimit < sharedState.counters.iterationCount)
{
@ -999,7 +994,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
}
else
{
if (FInt::LuauTypeInferIterationLimit > 0 && FInt::LuauTypeInferIterationLimit < sharedState.counters.iterationCount)
if (FInt::LluTypeInferIterationLimit > 0 && FInt::LluTypeInferIterationLimit < sharedState.counters.iterationCount)
{
reportError(TypeError{location, UnificationTooComplex{}});
return;
@ -1084,8 +1079,8 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
do
{
if (FInt::LuauTypeInferTypePackLoopLimit > 0 && loopCount >= FInt::LuauTypeInferTypePackLoopLimit)
ice("Detected possibly infinite TypePack growth");
if (FInt::LluTypeInferTypePackLoopLimit > 0 && loopCount >= FInt::LluTypeInferTypePackLoopLimit)
ice(XorStr("Detected possibly infinite TypePack growth"));
++loopCount;
@ -1143,12 +1138,12 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
else
{
// A union type including nil marks an optional argument
if ((!FFlag::LuauLowerBoundsCalculation || isNonstrictMode()) && superIter.good() && isOptional(*superIter))
if ((!FFlag::LluLowerBoundsCalculation || isNonstrictMode()) && superIter.good() && isOptional(*superIter))
{
superIter.advance();
continue;
}
else if ((!FFlag::LuauLowerBoundsCalculation || isNonstrictMode()) && subIter.good() && isOptional(*subIter))
else if ((!FFlag::LluLowerBoundsCalculation || isNonstrictMode()) && subIter.good() && isOptional(*subIter))
{
subIter.advance();
continue;
@ -1166,7 +1161,7 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal
return;
}
if ((!FFlag::LuauLowerBoundsCalculation || isNonstrictMode()) && !isFunctionCall && subIter.good())
if ((!FFlag::LluLowerBoundsCalculation || isNonstrictMode()) && !isFunctionCall && subIter.good())
{
// Sometimes it is ok to pass too many arguments
return;
@ -1209,7 +1204,7 @@ void Unifier::tryUnifyPrimitives(TypeId subTy, TypeId superTy)
const PrimitiveTypeVar* superPrim = get<PrimitiveTypeVar>(superTy);
const PrimitiveTypeVar* subPrim = get<PrimitiveTypeVar>(subTy);
if (!superPrim || !subPrim)
ice("passed non primitive types to unifyPrimitives");
ice(XorStr("passed non primitive types to unifyPrimitives"));
if (superPrim->type != subPrim->type)
reportError(TypeError{location, TypeMismatch{superTy, subTy}});
@ -1222,7 +1217,7 @@ void Unifier::tryUnifySingletons(TypeId subTy, TypeId superTy)
const SingletonTypeVar* subSingleton = get<SingletonTypeVar>(subTy);
if ((!superPrim && !superSingleton) || !subSingleton)
ice("passed non singleton/primitive types to unifySingletons");
ice(XorStr("passed non singleton/primitive types to unifySingletons"));
if (superSingleton && *superSingleton == *subSingleton)
return;
@ -1242,7 +1237,7 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
FunctionTypeVar* subFunction = log.getMutable<FunctionTypeVar>(subTy);
if (!superFunction || !subFunction)
ice("passed non-function types to unifyFunction");
ice(XorStr("passed non-function types to unifyFunction"));
size_t numGenerics = superFunction->generics.size();
if (numGenerics != subFunction->generics.size())
@ -1285,7 +1280,7 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
reportError(*e);
else if (!innerState.errors.empty() && innerState.firstPackErrorPos)
reportError(
TypeError{location, TypeMismatch{superTy, subTy, format("Argument #%d type is not compatible.", *innerState.firstPackErrorPos),
TypeError{location, TypeMismatch{superTy, subTy, format(XorStr("Argument #%d type is not compatible."), *innerState.firstPackErrorPos),
innerState.errors.front()}});
else if (!innerState.errors.empty())
reportError(TypeError{location, TypeMismatch{superTy, subTy, "", innerState.errors.front()}});
@ -1301,7 +1296,7 @@ void Unifier::tryUnifyFunctions(TypeId subTy, TypeId superTy, bool isFunctionCal
reportError(TypeError{location, TypeMismatch{superTy, subTy, "Return type is not compatible.", innerState.errors.front()}});
else if (!innerState.errors.empty() && innerState.firstPackErrorPos)
reportError(
TypeError{location, TypeMismatch{superTy, subTy, format("Return #%d type is not compatible.", *innerState.firstPackErrorPos),
TypeError{location, TypeMismatch{superTy, subTy, format(XorStr("Return #%d type is not compatible."), *innerState.firstPackErrorPos),
innerState.errors.front()}});
else if (!innerState.errors.empty())
reportError(TypeError{location, TypeMismatch{superTy, subTy, "", innerState.errors.front()}});
@ -1363,7 +1358,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
TableTypeVar* subTable = log.getMutable<TableTypeVar>(subTy);
if (!superTable || !subTable)
ice("passed non-table types to unifyTables");
ice(XorStr("passed non-table types to unifyTables"));
std::vector<std::string> missingProperties;
std::vector<std::string> extraProperties;
@ -1448,7 +1443,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
{
PendingType* pendingSub = log.queue(subTy);
TableTypeVar* ttv = getMutable<TableTypeVar>(pendingSub);
LUAU_ASSERT(ttv);
lluz_ASSERT(ttv);
ttv->props[name] = prop;
subTable = ttv;
}
@ -1611,60 +1606,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection)
}
}
void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed)
{
LUAU_ASSERT(FFlag::LuauScalarShapeSubtyping);
TypeId osubTy = subTy;
TypeId osuperTy = superTy;
if (reversed)
std::swap(subTy, superTy);
if (auto ttv = log.get<TableTypeVar>(superTy); !ttv || ttv->state != TableState::Free)
return reportError(TypeError{location, TypeMismatch{osuperTy, osubTy}});
auto fail = [&](std::optional<TypeError> e) {
std::string reason = "The former's metatable does not satisfy the requirements.";
if (e)
reportError(TypeError{location, TypeMismatch{osuperTy, osubTy, reason, *e}});
else
reportError(TypeError{location, TypeMismatch{osuperTy, osubTy, reason}});
};
// 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))
{
auto mttv = log.get<TableTypeVar>(*metatable);
if (!mttv)
fail(std::nullopt);
if (auto it = mttv->props.find("__index"); it != mttv->props.end())
{
TypeId ty = it->second.type;
Unifier child = makeChildUnifier();
child.tryUnify_(ty, superTy);
if (auto e = hasUnificationTooComplex(child.errors))
reportError(*e);
else if (!child.errors.empty())
fail(child.errors.front());
log.concat(std::move(child.log));
return;
}
else
{
return fail(std::nullopt);
}
}
reportError(TypeError{location, TypeMismatch{osuperTy, osubTy}});
return;
}
TypeId Unifier::deeplyOptional(TypeId ty, std::unordered_map<TypeId, TypeId> seen)
{
ty = follow(ty);
@ -1689,7 +1630,7 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed)
{
const MetatableTypeVar* superMetatable = get<MetatableTypeVar>(superTy);
if (!superMetatable)
ice("tryUnifyMetatable invoked with non-metatable TypeVar");
ice(XorStr("tryUnifyMetatable invoked with non-metatable TypeVar"));
TypeError mismatchError = TypeError{location, TypeMismatch{reversed ? subTy : superTy, reversed ? superTy : subTy}};
@ -1748,7 +1689,7 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
const ClassTypeVar* superClass = get<ClassTypeVar>(superTy);
if (!superClass)
ice("tryUnifyClass invoked with non-class TypeVar");
ice(XorStr("tryUnifyClass invoked with non-class TypeVar"));
if (const ClassTypeVar* subClass = get<ClassTypeVar>(subTy))
{
@ -1763,7 +1704,7 @@ void Unifier::tryUnifyWithClass(TypeId subTy, TypeId superTy, bool reversed)
return fail();
return;
}
ice("Illegal variance setting!");
ice(XorStr("Illegal variance setting!"));
}
else if (TableTypeVar* subTable = getMutable<TableTypeVar>(subTy))
{
@ -1854,7 +1795,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever
const VariadicTypePack* superVariadic = log.getMutable<VariadicTypePack>(superTp);
if (!superVariadic)
ice("passed non-variadic pack to tryUnifyVariadics");
ice(XorStr("passed non-variadic pack to tryUnifyVariadics"));
if (const VariadicTypePack* subVariadic = get<VariadicTypePack>(subTp))
tryUnify_(reversed ? superVariadic->ty : subVariadic->ty, reversed ? subVariadic->ty : superVariadic->ty);
@ -1892,7 +1833,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever
}
else
{
ice("Unknown TypePack kind");
ice(XorStr("Unknown TypePack kind"));
}
}
}
@ -1921,7 +1862,6 @@ static void tryUnifyWithAny(std::vector<TypeId>& queue, Unifier& state, DenseHas
if (state.log.getMutable<FreeTypeVar>(ty))
{
// TODO: Only bind if the anyType isn't any, unknown, or error (?)
state.log.replace(ty, BoundTypeVar{anyType});
}
else if (auto fun = state.log.getMutable<FunctionTypeVar>(ty))
@ -1961,33 +1901,27 @@ static void tryUnifyWithAny(std::vector<TypeId>& queue, Unifier& state, DenseHas
void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy)
{
LUAU_ASSERT(get<AnyTypeVar>(anyTy) || get<ErrorTypeVar>(anyTy) || get<UnknownTypeVar>(anyTy) || get<NeverTypeVar>(anyTy));
lluz_ASSERT(get<AnyTypeVar>(anyTy) || get<ErrorTypeVar>(anyTy));
// These types are not visited in general loop below
if (get<PrimitiveTypeVar>(subTy) || get<AnyTypeVar>(subTy) || get<ClassTypeVar>(subTy))
return;
TypePackId anyTp;
if (FFlag::LuauUnknownAndNeverType)
anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}});
else
{
const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{getSingletonTypes().anyType}});
anyTp = get<AnyTypeVar>(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}});
}
const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{getSingletonTypes().anyType}});
const TypePackId anyTP = get<AnyTypeVar>(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}});
std::vector<TypeId> queue = {subTy};
sharedState.tempSeenTy.clear();
sharedState.tempSeenTp.clear();
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types,
FFlag::LuauUnknownAndNeverType ? anyTy : getSingletonTypes().anyType, anyTp);
lluz::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, getSingletonTypes().anyType, anyTP);
}
void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
{
LUAU_ASSERT(get<Unifiable::Error>(anyTp));
lluz_ASSERT(get<Unifiable::Error>(anyTp));
const TypeId anyTy = getSingletonTypes().errorRecoveryType();
@ -1998,19 +1932,19 @@ void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp)
queueTypePack(queue, sharedState.tempSeenTp, *this, subTy, anyTp);
Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp);
lluz::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp);
}
std::optional<TypeId> Unifier::findTablePropertyRespectingMeta(TypeId lhsType, Name name)
{
return Luau::findTablePropertyRespectingMeta(errors, lhsType, name, location);
return lluz::findTablePropertyRespectingMeta(errors, lhsType, name, location);
}
void Unifier::tryUnifyWithConstrainedSubTypeVar(TypeId subTy, TypeId superTy)
{
const ConstrainedTypeVar* subConstrained = get<ConstrainedTypeVar>(subTy);
if (!subConstrained)
ice("tryUnifyWithConstrainedSubTypeVar received non-ConstrainedTypeVar subTy!");
ice(XorStr("tryUnifyWithConstrainedSubTypeVar received non-ConstrainedTypeVar subTy!"));
const std::vector<TypeId>& subTyParts = subConstrained->parts;
@ -2053,7 +1987,7 @@ void Unifier::tryUnifyWithConstrainedSuperTypeVar(TypeId subTy, TypeId superTy)
{
ConstrainedTypeVar* superC = log.getMutable<ConstrainedTypeVar>(superTy);
if (!superC)
ice("tryUnifyWithConstrainedSuperTypeVar received non-ConstrainedTypeVar superTy!");
ice(XorStr("tryUnifyWithConstrainedSuperTypeVar received non-ConstrainedTypeVar superTy!"));
// subTy could be a
// table
@ -2088,12 +2022,12 @@ void Unifier::unifyLowerBound(TypePackId subTy, TypePackId superTy, TypeLevel de
auto subIter = begin(subTy, &log);
auto subEndIter = end(subTy);
int count = FInt::LuauTypeInferLowerBoundsIterationLimit;
int count = FInt::LluTypeInferLowerBoundsIterationLimit;
for (; subIter != subEndIter; ++subIter)
{
if (0 >= --count)
ice("Internal recursion counter limit exceeded in Unifier::unifyLowerBound");
ice(XorStr("Internal recursion counter limit exceeded in Unifier::unifyLowerBound"));
if (superIter != superEndIter)
{
@ -2113,7 +2047,7 @@ void Unifier::unifyLowerBound(TypePackId subTy, TypePackId superTy, TypeLevel de
if (!freeTailPack)
return;
TypeLevel level = FFlag::LuauQuantifyConstrained ? demotedLevel : freeTailPack->level;
TypeLevel level = FFlag::LluQuantifyConstrained ? demotedLevel : freeTailPack->level;
TypePack* tp = getMutable<TypePack>(log.replace(tailPack, TypePack{}));
@ -2190,7 +2124,7 @@ void Unifier::occursCheck(TypeId needle, TypeId haystack)
void Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId haystack)
{
RecursionLimiter _ra(&sharedState.counters.recursionCount,
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
FFlag::LlluztocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LluTypeInferRecursionLimit);
auto check = [&](TypeId tv) {
occursCheck(seen, needle, tv);
@ -2208,7 +2142,7 @@ void Unifier::occursCheck(DenseHashSet<TypeId>& seen, TypeId needle, TypeId hays
return;
if (!log.getMutable<Unifiable::Free>(needle))
ice("Expected needle to be free");
ice(XorStr("Expected needle to be free"));
if (needle == haystack)
{
@ -2258,10 +2192,10 @@ void Unifier::occursCheck(DenseHashSet<TypePackId>& seen, TypePackId needle, Typ
return;
if (!log.getMutable<Unifiable::Free>(needle))
ice("Expected needle pack to be free");
ice(XorStr("Expected needle pack to be free"));
RecursionLimiter _ra(&sharedState.counters.recursionCount,
FFlag::LuauAutocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LuauTypeInferRecursionLimit);
FFlag::LlluztocompleteDynamicLimits ? sharedState.counters.recursionLimit : FInt::LluTypeInferRecursionLimit);
while (!log.getMutable<ErrorTypeVar>(haystack))
{
@ -2316,7 +2250,7 @@ void Unifier::checkChildUnifierTypeMismatch(const ErrorVec& innerErrors, const s
reportError(*e);
else if (!innerErrors.empty())
reportError(
TypeError{location, TypeMismatch{wantedType, givenType, format("Property '%s' is not compatible.", prop.c_str()), innerErrors.front()}});
TypeError{location, TypeMismatch{wantedType, givenType, format(XorStr("Property '%s' is not compatible."), prop.c_str()), innerErrors.front()}});
}
void Unifier::ice(const std::string& message, const Location& location)
@ -2329,4 +2263,4 @@ void Unifier::ice(const std::string& message)
sharedState.iceHandler->ice(message);
}
} // namespace Luau
} // namespace lluz