diff --git a/Analysis/include/lluz/AstQuery.h b/Analysis/include/lluz/AstQuery.h new file mode 100644 index 00000000..409c2fea --- /dev/null +++ b/Analysis/include/lluz/AstQuery.h @@ -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 + +namespace lluz +{ + +struct Binding; +struct SourceModule; +struct Module; + +struct TypeVar; +using TypeId = const TypeVar*; + +using ScopePtr = std::shared_ptr; + +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 getLocation() + { + return expr ? expr->location : (local ? local->location : std::optional{}); + } + std::optional 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 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 findBindingAtPosition(const Module& module, const SourceModule& source, Position pos); +ExprOrLocal findExprOrLocalAtPosition(const SourceModule& source, Position pos); + +std::optional findTypeAtPosition(const Module& module, const SourceModule& sourceModule, Position pos); +std::optional findExpectedTypeAtPosition(const Module& module, const SourceModule& sourceModule, Position pos); + +std::optional getDocumentationSymbolAtPosition(const SourceModule& source, const Module& module, Position position); + +} // namespace lluz diff --git a/Analysis/include/lluz/Autocomplete.h b/Analysis/include/lluz/Autocomplete.h new file mode 100644 index 00000000..9a3d2492 --- /dev/null +++ b/Analysis/include/lluz/Autocomplete.h @@ -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 +#include +#include +#include + +namespace lluz +{ + +struct Frontend; +struct SourceModule; +struct Module; +struct TypeChecker; + +using ModulePtr = std::shared_ptr; + +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 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 containingClass = std::nullopt; + std::optional prop = std::nullopt; + std::optional documentationSymbol = std::nullopt; + Tags tags; + ParenthesesRecommendation parens = ParenthesesRecommendation::None; +}; + +using AutocompleteEntryMap = std::unordered_map; +struct AutocompleteResult +{ + AutocompleteEntryMap entryMap; + std::vector ancestry; + + AutocompleteResult() = default; + AutocompleteResult(AutocompleteEntryMap entryMap, std::vector ancestry) + : entryMap(std::move(entryMap)) + , ancestry(std::move(ancestry)) + { + } +}; + +using ModuleName = std::string; +using StringCompletionCallback = std::function(std::string tag, std::optional ctx)>; + +struct OwningAutocompleteResult +{ + AutocompleteResult result; + ModulePtr module; + std::unique_ptr 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 diff --git a/Analysis/include/lluz/BuiltinDefinitions.h b/Analysis/include/lluz/BuiltinDefinitions.h new file mode 100644 index 00000000..975d0bb5 --- /dev/null +++ b/Analysis/include/lluz/BuiltinDefinitions.h @@ -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&& types); +TypeId makeIntersection(TypeArena& arena, std::vector&& 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 selfType, std::initializer_list paramTypes, std::initializer_list retTypes); + +TypeId makeFunction( // Polymorphic + TypeArena& arena, std::optional selfType, std::initializer_list generics, std::initializer_list genericPacks, + std::initializer_list paramTypes, std::initializer_list retTypes); + +TypeId makeFunction( // Monomorphic + TypeArena& arena, std::optional selfType, std::initializer_list paramTypes, std::initializer_list paramNames, + std::initializer_list retTypes); + +TypeId makeFunction( // Polymorphic + TypeArena& arena, std::optional selfType, std::initializer_list generics, std::initializer_list genericPacks, + std::initializer_list paramTypes, std::initializer_list paramNames, std::initializer_list retTypes); + +void attachMagicFunction(TypeId ty, MagicFunction fn); + +Property makeProperty(TypeId ty, std::optional 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 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 diff --git a/Analysis/include/lluz/Clone.h b/Analysis/include/lluz/Clone.h new file mode 100644 index 00000000..deb56f07 --- /dev/null +++ b/Analysis/include/lluz/Clone.h @@ -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 + +namespace lluz +{ + +// Only exposed so they can be unit tested. +using SeenTypes = std::unordered_map; +using SeenTypePacks = std::unordered_map; + +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 diff --git a/Analysis/include/lluz/Config.h b/Analysis/include/lluz/Config.h new file mode 100644 index 00000000..d1425d89 --- /dev/null +++ b/Analysis/include/lluz/Config.h @@ -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 +#include +#include + +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 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 parseModeString(Mode& mode, const std::string& modeString, bool compat = false); +std::optional parseLintRuleString( + LintOptions& enabledLints, LintOptions& fatalLints, const std::string& warningName, const std::string& value, bool compat = false); + +std::optional parseConfig(const std::string& contents, Config& config, bool compat = false); + +} // namespace lluz diff --git a/Analysis/include/lluz/Constraint.h b/Analysis/include/lluz/Constraint.h new file mode 100644 index 00000000..b4043848 --- /dev/null +++ b/Analysis/include/lluz/Constraint.h @@ -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 +#include +#include + +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; +using ConstraintPtr = std::unique_ptr; + +struct Constraint +{ + explicit Constraint(ConstraintV&& c); + + Constraint(const Constraint&) = delete; + Constraint& operator=(const Constraint&) = delete; + + ConstraintV c; + std::vector> dependencies; +}; + +inline Constraint& asMutable(const Constraint& c) +{ + return const_cast(c); +} + +template +T* getMutable(Constraint& c) +{ + return ::lluz::get_if(&c.c); +} + +template +const T* get(const Constraint& c) +{ + return getMutable(asMutable(c)); +} + +} // namespace lluz diff --git a/Analysis/include/lluz/ConstraintGraphBuilder.h b/Analysis/include/lluz/ConstraintGraphBuilder.h new file mode 100644 index 00000000..f537ed87 --- /dev/null +++ b/Analysis/include/lluz/ConstraintGraphBuilder.h @@ -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 +#include +#include + +#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>> scopes; + + ModuleName moduleName; + SingletonTypes& singletonTypes; + const NotNull 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 astTypes{nullptr}; + // A mapping of AST node to TypePackId. + DenseHashMap astTypePacks{nullptr}; + DenseHashMap astOriginalCallTypes{nullptr}; + // Types resolved from type annotations. Analogous to astTypes. + DenseHashMap astResolvedTypes{nullptr}; + // Type packs resolved from type annotations. Analogous to astTypePacks. + DenseHashMap astResolvedTypePacks{nullptr}; + + int recursionCount = 0; + + // It is pretty uncommon for constraint generation to itself produce errors, but it can happen. + std::vector errors; + + // Occasionally constraint generation needs to produce an ICE. + const NotNull ice; + + NotNull globalScope; + + ConstraintGraphBuilder(const ModuleName& moduleName, TypeArena* arena, NotNull ice, NotNull globalScope); + + /** + * Fabricates a new free type belonging to a given scope. + * @param scope the scope the free type belongs to. + */ + TypeId freshType(NotNull 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 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 childScope(Location location, NotNull 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 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 scope, std::unique_ptr 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 scope, AstStatBlock* block); + + void visit(NotNull scope, AstStat* stat); + void visit(NotNull scope, AstStatBlock* block); + void visit(NotNull scope, AstStatLocal* local); + void visit(NotNull scope, AstStatLocalFunction* function); + void visit(NotNull scope, AstStatFunction* function); + void visit(NotNull scope, AstStatReturn* ret); + void visit(NotNull scope, AstStatAssign* assign); + void visit(NotNull scope, AstStatIf* ifStatement); + void visit(NotNull scope, AstStatTypeAlias* alias); + + TypePackId checkExprList(NotNull scope, const AstArray& exprs); + + TypePackId checkPack(NotNull scope, AstArray exprs); + TypePackId checkPack(NotNull 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 scope, AstExpr* expr); + + TypeId checkExprTable(NotNull scope, AstExprTable* expr); + TypeId check(NotNull scope, AstExprIndexName* indexName); + TypeId check(NotNull scope, AstExprIndexExpr* indexExpr); + TypeId check(NotNull scope, AstExprUnary* unary); + TypeId check(NotNull 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 bodyScope; + }; + + FunctionSignature checkFunctionSignature(NotNull 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 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 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 scope, AstTypePack* tp); + + TypePackId resolveTypePack(NotNull scope, const AstTypeList& list); + + std::vector> createGenerics(NotNull scope, AstArray generics); + std::vector> createGenericPacks(NotNull scope, AstArray packs); + + TypeId flattenPack(NotNull 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 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> collectConstraints(NotNull rootScope); + +} // namespace lluz diff --git a/Analysis/include/lluz/ConstraintSolver.h b/Analysis/include/lluz/ConstraintSolver.h new file mode 100644 index 00000000..033fd45f --- /dev/null +++ b/Analysis/include/lluz/ConstraintSolver.h @@ -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 + +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> constraints; + NotNull rootScope; + + // This includes every constraint that has not been fully solved. + // A constraint can be both blocked and unsolved, for instance. + std::vector> 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, size_t> blockedConstraints; + // A mapping of type/pack pointers to the constraints they block. + std::unordered_map>> blocked; + + ConstraintSolverLogger logger; + + explicit ConstraintSolver(TypeArena* arena, NotNull 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 c, bool force); + + bool tryDispatch(const SubtypeConstraint& c, NotNull constraint, bool force); + bool tryDispatch(const PackSubtypeConstraint& c, NotNull constraint, bool force); + bool tryDispatch(const GeneralizationConstraint& c, NotNull constraint, bool force); + bool tryDispatch(const InstantiationConstraint& c, NotNull constraint, bool force); + bool tryDispatch(const UnaryConstraint& c, NotNull constraint, bool force); + bool tryDispatch(const BinaryConstraint& c, NotNull constraint, bool force); + bool tryDispatch(const NameConstraint& c, NotNull constraint); + + void block(NotNull target, NotNull 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 constraint); + bool block(TypePackId target, NotNull constraint); + + void unblock(NotNull 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 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 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 rootScope, struct ToStringOptions& opts); + +} // namespace lluz diff --git a/Analysis/include/lluz/ConstraintSolverLogger.h b/Analysis/include/lluz/ConstraintSolverLogger.h new file mode 100644 index 00000000..a5ca24d7 --- /dev/null +++ b/Analysis/include/lluz/ConstraintSolverLogger.h @@ -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 +#include +#include + +namespace lluz +{ + +struct ConstraintSolverLogger +{ + std::string compileOutput(); + void captureBoundarySnapshot(const Scope2* rootScope, std::vector>& unsolvedConstraints); + void prepareStepSnapshot(const Scope2* rootScope, NotNull current, std::vector>& unsolvedConstraints); + void commitPreparedStepSnapshot(); + +private: + std::vector snapshots; + std::optional preparedSnapshot; + ToStringOptions opts; +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Documentation.h b/Analysis/include/lluz/Documentation.h new file mode 100644 index 00000000..5467ee41 --- /dev/null +++ b/Analysis/include/lluz/Documentation.h @@ -0,0 +1,62 @@ +#pragma once + +#include "lluz/DenseHash.h" +#include "lluz/Variant.h" + +#include +#include + +namespace lluz +{ + +struct FunctionDocumentation; +struct TableDocumentation; +struct OverloadedFunctionDocumentation; +struct BasicDocumentation; + +using Documentation = lluz::Variant; +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 parameters; + std::vector returns; + std::string learnMoreLink; + std::string codeSample; +}; + +struct OverloadedFunctionDocumentation +{ + // This is a map of function signature to overload symbol name. + lluz::DenseHashMap 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 keys; + std::string learnMoreLink; + std::string codeSample; +}; + +using DocumentationDatabase = lluz::DenseHashMap; + +} // namespace lluz diff --git a/Analysis/include/lluz/Error.h b/Analysis/include/lluz/Error.h new file mode 100644 index 00000000..2700cddb --- /dev/null +++ b/Analysis/include/lluz/Error.h @@ -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 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 candidates. +// But for telemetry purposes, we want to have this be a distinct variant. +struct UnknownPropButFoundLikeProp +{ + TypeId table; + Name key; + std::set 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 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 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 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 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; + +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 +const T* get(const TypeError& e) +{ + return get_if(&e.data); +} + +template +T* get(TypeError& e) +{ + return get_if(&e.data); +} + +using ErrorVec = std::vector; + +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 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; +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/FileResolver.h b/Analysis/include/lluz/FileResolver.h new file mode 100644 index 00000000..703cb7c8 --- /dev/null +++ b/Analysis/include/lluz/FileResolver.h @@ -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 +#include + +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 readSource(const ModuleName& name) = 0; + + virtual std::optional resolveModule(const ModuleInfo* context, AstExpr* expr) + { + return std::nullopt; + } + + virtual std::string getHumanReadableModuleName(const ModuleName& name) const + { + return name; + } + + virtual std::optional getEnvironmentForModule(const ModuleName& name) const + { + return std::nullopt; + } +}; + +struct NullFileResolver : FileResolver +{ + std::optional readSource(const ModuleName& name) override + { + return std::nullopt; + } +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Frontend.h b/Analysis/include/lluz/Frontend.h new file mode 100644 index 00000000..28217de2 --- /dev/null +++ b/Analysis/include/lluz/Frontend.h @@ -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 +#include +#include + +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 parseMode(const std::vector& hotcomments); + +std::vector parsePathExpr(const AstExpr& pathExpr); + +// Exported only for convenient testing. +std::optional pathExprToModuleName(const ModuleName& currentModuleName, const std::vector& 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 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 requireSet; + std::vector> 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 errors; + std::vector timeoutHits; +}; + +struct FrontendModuleResolver : ModuleResolver +{ + FrontendModuleResolver(Frontend* frontend); + + const ModulePtr getModule(const ModuleName& moduleName) const override; + bool moduleExists(const ModuleName& moduleName) const override; + std::optional resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override; + std::string getHumanReadableModuleName(const ModuleName& moduleName) const override; + + Frontend* frontend; + std::unordered_map 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 optionOverride = {}); // new shininess + LintResult lint(const ModuleName& name, std::optional 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 lintFragment(std::string_view source, std::optional enabledLintWarnings = {}); + + LintResult lint(const SourceModule& module, std::optional enabledLintWarnings = {}); + + bool isDirty(const ModuleName& name, bool forAutocomplete = false) const; + void markDirty(const ModuleName& name, std::vector* 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 applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName); + + NotNull getGlobalScope2(); + +private: + ModulePtr check(const SourceModule& sourceModule, Mode mode, const ScopePtr& environmentScope); + + std::pair getSourceNode(CheckResult& checkResult, const ModuleName& name); + SourceModule parse(const ModuleName& name, std::string_view src, const ParseOptions& parseOptions); + + bool parseGraph(std::vector& buildQueue, CheckResult& checkResult, const ModuleName& root, bool forAutocomplete); + + static LintResult classifyLints(const std::vector& warnings, const Config& config); + + ScopePtr getModuleEnvironment(const SourceModule& module, const Config& config); + + std::unordered_map environments; + std::unordered_map> builtinDefinitions; + + std::unique_ptr globalScope2; + +public: + FileResolver* fileResolver; + FrontendModuleResolver moduleResolver; + FrontendModuleResolver moduleResolverForAutocomplete; + TypeChecker typeChecker; + TypeChecker typeCheckerForAutocomplete; + ConfigResolver* configResolver; + FrontendOptions options; + InternalErrorReporter iceHandler; + TypeArena arenaForAutocomplete; + + std::unordered_map sourceNodes; + std::unordered_map sourceModules; + std::unordered_map requireTrace; + + Stats stats = {}; +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Instantiation.h b/Analysis/include/lluz/Instantiation.h new file mode 100644 index 00000000..47b33c4f --- /dev/null +++ b/Analysis/include/lluz/Instantiation.h @@ -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& generics, const std::vector& genericPacks) + : Substitution(log, arena) + , level(level) + , generics(generics) + , genericPacks(genericPacks) + { + } + + TypeLevel level; + std::vector generics; + std::vector 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 diff --git a/Analysis/include/lluz/IostreamHelpers.h b/Analysis/include/lluz/IostreamHelpers.h new file mode 100644 index 00000000..75027076 --- /dev/null +++ b/Analysis/include/lluz/IostreamHelpers.h @@ -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 + +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 diff --git a/Analysis/include/lluz/JsonEncoder.h b/Analysis/include/lluz/JsonEncoder.h new file mode 100644 index 00000000..56ae8d27 --- /dev/null +++ b/Analysis/include/lluz/JsonEncoder.h @@ -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 + +namespace lluz +{ + +class AstNode; + +std::string toJson(AstNode* node); + +} // namespace lluz diff --git a/Analysis/include/lluz/LValue.h b/Analysis/include/lluz/LValue.h new file mode 100644 index 00000000..5c45eeef --- /dev/null +++ b/Analysis/include/lluz/LValue.h @@ -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 +#include + +namespace lluz +{ + +struct TypeVar; +using TypeId = const TypeVar*; + +struct Field; +using LValue = Variant; + +struct Field +{ + std::shared_ptr 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 tryGetLValue(const class AstExpr& expr); + +// Utility function: breaks down an LValue to get at the Symbol +Symbol getBaseSymbol(const LValue& lvalue); + +template +const T* get(const LValue& lvalue) +{ + return get_if(&lvalue); +} + +using RefinementMap = std::unordered_map; + +void merge(RefinementMap& l, const RefinementMap& r, std::function f); +void addRefinement(RefinementMap& refis, const LValue& lvalue, TypeId ty); + +} // namespace lluz diff --git a/Analysis/include/lluz/Linter.h b/Analysis/include/lluz/Linter.h new file mode 100644 index 00000000..d4019043 --- /dev/null +++ b/Analysis/include/lluz/Linter.h @@ -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 +#include + +namespace lluz +{ + +struct AstName; +class AstStat; +class AstNameTable; +struct TypeChecker; +struct Module; +struct HotComment; + +using ScopePtr = std::shared_ptr; + +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& hotcomments); +}; + +struct LintResult +{ + std::vector errors; + std::vector 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 lint(AstStat* root, const AstNameTable& names, const ScopePtr& env, const Module* module, + const std::vector& hotcomments, const LintOptions& options); + +std::vector getDeprecatedGlobals(const AstNameTable& names); + +} // namespace lluz diff --git a/Analysis/include/lluz/Module.h b/Analysis/include/lluz/Module.h new file mode 100644 index 00000000..8174eed6 --- /dev/null +++ b/Analysis/include/lluz/Module.h @@ -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 +#include +#include +#include + +namespace lluz +{ + +struct Module; + +using ScopePtr = std::shared_ptr; +using ModulePtr = std::shared_ptr; + +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 environmentName; + bool cyclic = false; + + std::shared_ptr allocator; + std::shared_ptr names; + std::vector parseErrors; + + AstStatBlock* root = nullptr; + std::optional mode; + + std::vector hotcomments; + std::vector commentLocations; + + SourceModule() + : allocator(new Allocator) + , names(new AstNameTable(*allocator)) + { + } +}; + +bool isWithinComment(const SourceModule& sourceModule, Position pos); + +struct RequireCycle +{ + Location location; + std::vector 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; + std::shared_ptr names; + + std::vector> scopes; // never empty + std::vector>> scope2s; // never empty + + DenseHashMap astTypes{nullptr}; + DenseHashMap astTypePacks{nullptr}; + DenseHashMap astExpectedTypes{nullptr}; + DenseHashMap astOriginalCallTypes{nullptr}; + DenseHashMap astOverloadResolvedTypes{nullptr}; + DenseHashMap astResolvedTypes{nullptr}; + DenseHashMap astResolvedTypePacks{nullptr}; + + std::unordered_map 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 diff --git a/Analysis/include/lluz/ModuleResolver.h b/Analysis/include/lluz/ModuleResolver.h new file mode 100644 index 00000000..95a512d0 --- /dev/null +++ b/Analysis/include/lluz/ModuleResolver.h @@ -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 +#include +#include + +namespace lluz +{ + +class AstExpr; +struct Module; + +using ModulePtr = std::shared_ptr; + +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 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 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 diff --git a/Analysis/include/lluz/Normalize.h b/Analysis/include/lluz/Normalize.h new file mode 100644 index 00000000..89ce21da --- /dev/null +++ b/Analysis/include/lluz/Normalize.h @@ -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 normalize(TypeId ty, TypeArena& arena, InternalErrorReporter& ice); +std::pair normalize(TypeId ty, const ModulePtr& module, InternalErrorReporter& ice); +std::pair normalize(TypePackId ty, TypeArena& arena, InternalErrorReporter& ice); +std::pair normalize(TypePackId ty, const ModulePtr& module, InternalErrorReporter& ice); + +} // namespace lluz diff --git a/Analysis/include/lluz/NotNull.h b/Analysis/include/lluz/NotNull.h new file mode 100644 index 00000000..e9db6c16 --- /dev/null +++ b/Analysis/include/lluz/NotNull.h @@ -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 + +namespace lluz +{ + +/** A non-owning, non-null pointer to a T. + * + * A NotNull is notionally identical to a T* with the added restriction that + * it can never store nullptr. + * + * The sole conversion rule from T* to NotNull 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 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 through this implicit conversion. + */ +template +struct NotNull +{ + explicit NotNull(T* t) + : ptr(t) + { + lluz_ASSERT(t); + } + + explicit NotNull(std::nullptr_t) = delete; + void operator=(std::nullptr_t) = delete; + + template + NotNull(NotNull 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 +struct hash> +{ + size_t operator()(const lluz::NotNull& p) const + { + return std::hash()(p.get()); + } +}; + +} // namespace std diff --git a/Analysis/include/lluz/Predicate.h b/Analysis/include/lluz/Predicate.h new file mode 100644 index 00000000..e8642340 --- /dev/null +++ b/Analysis/include/lluz/Predicate.h @@ -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 + +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; +using PredicateVec = std::vector; + +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 +const T* get(const Predicate& predicate) +{ + return get_if(&predicate); +} + +} // namespace lluz diff --git a/Analysis/include/lluz/Quantify.h b/Analysis/include/lluz/Quantify.h new file mode 100644 index 00000000..a00727d0 --- /dev/null +++ b/Analysis/include/lluz/Quantify.h @@ -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 diff --git a/Analysis/include/lluz/RecursionCounter.h b/Analysis/include/lluz/RecursionCounter.h new file mode 100644 index 00000000..fb4bea87 --- /dev/null +++ b/Analysis/include/lluz/RecursionCounter.h @@ -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 +#include + +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 diff --git a/Analysis/include/lluz/RequireTracer.h b/Analysis/include/lluz/RequireTracer.h new file mode 100644 index 00000000..3121bab3 --- /dev/null +++ b/Analysis/include/lluz/RequireTracer.h @@ -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 + +namespace lluz +{ + +class AstStat; +class AstExpr; +class AstStatBlock; +struct AstLocal; + +struct RequireTraceResult +{ + DenseHashMap exprs{nullptr}; + + std::vector> requireList; +}; + +RequireTraceResult traceRequires(FileResolver* fileResolver, AstStatBlock* root, const ModuleName& currentModuleName); + +} // namespace lluz diff --git a/Analysis/include/lluz/Scope.h b/Analysis/include/lluz/Scope.h new file mode 100644 index 00000000..5723f40f --- /dev/null +++ b/Analysis/include/lluz/Scope.h @@ -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 +#include +#include + +namespace lluz +{ + +struct Scope; + +using ScopePtr = std::shared_ptr; + +struct Binding +{ + TypeId typeId; + Location location; + bool deprecated = false; + std::string deprecatedSuggestion; + std::optional 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 bindings; + TypePackId returnType; + bool breakOk = false; + std::optional varargPack; + + TypeLevel level; + + std::unordered_map exportedTypeBindings; + std::unordered_map privateTypeBindings; + std::unordered_map typeAliasLocations; + + std::unordered_map> importedTypeBindings; + + std::optional lookup(const Symbol& name); + + std::optional lookupType(const Name& name); + std::optional lookupImportedType(const Name& moduleAlias, const Name& name); + + std::unordered_map privateTypePackBindings; + std::optional lookupPack(const Name& name); + + // WARNING: This function linearly scans for a string key of equal value! It is thus O(n**2) + std::optional 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 { data: T, children: Forest } type Forest = {Tree}` + // we need that the generic type `T` in both cases is the same, so we use a cache. + std::unordered_map typeAliasTypeParameters; + std::unordered_map 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> children; + std::unordered_map bindings; // TODO: I think this can be a DenseHashMap + std::unordered_map typeBindings; + std::unordered_map typePackBindings; + TypePackId returnType; + std::optional varargPack; + // All constraints belonging to this scope. + std::vector constraints; + + std::optional lookup(Symbol sym); + std::optional lookupTypeBinding(const Name& name); + std::optional lookupTypePackBinding(const Name& name); +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Substitution.h b/Analysis/include/lluz/Substitution.h new file mode 100644 index 00000000..d08acb38 --- /dev/null +++ b/Analysis/include/lluz/Substitution.h @@ -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 typeToIndex{nullptr}; + DenseHashMap packToIndex{nullptr}; + std::vector indexToType; + std::vector indexToPack; + + // Tarjan keeps a stack of vertices where we're still in the process + // of finding their SCC. + std::vector stack; + std::vector onStack; + + // Tarjan calculates the lowlink for each vertex, + // which is the lowest ancestor index reachable from the vertex. + std::vector 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 edgesTy; + std::vector edgesTp; + std::vector 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 indexify(TypeId ty); + std::pair 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 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 newTypes{nullptr}; + DenseHashMap newPacks{nullptr}; + + std::optional substitute(TypeId ty); + std::optional 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 + TypeId addType(const T& tv) + { + return arena->addType(tv); + } + + template + TypePackId addTypePack(const T& tp) + { + return arena->addTypePack(TypePackVar{tp}); + } +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Symbol.h b/Analysis/include/lluz/Symbol.h new file mode 100644 index 00000000..95d62ef1 --- /dev/null +++ b/Analysis/include/lluz/Symbol.h @@ -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 + +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 + 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 +{ + std::size_t operator()(const lluz::Symbol& s) const noexcept + { + return std::hash()(s.local) ^ (s.global.value ? std::hash()(s.global.value) : 0); + } +}; +} // namespace std diff --git a/Analysis/include/lluz/ToDot.h b/Analysis/include/lluz/ToDot.h new file mode 100644 index 00000000..d8189105 --- /dev/null +++ b/Analysis/include/lluz/ToDot.h @@ -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 + +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 diff --git a/Analysis/include/lluz/ToString.h b/Analysis/include/lluz/ToString.h new file mode 100644 index 00000000..01d446ce --- /dev/null +++ b/Analysis/include/lluz/ToString.h @@ -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 +#include +#include +#include + +lluz_FASTINT(LluTableTypeMaximumStringifierLength) +lluz_FASTINT(LluTypeMaximumStringifierLength) + +namespace lluz +{ + +struct ToStringNameMap +{ + std::unordered_map typeVars; + std::unordered_map 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 nameMap; + std::shared_ptr scope; // If present, module names will be added and types that are not available in scope will be marked as 'invalid' + std::vector 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, const char* name); + +std::string generateName(size_t n); + +} // namespace lluz diff --git a/Analysis/include/lluz/TopoSortStatements.h b/Analysis/include/lluz/TopoSortStatements.h new file mode 100644 index 00000000..4da4f777 --- /dev/null +++ b/Analysis/include/lluz/TopoSortStatements.h @@ -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 + +namespace lluz +{ + +template +struct AstArray; + +class AstStat; + +bool containsFunctionCall(const AstStat& stat); +bool containsFunctionCallOrReturn(const AstStat& stat); +bool isFunction(const AstStat& stat); +void toposort(std::vector& stats); + +} // namespace lluz diff --git a/Analysis/include/lluz/Transpiler.h b/Analysis/include/lluz/Transpiler.h new file mode 100644 index 00000000..876135f1 --- /dev/null +++ b/Analysis/include/lluz/Transpiler.h @@ -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 + +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 diff --git a/Analysis/include/lluz/TxnLog.h b/Analysis/include/lluz/TxnLog.h new file mode 100644 index 00000000..48e23f23 --- /dev/null +++ b/Analysis/include/lluz/TxnLog.h @@ -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 +#include + +#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 +T* getMutable(PendingType* pending) +{ + // We use getMutable here because this state is intended to be mutated freely. + return getMutable(&pending->pending); +} + +template +T* getMutable(PendingTypePack* pending) +{ + // We use getMutable here because this state is intended to be mutated freely. + return getMutable(&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>* 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 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 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 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 + 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 + 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 + T* getMutable(TID ty) const + { + auto* pendingTy = pending(ty); + if (pendingTy) + return lluz::getMutable(pendingTy); + + return lluz::getMutable(ty); + } + + template + const T* get(TID ty) const + { + return this->getMutable(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 + 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(&pendingTy->pending.ty) != nullptr; + + return lluz::get_if(&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> typeVarChanges; + DenseHashMap> 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> 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>* sharedSeen; +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/TypeArena.h b/Analysis/include/lluz/TypeArena.h new file mode 100644 index 00000000..9d54a257 --- /dev/null +++ b/Analysis/include/lluz/TypeArena.h @@ -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 + +namespace lluz +{ + +struct TypeArena +{ + TypedAllocator typeVars; + TypedAllocator typePacks; + + void clear(); + + template + TypeId addType(T tv) + { + if constexpr (std::is_same_v) + 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 types); + TypePackId addTypePack(std::vector types); + TypePackId addTypePack(TypePack pack); + TypePackId addTypePack(TypePackVar pack); + + template + TypePackId addTypePack(T tp) + { + return addTypePack(TypePackVar(std::move(tp))); + } +}; + +void freeze(TypeArena& arena); +void unfreeze(TypeArena& arena); + +} // namespace lluz diff --git a/Analysis/include/lluz/TypeAttach.h b/Analysis/include/lluz/TypeAttach.h new file mode 100644 index 00000000..3786c741 --- /dev/null +++ b/Analysis/include/lluz/TypeAttach.h @@ -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 + +namespace lluz +{ + +struct TypeRehydrationOptions +{ + std::unordered_set bannedNames; + bool expandClassProps = false; +}; + +void attachTypeData(SourceModule& source, Module& result); + +AstType* rehydrateAnnotation(TypeId type, Allocator* allocator, const TypeRehydrationOptions& options = {}); + +} // namespace lluz diff --git a/Analysis/include/lluz/TypeChecker2.h b/Analysis/include/lluz/TypeChecker2.h new file mode 100644 index 00000000..703418df --- /dev/null +++ b/Analysis/include/lluz/TypeChecker2.h @@ -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 diff --git a/Analysis/include/lluz/TypeInfer.h b/Analysis/include/lluz/TypeInfer.h new file mode 100644 index 00000000..b6a41b23 --- /dev/null +++ b/Analysis/include/lluz/TypeInfer.h @@ -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 +#include +#include + +namespace lluz +{ + +struct Scope; +struct TypeChecker; +struct ModuleResolver; + +using Name = std::string; +using ScopePtr = std::shared_ptr; +using OverloadErrorEntry = std::tuple, std::vector, 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 typeArguments; + std::unordered_map 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 genericTypes; + std::vector genericPacks; +}; + +struct HashBoolNamePair +{ + size_t operator()(const std::pair& 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 environmentScope = std::nullopt); + ModulePtr checkWithoutRecursionCheck(const SourceModule& module, Mode mode, std::optional environmentScope = std::nullopt); + + std::vector> 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& sorted); + + WithPredicate checkExpr( + const ScopePtr& scope, const AstExpr& expr, std::optional expectedType = std::nullopt, bool forceSingleton = false); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprLocal& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprGlobal& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprVarargs& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprCall& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprIndexName& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprIndexExpr& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprFunction& expr, std::optional expectedType = std::nullopt); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprTable& expr, std::optional expectedType = std::nullopt); + WithPredicate 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 checkExpr(const ScopePtr& scope, const AstExprBinary& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprTypeAssertion& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprError& expr); + WithPredicate checkExpr(const ScopePtr& scope, const AstExprIfElse& expr, std::optional expectedType = std::nullopt); + + TypeId checkExprTable(const ScopePtr& scope, const AstExprTable& expr, const std::vector>& fieldTypes, + std::optional 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 checkFunctionSignature(const ScopePtr& scope, int subLevel, const AstExprFunction& expr, + std::optional originalNameLoc, std::optional selfType, std::optional 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& argLocations); + + WithPredicate checkExprPack(const ScopePtr& scope, const AstExpr& expr); + WithPredicate checkExprPack(const ScopePtr& scope, const AstExprCall& expr); + std::vector> getExpectedTypesForCall(const std::vector& overloads, size_t argumentCount, bool selfCall); + std::optional> checkCallOverload(const ScopePtr& scope, const AstExprCall& expr, TypeId fn, TypePackId retPack, + TypePackId argPack, TypePack* args, const std::vector* argLocations, const WithPredicate& argListResult, + std::vector& overloadsThatMatchArgCount, std::vector& overloadsThatDont, std::vector& errors); + bool handleSelfCallMismatch(const ScopePtr& scope, const AstExprCall& expr, TypePack* args, const std::vector& argLocations, + const std::vector& errors); + void reportOverloadResolutionError(const ScopePtr& scope, const AstExprCall& expr, TypePackId retPack, TypePackId argPack, + const std::vector& argLocations, const std::vector& overloads, const std::vector& overloadsThatMatchArgCount, + const std::vector& errors); + + WithPredicate checkExprList(const ScopePtr& scope, const Location& location, const AstArray& exprs, + bool substituteFreeForNil = false, const std::vector& lhsAnnotations = {}, + const std::vector>& expectedTypes = {}); + + static std::optional 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 + 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 + 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 findMetatableEntry(TypeId type, std::string entry, const Location& location); + std::optional findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location); + + std::optional 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 reduceUnion(const std::vector& types); + + std::optional 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: () -> 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 filterMap(TypeId type, TypeIdPredicate predicate); + +public: + std::optional pickTypesFromSense(TypeId type, bool sense); + +private: + TypeId unionOfTypes(TypeId a, TypeId b, const Location& location, bool unifyFreeTypes = true); + + // ex + // TypeId id = addType(FreeTypeVar()); + template + 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& ty); + TypePackId addTypePack(const std::vector& ty, std::optional tail); + TypePackId addTypePack(std::initializer_list&& 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& typeParams, + const std::vector& typePackParams, const Location& location); + + // Note: `scope` must be a fresh scope. + GenericTypeDefinitions createGenericTypes(const ScopePtr& scope, std::optional levelOpt, const AstNode& node, + const AstArray& genericNames, const AstArray& 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 resolveLValue(const ScopePtr& scope, const LValue& lvalue); + std::optional 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 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 prepareModuleScope; + InternalErrorReporter* iceHandler; + + UnifierSharedState unifierState; + + std::vector requireCycles; + + // Type inference limits + std::optional finishTime; + std::optional instantiationChildLimit; + std::optional 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, HashBoolNamePair> duplicateTypeAliases; + + std::vector> deferredQuantification; +}; + +// Unit test hook +void setPrintLine(void (*pl)(const std::string& s)); +void resetPrintLine(); + +} // namespace lluz diff --git a/Analysis/include/lluz/TypePack.h b/Analysis/include/lluz/TypePack.h new file mode 100644 index 00000000..2a2ba561 --- /dev/null +++ b/Analysis/include/lluz/TypePack.h @@ -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 +#include + +namespace lluz +{ + +struct TypeArena; + +struct TypePack; +struct VariadicTypePack; + +struct TypePackVar; + +struct TxnLog; + +using TypePackId = const TypePackVar*; +using FreeTypePack = Unifiable::Free; +using BoundTypePack = Unifiable::Bound; +using GenericTypePack = Unifiable::Generic; +using TypePackVariant = Unifiable::Variant; + +/* 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 head; + std::optional 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 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>; + +bool areEqual(SeenSet& seen, const TypePackVar& lhs, const TypePackVar& rhs); + +TypePackId follow(TypePackId tp); +TypePackId follow(TypePackId tp, std::function 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 first(TypePackId tp, bool ignoreHiddenVariadics = true); + +TypePackVar* asMutable(TypePackId tp); +TypePack* asMutable(const TypePack* tp); + +template +const T* get(TypePackId tp) +{ + lluz_ASSERT(tp); + + if constexpr (!std::is_same_v) + lluz_ASSERT(get_if(&tp->ty) == nullptr); + + return get_if(&(tp->ty)); +} + +template +T* getMutable(TypePackId tp) +{ + lluz_ASSERT(tp); + + if constexpr (!std::is_same_v) + lluz_ASSERT(get_if(&tp->ty) == nullptr); + + return get_if(&(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::optional> flatten(TypePackId tp); +std::pair, std::optional> 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 diff --git a/Analysis/include/lluz/TypeUtils.h b/Analysis/include/lluz/TypeUtils.h new file mode 100644 index 00000000..91082901 --- /dev/null +++ b/Analysis/include/lluz/TypeUtils.h @@ -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 +#include + +namespace lluz +{ + +using ScopePtr = std::shared_ptr; + +std::optional findMetatableEntry(ErrorVec& errors, TypeId type, std::string entry, Location location); +std::optional findTablePropertyRespectingMeta(ErrorVec& errors, TypeId ty, Name name, Location location); + +} // namespace lluz diff --git a/Analysis/include/lluz/TypeVar.h b/Analysis/include/lluz/TypeVar.h new file mode 100644 index 00000000..b6d5e12e --- /dev/null +++ b/Analysis/include/lluz/TypeVar.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +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(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; + +using GenericTypeVar = Unifiable::Generic; + +using Tags = std::vector; + +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 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& parts) + : parts(parts) + , level(level) + { + } + + std::vector 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; + +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 +const T* get(const SingletonTypeVar* stv) +{ + if (stv) + return get_if(&stv->variant); + else + return nullptr; +} + +struct GenericTypeDefinition +{ + TypeId ty; + std::optional defaultValue; +}; + +struct GenericTypePackDefinition +{ + TypePackId tp; + std::optional defaultValue; +}; + +struct FunctionArgument +{ + Name name; + Location location; +}; + +struct FunctionDefinition +{ + std::optional definitionModuleName; + Location definitionLocation; + std::optional 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 +struct WithPredicate +{ + T type; + PredicateVec predicates; +}; + +using MagicFunction = std::function>( + struct TypeChecker&, const std::shared_ptr&, const class AstExprCall&, WithPredicate)>; + +struct FunctionTypeVar +{ + // Global monomorphic function + FunctionTypeVar(TypePackId argTypes, TypePackId retTypes, std::optional defn = {}, bool hasSelf = false); + + // Global polymorphic function + FunctionTypeVar(std::vector generics, std::vector genericPacks, TypePackId argTypes, TypePackId retTypes, + std::optional defn = {}, bool hasSelf = false); + + // Local monomorphic function + FunctionTypeVar(TypeLevel level, TypePackId argTypes, TypePackId retTypes, std::optional defn = {}, bool hasSelf = false); + + // Local polymorphic function + FunctionTypeVar(TypeLevel level, std::vector generics, std::vector genericPacks, TypePackId argTypes, TypePackId retTypes, + std::optional defn = {}, bool hasSelf = false); + + TypeLevel level; + Scope2* scope = nullptr; + /// These should all be generic + std::vector generics; + std::vector genericPacks; + TypePackId argTypes; + std::vector> argNames; + TypePackId retTypes; + std::optional 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 = std::nullopt; + Tags tags; + std::optional 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; + + TableTypeVar() = default; + explicit TableTypeVar(TableState state, TypeLevel level); + TableTypeVar(const Props& props, const std::optional& indexer, TypeLevel level, TableState state); + + Props props; + std::optional indexer; + + TableState state = TableState::Unsealed; + TypeLevel level; + Scope2* scope = nullptr; + std::optional 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 syntheticName; + + std::vector instantiatedTypeParams; + std::vector instantiatedTypePackParams; + ModuleName definitionModuleName; + + std::optional boundTo; + Tags tags; + + // Methods of this table that have an untyped self will use the same shared self type. + std::optional 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 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 parent; + std::optional metatable; // metaclass? + Tags tags; + std::shared_ptr userData; + ModuleName definitionModuleName; + + ClassTypeVar(Name name, Props props, std::optional parent, std::optional metatable, Tags tags, + std::shared_ptr 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 typeParams; + std::vector 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 typeParams, TypeId type) + : typeParams(std::move(typeParams)) + , type(type) + { + } + + TypeFun(std::vector typeParams, std::vector 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 options; +}; + +struct IntersectionTypeVar +{ + std::vector parts; +}; + +struct LazyTypeVar +{ + std::function thunk; +}; + +using ErrorTypeVar = Unifiable::Error; + +using TypeVariant = Unifiable::Variant; + +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 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>; +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 mapper); + +std::vector 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 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 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& 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 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 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 +const T* get(TypeId tv) +{ + lluz_ASSERT(tv); + + if constexpr (!std::is_same_v) + lluz_ASSERT(get_if(&tv->ty) == nullptr); + + return get_if(&tv->ty); +} + +template +T* getMutable(TypeId tv) +{ + lluz_ASSERT(tv); + + if constexpr (!std::is_same_v) + lluz_ASSERT(get_if(&tv->ty) == nullptr); + + return get_if(&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; + + std::deque stack; + std::unordered_set 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(TypeId)>; +std::vector 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 diff --git a/Analysis/include/lluz/TypedAllocator.h b/Analysis/include/lluz/TypedAllocator.h new file mode 100644 index 00000000..64a5c89a --- /dev/null +++ b/Analysis/include/lluz/TypedAllocator.h @@ -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 +#include + +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 +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 + 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...)); + ++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(block)); + currentBlockSize = 0; + } + + bool frozen = false; + std::vector stuff; + size_t currentBlockSize = 0; + + static constexpr size_t kBlockSizeBytes = 32768; + static constexpr size_t kBlockSize = kBlockSizeBytes / sizeof(T); +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Unifiable.h b/Analysis/include/lluz/Unifiable.h new file mode 100644 index 00000000..ced49ed5 --- /dev/null +++ b/Analysis/include/lluz/Unifiable.h @@ -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 + +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 +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 +using Variant = lluz::Variant, Generic, Error, Value...>; + +} // namespace lluz::Unifiable diff --git a/Analysis/include/lluz/Unifier.h b/Analysis/include/lluz/Unifier.h new file mode 100644 index 00000000..4b832b12 --- /dev/null +++ b/Analysis/include/lluz/Unifier.h @@ -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 + +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 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 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& seen, TypeId needle, TypeId haystack); + void occursCheck(TypePackId needle, TypePackId haystack); + void occursCheck(DenseHashSet& 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 firstPackErrorPos; +}; + +void promoteTypeLevels(TxnLog& log, const TypeArena* arena, TypeLevel minLevel, TypePackId tp); + +} // namespace lluz diff --git a/Analysis/include/lluz/UnifierSharedState.h b/Analysis/include/lluz/UnifierSharedState.h new file mode 100644 index 00000000..05f9361f --- /dev/null +++ b/Analysis/include/lluz/UnifierSharedState.h @@ -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 + +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& 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 skipCacheForType{nullptr}; + DenseHashSet, TypeIdPairHash> cachedUnify{{nullptr, nullptr}}; + DenseHashMap, TypeErrorData, TypeIdPairHash> cachedUnifyError{{nullptr, nullptr}}; + + DenseHashSet tempSeenTy{nullptr}; + DenseHashSet tempSeenTp{nullptr}; + + UnifierCounters counters; +}; + +} // namespace lluz diff --git a/Analysis/include/lluz/Variant.h b/Analysis/include/lluz/Variant.h new file mode 100644 index 00000000..7b3f2621 --- /dev/null +++ b/Analysis/include/lluz/Variant.h @@ -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 +#include +#include +#include +#include + +namespace lluz +{ + +template +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...> == false, XorStr("variant does not allow void as an alternative type")); + static_assert(std::disjunction_v...> == false, XorStr("variant does not allow references as an alternative type")); + static_assert(std::disjunction_v...> == false, XorStr("variant does not allow arrays as an alternative type")); + +private: + template + static constexpr int getTypeId() + { + using TT = std::decay_t; + + constexpr int N = sizeof...(Ts); + constexpr bool is[N] = {std::is_same_v...}; + + for (int i = 0; i < N; ++i) + if (is[i]) + return i; + + return -1; + } + + template + struct First + { + using type = T; + }; + +public: + using first_alternative = typename First::type; + + Variant() + { + static_assert(std::is_default_constructible_v, XorStr("first alternative type must be default constructible")); + typeId = 0; + new (&storage) first_alternative(); + } + + template + Variant(T&& value, std::enable_if_t() >= 0>* = 0) + { + using TT = std::decay_t; + + constexpr int tid = getTypeId(); + 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 is equivalent to std::move() but faster in Debug + return *this = static_cast(copy); + } + + Variant& operator=(Variant&& other) + { + if (this != &other) + { + tableDtor[typeId](&storage); + typeId = other.typeId; + tableMove[typeId](&storage, &other.storage); // nothrow + } + return *this; + } + + template + T& emplace(Args&&... args) + { + using TT = std::decay_t; + constexpr int tid = getTypeId(); + static_assert(tid >= 0, XorStr("unsupported T")); + + tableDtor[typeId](&storage); + typeId = tid; + new (&storage) TT(std::forward(args)...); + + return *reinterpret_cast(&storage); + } + + template + const T* get_if() const + { + constexpr int tid = getTypeId(); + static_assert(tid >= 0, XorStr("unsupported T")); + + return tid == typeId ? reinterpret_cast(&storage) : nullptr; + } + + template + T* get_if() + { + constexpr int tid = getTypeId(); + static_assert(tid >= 0, XorStr("unsupported T")); + + return tid == typeId ? reinterpret_cast(&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...}; + + 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 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 + static void fnCopy(void* dst, const void* src) + { + new (dst) T(*static_cast(src)); + } + + template + static void fnMove(void* dst, void* src) + { + // static_cast is equivalent to std::move() but faster in Debug + new (dst) T(static_cast(*static_cast(src))); + } + + template + static void fnDtor(void* dst) + { + static_cast(dst)->~T(); + } + + template + static bool fnPredEq(const void* lhs, const void* rhs) + { + return *static_cast(lhs) == *static_cast(rhs); + } + + static constexpr FnCopy tableCopy[sizeof...(Ts)] = {&fnCopy...}; + static constexpr FnMove tableMove[sizeof...(Ts)] = {&fnMove...}; + static constexpr FnDtor tableDtor[sizeof...(Ts)] = {&fnDtor...}; + + int typeId; + alignas(storageAlign) char storage[storageSize]; + + template + friend auto visit(Visitor&& vis, const Variant<_Ts...>& var); + template + friend auto visit(Visitor&& vis, Variant<_Ts...>& var); +}; + +template +const T* get_if(const Variant* var) +{ + return var ? var->template get_if() : nullptr; +} + +template +T* get_if(Variant* var) +{ + return var ? var->template get_if() : nullptr; +} + +template +static void fnVisitR(Visitor& vis, Result& dst, std::conditional_t, const void, void>* src) +{ + dst = vis(*static_cast(src)); +} + +template +static void fnVisitV(Visitor& vis, std::conditional_t, const void, void>* src) +{ + vis(*static_cast(src)); +} + +template +auto visit(Visitor&& vis, const Variant& var) +{ + static_assert(std::conjunction_v...>, XorStr("visitor must accept every alternative as an argument")); + + using Result = std::invoke_result_t::first_alternative>; + static_assert(std::conjunction_v>...>, + XorStr("visitor result type must be consistent between alternatives")); + + if constexpr (std::is_same_v) + { + using FnVisitV = void (*)(Visitor&, const void*); + static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV...}; + + tableVisit[var.typeId](vis, &var.storage); + } + else + { + using FnVisitR = void (*)(Visitor&, Result&, const void*); + static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR...}; + + Result res; + tableVisit[var.typeId](vis, res, &var.storage); + return res; + } +} + +template +auto visit(Visitor&& vis, Variant& var) +{ + static_assert(std::conjunction_v...>, XorStr("visitor must accept every alternative as an argument")); + + using Result = std::invoke_result_t::first_alternative&>; + static_assert(std::conjunction_v>...>, + XorStr("visitor result type must be consistent between alternatives")); + + if constexpr (std::is_same_v) + { + using FnVisitV = void (*)(Visitor&, void*); + static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV...}; + + tableVisit[var.typeId](vis, &var.storage); + } + else + { + using FnVisitR = void (*)(Visitor&, Result&, void*); + static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR...}; + + Result res; + tableVisit[var.typeId](vis, res, &var.storage); + return res; + } +} + +template +inline constexpr bool always_false_v = false; + +} // namespace lluz diff --git a/Analysis/include/lluz/VisitTypeVar.h b/Analysis/include/lluz/VisitTypeVar.h new file mode 100644 index 00000000..88d1a48b --- /dev/null +++ b/Analysis/include/lluz/VisitTypeVar.h @@ -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 + +#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 +auto apply(A tid, const B& t, C& c, F& f) -> decltype(f(tid, t, c)) +{ + return f(tid, t, c); +} + +template +auto apply(A tid, const B& t, C&, F& f) -> decltype(f(tid, t)) +{ + return f(tid, t); +} + +inline bool hasSeen(std::unordered_set& seen, const void* tv) +{ + void* ttv = const_cast(tv); + return !seen.insert(ttv).second; +} + +inline bool hasSeen(DenseHashSet& seen, const void* tv) +{ + void* ttv = const_cast(tv); + + if (seen.contains(ttv)) + return true; + + seen.insert(ttv); + return false; +} + +inline void unsee(std::unordered_set& seen, const void* tv) +{ + void* ttv = const_cast(tv); + seen.erase(ttv); +} + +inline void unsee(DenseHashSet& seen, const void* tv) +{ + // When DenseHashSet is used for 'visitTypeVarOnce', where don't forget visited elements +} + +} // namespace visit_detail + +template +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(ty)) + { + if (visit(ty, *btv)) + traverse(btv->boundTo); + } + + else if (auto ftv = get(ty)) + visit(ty, *ftv); + + else if (auto gtv = get(ty)) + visit(ty, *gtv); + + else if (auto etv = get(ty)) + visit(ty, *etv); + + else if (auto ctv = get(ty)) + { + if (visit(ty, *ctv)) + { + for (TypeId part : ctv->parts) + traverse(part); + } + } + + else if (auto ptv = get(ty)) + visit(ty, *ptv); + + else if (auto ftv = get(ty)) + { + if (visit(ty, *ftv)) + { + traverse(ftv->argTypes); + traverse(ftv->retTypes); + } + } + + else if (auto ttv = get(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(ty)) + { + if (visit(ty, *mtv)) + { + traverse(mtv->table); + traverse(mtv->metatable); + } + } + + else if (auto ctv = get(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(ty)) + visit(ty, *atv); + + else if (auto utv = get(ty)) + { + if (visit(ty, *utv)) + { + for (TypeId optTy : utv->options) + traverse(optTy); + } + } + + else if (auto itv = get(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(tp)) + { + if (visit(tp, *btv)) + traverse(btv->boundTo); + } + + else if (auto ftv = get(tp)) + visit(tp, *ftv); + + else if (auto gtv = get(tp)) + visit(tp, *gtv); + + else if (auto etv = get(tp)) + visit(tp, *etv); + + else if (auto pack = get(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(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> +{ +}; + +/// 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> +{ + TypeVarOnceVisitor() + : GenericTypeVarVisitor{DenseHashSet{nullptr}} + { + } +}; + +} // namespace lluz diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 1124c29e..1cb6e8f9 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -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 -namespace Luau +namespace lluz { namespace { - -struct AutocompleteNodeFinder : public AstVisitor -{ - const Position pos; - std::vector 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()) - 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()) - 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()) - 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 findAncestryAtPositionForAutocomplete(const SourceModule& source, Position pos) -{ - AutocompleteNodeFinder finder{pos, source.root}; - source.root->visit(&finder); - return finder.ancestry; -} - std::vector findAstAncestryOfPosition(const SourceModule& source, Position pos) { const Position end = source.root->location.end; @@ -215,7 +110,7 @@ std::vector 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 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 getDocumentationSymbolAtPosition(const Source return std::nullopt; } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index a57a789f..98c33e65 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -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 #include #include -LUAU_FASTFLAG(LuauSelfCallAutocompleteFix3) +lluz_FASTFLAG(LluSelfCallAutocompleteFix2) static const std::unordered_set 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 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()) + 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()) + 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()) + 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& nodes) { @@ -52,7 +151,7 @@ static ParenthesesRecommendation getParenRecommendationForFunc(const FunctionTyp auto idxExpr = nodes.back()->as(); 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& nodes) { ParenthesesRecommendation rec = ParenthesesRecommendation::None; - for (Luau::TypeId partId : intersect->parts) + for (lluz::TypeId partId : intersect->parts) { - if (auto partFunc = Luau::get(partId)) + if (auto partFunc = lluz::get(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(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 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& nodes, AutocompleteEntryMap& result, std::unordered_set& seen, std::optional 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(ty)](Luau::TypeId type) { - LUAU_ASSERT(!FFlag::LuauSelfCallAutocompleteFix3); + auto isWrongIndexer_DEPRECATED = [indexType, useStrictFunctionIndexers = !!get(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(Luau::follow(subType))) + if (const FunctionTypeVar* ftv = get(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(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 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 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(Luau::follow(subType))) + if (const FunctionTypeVar* ftv = get(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(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 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(ty); pt && FFlag::LuauSelfCallAutocompleteFix3) + else if (auto pt = get(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(get(ty))) + else if (FFlag::LluSelfCallAutocompleteFix2 && get(get(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& 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 functionIsExpectedAt(const Module& module, AstNode* n if (const IntersectionTypeVar* itv = get(expectedType)) { return std::all_of(begin(itv->parts), end(itv->parts), [](auto&& ty) { - return get(Luau::follow(ty)) != nullptr; + return get(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(); statForIn && !statForIn->hasEnd) - result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword}); + result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword}); if (AstStatFor* statFor = (*it)->as(); statFor && !statFor->hasEnd) - result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword}); + result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword}); if (AstStatIf* statIf = (*it)->as(); statIf && !statIf->hasEnd) - result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword}); + result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword}); if (AstStatWhile* statWhile = (*it)->as(); statWhile && !statWhile->hasEnd) - result.emplace("end", AutocompleteEntry{AutocompleteEntryKind::Keyword}); + result.emplace(XorStr("end"), AutocompleteEntry{AutocompleteEntryKind::Keyword}); if (AstExprFunction* exprFunction = (*it)->as(); 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(); 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(); statIf != nullptr && !statIf->elsebody && iter[2]->is() && iter[1]->is() && 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(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& 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 getMethodContainingClass(const ModuleP return std::nullopt; } - Luau::TypeId parentType = Luau::follow(*parentIt); + lluz::TypeId parentType = lluz::follow(*parentIt); - if (auto parentClass = Luau::get(parentType)) + if (auto parentClass = lluz::get(parentType)) { return parentClass; } - if (auto parentUnion = Luau::get(parentType)) + if (auto parentUnion = lluz::get(parentType)) { return returnFirstNonnullOptionOfType(parentUnion); } @@ -1350,17 +1450,17 @@ static std::optional autocompleteStringParams(const Source return std::nullopt; }; - auto followedId = Luau::follow(*it); - if (auto functionType = Luau::get(followedId)) + auto followedId = lluz::follow(*it); + if (auto functionType = lluz::get(followedId)) { return performCallback(functionType); } - if (auto intersect = Luau::get(followedId)) + if (auto intersect = lluz::get(followedId)) { for (TypeId part : intersect->parts) { - if (auto candidateFunctionType = Luau::get(part)) + if (auto candidateFunctionType = lluz::get(part)) { if (std::optional ret = performCallback(candidateFunctionType)) { @@ -1379,20 +1479,21 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M if (isWithinComment(sourceModule, position)) return {}; - std::vector 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(); 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()) @@ -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()) { 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()) { - return {autocompleteTypeNames(*module, position, ancestry), ancestry}; + return {autocompleteTypeNames(*module, position, finder.ancestry), finder.ancestry}; } else if (AstStatLocal* statLocal = node->as()) { 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(ancestry)) + else if (AstStatFor* statFor = extractStat(finder.ancestry)) { if (!statFor->hasDo || position < statFor->doLocation.begin) { if (!statFor->from->is() && !statFor->to->is() && (!statFor->step || !statFor->step->is())) - 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(); statForIn && (node->is() || 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(ancestry)) + else if (AstStatForIn* statForIn = extractStat(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(); node->is() && statWhile) { if (!statWhile->hasDo && !statWhile->condition->is() && 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(ancestry); statWhile && !statWhile->hasDo) - return {{{"do", AutocompleteEntry{AutocompleteEntryKind::Keyword}}}, ancestry}; + else if (AstStatWhile* statWhile = extractStat(finder.ancestry); statWhile && !statWhile->hasDo) + return {{{XorStr("do"), AutocompleteEntry{ + AutocompleteEntryKind::Keyword}}}, finder.ancestry}; else if (AstStatIf* statIf = node->as(); 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(); statIf && node->is()) { if (statIf->condition->is()) - 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(ancestry); + else if (AstStatIf* statIf = extractStat(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(); statRepeat && statRepeat->condition->is()) - return {autocompleteExpression(sourceModule, *module, typeChecker, typeArena, ancestry, position), ancestry}; - else if (AstStatRepeat* statRepeat = extractStat(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(finder.ancestry); statRepeat) + return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry}; else if (AstExprTable* exprTable = parent->as(); exprTable && (node->is() || node->is())) { 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() || parent->is())) - return {autocompleteStatement(sourceModule, *module, ancestry, position), ancestry}; + return {autocompleteStatement(sourceModule, *module, finder.ancestry, position), finder.ancestry}; - if (std::optional ret = autocompleteStringParams(sourceModule, module, ancestry, position, callback)) + if (std::optional ret = autocompleteStringParams(sourceModule, module, finder.ancestry, position, callback)) { - return {*ret, ancestry}; + return {*ret, finder.ancestry}; } else if (node->is()) { @@ -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()) + if (auto idxExpr = finder.ancestry.at(finder.ancestry.size() - 2)->as()) { 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()) + else if (auto binExpr = finder.ancestry.at(finder.ancestry.size() - 2)->as()) { 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()) @@ -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(); + 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 diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index aeba2c13..807e4d90 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -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 -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> magicFunctionSelect( @@ -79,12 +78,12 @@ TypeId makeFunction(TypeArena& arena, std::optional 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(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 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 stringMetatableTy = getMetatable(getSingletonTypes().stringType); - LUAU_ASSERT(stringMetatableTy); + lluz_ASSERT(stringMetatableTy); const TableTypeVar* stringMetatableTable = get(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(t: Table, 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(t: Table) -> ((Table, K?) -> (K, V), Table, 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({ @metatable MT }, MT) -> { @metatable MT } // clang-format off - // setmetatable(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(getGlobalBinding(typeChecker, "table"))) + if (TableTypeVar* ttv = getMutable(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> magicFunctionSelect( @@ -273,7 +272,7 @@ static std::optional> 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> magicFunctionSelect( return WithPredicate{*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()) { @@ -310,12 +309,6 @@ static std::optional> magicFunctionSetMetaTable( { auto [paramPack, _predicates] = withPredicate; - if (FFlag::LuauUnknownAndNeverType) - { - if (size(paramPack) < 2 && finite(paramPack)) - return std::nullopt; - } - TypeArena& arena = typechecker.currentModule->internalTypes; std::vector expectedArgs = typechecker.unTypePack(scope, paramPack, 2, expr.location); @@ -323,12 +316,6 @@ static std::optional> 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(target)) { if (target->persistent) @@ -337,8 +324,7 @@ static std::optional> magicFunctionSetMetaTable( } else { - if (!FFlag::LuauUnknownAndNeverType) - typechecker.tablify(mt); + typechecker.tablify(mt); const TableTypeVar* mtTtv = get(mt); MetatableTypeVar mtv{target, mt}; @@ -350,20 +336,17 @@ static std::optional> 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{}; + return WithPredicate{}; } - if (!FFlag::LuauSetMetaTableArgsCheck || !expr.self) + if (!FFlag::LluSetMetaTableArgsCheck || !expr.self) { AstExpr* targetExpr = expr.args.data[0]; if (AstExprLocal* targetLocal = targetExpr->as()) @@ -381,7 +364,7 @@ static std::optional> 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{arena.addTypePack({target})}; @@ -407,21 +390,11 @@ static std::optional> magicFunctionAssert( if (head.size() > 0) { - auto [ty, ok] = typechecker.pickTypesFromSense(head[0], true); - if (FFlag::LuauUnknownAndNeverType) - { - if (get(*ty)) - head = {*ty}; - else - head[0] = *ty; - } + std::optional 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{arena.addTypePack(TypePack{std::move(head), tail})}; @@ -469,16 +442,16 @@ static std::optional> 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(); 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> 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> magicFunctionRequire( return std::nullopt; } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index 88c50318..f23751f7 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -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& 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(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& 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(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(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(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(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(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 diff --git a/Analysis/src/Config.cpp b/Analysis/src/Config.cpp index 35a2259d..4ad89a7a 100644 --- a/Analysis/src/Config.cpp +++ b/Analysis/src/Config.cpp @@ -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; } -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 @@ -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& 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 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 diff --git a/Analysis/src/Constraint.cpp b/Analysis/src/Constraint.cpp index 64e3a666..8c89b003 100644 --- a/Analysis/src/Constraint.cpp +++ b/Analysis/src/Constraint.cpp @@ -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 diff --git a/Analysis/src/ConstraintGraphBuilder.cpp b/Analysis/src/ConstraintGraphBuilder.cpp index efaeff6c..6292d77b 100644 --- a/Analysis/src/ConstraintGraphBuilder.cpp +++ b/Analysis/src/ConstraintGraphBuilder.cpp @@ -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 ice, NotNull globalScope) + const ModuleName& moduleName, TypeArena* arena, NotNull ice, NotNull 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 scope) { - return arena->addType(FreeTypeVar{scope.get()}); + return arena->addType(FreeTypeVar{scope}); } -TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope) +TypePackId ConstraintGraphBuilder::freshTypePack(NotNull scope) { - FreeTypePack f{scope.get()}; + FreeTypePack f{scope}; return arena->addTypePack(TypePackVar{std::move(f)}); } -ScopePtr ConstraintGraphBuilder::childScope(Location location, const ScopePtr& parent) +NotNull ConstraintGraphBuilder::childScope(Location location, NotNull parent) { - auto scope = std::make_shared(parent); - scopes.emplace_back(location, scope); + auto scope = std::make_unique(); + NotNull 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 scope, ConstraintV cv) { scope->constraints.emplace_back(new Constraint{std::move(cv)}); } -void ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr c) +void ConstraintGraphBuilder::addConstraint(NotNull scope, std::unique_ptr 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(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()); + rootScope = scopes.back().second.get(); + NotNull 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 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 scope, AstStat* stat) { - RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit}; + RecursionLimiter limiter{&recursionCount, FInt::LluCheckRecursionLimit}; if (auto s = stat->as()) visit(scope, s); else if (auto s = stat->as()) visit(scope, s); - else if (auto s = stat->as()) - visit(scope, s); else if (auto f = stat->as()) visit(scope, f); else if (auto f = stat->as()) @@ -118,27 +118,25 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat) else if (auto a = stat->as()) visit(scope, a); else - LUAU_ASSERT(0); + lluz_ASSERT(0); } -void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local) +void ConstraintGraphBuilder::visit(NotNull scope, AstStatLocal* local) { std::vector 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) +void addConstraints(Constraint* constraint, NotNull 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 childScope : scope->children) + for (NotNull childScope : scope->children) addConstraints(constraint, childScope); } -void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function) +void ConstraintGraphBuilder::visit(NotNull 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 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 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()) { @@ -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()) { @@ -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 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 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 scope, AstStatBlock* block) { - ScopePtr innerScope = childScope(block->location, scope); + NotNull 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 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 scope, AstStatIf* ifStatement) { check(scope, ifStatement->condition); - ScopePtr thenScope = childScope(ifStatement->thenbody->location, scope); + NotNull thenScope = childScope(ifStatement->thenbody->location, scope); visit(thenScope, ifStatement->thenbody); if (ifStatement->elsebody) { - ScopePtr elseScope = childScope(ifStatement->elsebody->location, scope); + NotNull elseScope = childScope(ifStatement->elsebody->location, scope); visit(elseScope, ifStatement->elsebody); } } -void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias) +void ConstraintGraphBuilder::visit(NotNull 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 exprs) +TypePackId ConstraintGraphBuilder::checkPack(NotNull scope, AstArray exprs) { if (exprs.size == 0) return arena->addTypePack({}); @@ -387,16 +364,16 @@ TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArrayaddTypePack(TypePack{std::move(types), last}); } -TypePackId ConstraintGraphBuilder::checkExprList(const ScopePtr& scope, const AstArray& exprs) +TypePackId ConstraintGraphBuilder::checkExprList(NotNull scope, const AstArray& exprs) { TypePackId result = arena->addTypePack({}); TypePack* resultPack = getMutable(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 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 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 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 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 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 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 scope, AstExprTable* expr) { TypeId ty = arena->addType(TableTypeVar{}); TableTypeVar* ttv = getMutable(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 parent, AstExprFunction* fn) { - ScopePtr signatureScope = nullptr; - ScopePtr bodyScope = nullptr; + Scope2* signatureScope = nullptr; + Scope2* bodyScope = nullptr; TypePackId returnType = nullptr; std::vector 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> genericDefinitions = createGenerics(signatureScope, fn->generics); - std::vector> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks); + std::vector> genericDefinitions = createGenerics(signatureBorrow, fn->generics); + std::vector> 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 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 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 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 genericTypes; std::vector 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 signatureBorrow = childScope(fn->location, scope); + signatureScope = signatureBorrow.get(); - std::vector> genericDefinitions = createGenerics(signatureScope, fn->generics); - std::vector> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks); + std::vector> genericDefinitions = createGenerics(signatureBorrow, fn->generics); + std::vector> 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 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 scope, AstTypePack* tp) { TypePackId result; if (auto expl = tp->as()) @@ -956,11 +941,11 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTyp } else if (auto gen = tp->as()) { - 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 scope, const AstTypeList& list) { std::vector head; @@ -986,12 +971,12 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const return arena->addTypePack(TypePack{head, tail}); } -std::vector> ConstraintGraphBuilder::createGenerics(const ScopePtr& scope, AstArray generics) +std::vector> ConstraintGraphBuilder::createGenerics(NotNull scope, AstArray generics) { std::vector> 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 defaultTy = std::nullopt; if (generic.defaultValue) @@ -1007,12 +992,12 @@ std::vector> ConstraintGraphBuilder::crea } std::vector> ConstraintGraphBuilder::createGenericPacks( - const ScopePtr& scope, AstArray generics) + NotNull scope, AstArray generics) { std::vector> 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 defaultTy = std::nullopt; if (generic.defaultValue) @@ -1027,7 +1012,7 @@ std::vector> ConstraintGraphBuilder:: return result; } -TypeId ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, TypePackId tp) +TypeId ConstraintGraphBuilder::flattenPack(NotNull 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 globalScope; + const NotNull globalScope; const NotNull arena; - GlobalPrepopulator(NotNull globalScope, NotNull arena) + GlobalPrepopulator(NotNull globalScope, NotNull arena) : globalScope(globalScope) , arena(arena) { @@ -1065,33 +1050,33 @@ struct GlobalPrepopulator : AstVisitor bool visit(AstStatFunction* function) override { if (AstExprGlobal* g = function->name->as()) - 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 globalScope, AstStatBlock* program) { - GlobalPrepopulator gp{NotNull{globalScope.get()}, arena}; + GlobalPrepopulator gp{NotNull{globalScope}, arena}; program->visit(&gp); } -void collectConstraints(std::vector>& result, NotNull scope) +void collectConstraints(std::vector>& result, NotNull scope) { for (const auto& c : scope->constraints) result.push_back(NotNull{c.get()}); - for (NotNull child : scope->children) + for (NotNull child : scope->children) collectConstraints(result, child); } -std::vector> collectConstraints(NotNull rootScope) +std::vector> collectConstraints(NotNull rootScope) { std::vector> result; collectConstraints(result, rootScope); return result; } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 1cd29918..0f6a77d9 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -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, ToStringOptions& opts) +[[maybe_unused]] static void dumpBindings(NotNull 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 child : scope->children) + for (NotNull child : scope->children) dumpBindings(child, opts); } -static void dumpConstraints(NotNull scope, ToStringOptions& opts) +static void dumpConstraints(NotNull scope, ToStringOptions& opts) { for (const ConstraintPtr& c : scope->constraints) { printf("\t%s\n", toString(*c, opts).c_str()); } - for (NotNull child : scope->children) + for (NotNull child : scope->children) dumpConstraints(child, opts); } -void dump(NotNull rootScope, ToStringOptions& opts) +void dump(NotNull 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 rootScope) +ConstraintSolver::ConstraintSolver(TypeArena* arena, NotNull 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 constraint, bool fo else if (auto nc = get(*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 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(*instantiated); @@ -270,7 +270,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull(operandType)) return block(operandType, constraint); - LUAU_ASSERT(get(c.resultType)); + lluz_ASSERT(get(c.resultType)); if (isNumber(operandType) || get(operandType) || get(operandType)) { @@ -278,7 +278,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull 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 diff --git a/Analysis/src/ConstraintSolverLogger.cpp b/Analysis/src/ConstraintSolverLogger.cpp index 0c2517c0..ce443175 100644 --- a/Analysis/src/ConstraintSolverLogger.cpp +++ b/Analysis/src/ConstraintSolverLogger.cpp @@ -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>& unsolvedConstraints) +void ConstraintSolverLogger::captureBoundarySnapshot(const Scope2* rootScope, std::vector>& 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 current, std::vector>& unsolvedConstraints) + const Scope2* rootScope, NotNull current, std::vector>& unsolvedConstraints) { - // LUAU_ASSERT(!preparedSnapshot); + // lluz_ASSERT(!preparedSnapshot); std::string snapshot = "{\"type\":\"step\",\"rootScope\":"; @@ -136,4 +136,4 @@ void ConstraintSolverLogger::commitPreparedStepSnapshot() } } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/EmbeddedBuiltinDefinitions.cpp b/Analysis/src/EmbeddedBuiltinDefinitions.cpp index 45663531..f8bd97e5 100644 --- a/Analysis/src/EmbeddedBuiltinDefinitions.cpp +++ b/Analysis/src/EmbeddedBuiltinDefinitions.cpp @@ -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(value: T): string -- `assert` has a magic function attached that will give more detailed type information declare function assert(value: T, errorMessage: string?): T +declare function error(message: T, level: number?) + declare function tostring(value: T): string declare function tonumber(value: T, radix: number?): number? declare function rawequal(a: T1, b: T2): boolean declare function rawget(tab: {[K]: V}, k: K): V declare function rawset(tab: {[K]: V}, k: K, v: V): {[K]: V} -declare function rawlen(obj: {[K]: V} | string): number declare function setfenv(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(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(message: T, level: number?): never\n"; - else - result += "declare function error(message: T, level: number?)\n"; + // TODO: move this into kBuiltinDefinitionLuaSrc + if (FFlag::LluCheckLenMT) + result += XorStr("declare function rawlen(obj: {[K]: V} | string): number\n"); return result; } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index 93cb65b9..8df558b7 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -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 -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(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(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 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, "Non-exhaustive type switch"); + static_assert(always_false_v, 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 diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 222a17df..75bce95b 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -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 #include #include -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 parseMode(const std::vector& hotcomments) @@ -37,13 +37,13 @@ std::optional parseMode(const std::vector& 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 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 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 pathExprToModuleName(const ModuleName& currentModuleName, const AstExpr& pathExpr) @@ -271,7 +271,7 @@ std::vector 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 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 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 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& 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& 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 enabledLintWarnings) +LintResult Frontend::lint(const ModuleName& name, std::optional 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 enabledLintWarnings) +std::pair Frontend::lintFragment(std::string_view source, std::optional 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 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 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 warnings = Luau::lint(module.root, *module.names, environmentScope, modulePtr.get(), module.hotcomments, options); + std::vector 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* 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(this)->getSourceModule(moduleName); } -NotNull Frontend::getGlobalScope() +NotNull Frontend::getGlobalScope2() { - if (!globalScope) + if (!globalScope2) { const SingletonTypes& singletonTypes = getSingletonTypes(); - globalScope = std::make_unique(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(); + 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(); - 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 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 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 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 to describe the + * We also translate lluz::ParseError into a lluz::TypeError so that we can use a vector 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 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 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 diff --git a/Analysis/src/Instantiation.cpp b/Analysis/src/Instantiation.cpp index 77c62422..f8da03e7 100644 --- a/Analysis/src/Instantiation.cpp +++ b/Analysis/src/Instantiation.cpp @@ -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(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(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 diff --git a/Analysis/src/IostreamHelpers.cpp b/Analysis/src/IostreamHelpers.cpp index e4fac455..cf39fee4 100644 --- a/Analysis/src/IostreamHelpers.cpp +++ b/Analysis/src/IostreamHelpers.cpp @@ -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) stream << "NormalizationTooComplex { }"; else - static_assert(always_false_v, "Non-exhaustive type switch"); + static_assert(always_false_v, 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 diff --git a/Analysis/src/JsonEncoder.cpp b/Analysis/src/JsonEncoder.cpp index ee7dadb3..af8e3ab1 100644 --- a/Analysis/src/JsonEncoder.cpp +++ b/Analysis/src/JsonEncoder.cpp @@ -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 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 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 void write(AstArray 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 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 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& 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 diff --git a/Analysis/src/LValue.cpp b/Analysis/src/LValue.cpp index 38dfe1ae..d0085442 100644 --- a/Analysis/src/LValue.cpp +++ b/Analysis/src/LValue.cpp @@ -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 -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(*current)) acc ^= std::hash{}(*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(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(*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 diff --git a/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index fb952f5e..8c20b23c 100644 --- a/Analysis/src/Linter.cpp +++ b/Analysis/src/Linter.cpp @@ -1,21 +1,21 @@ -// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details -#include "Luau/Linter.h" +// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details +#include "lluz/Linter.h" -#include "Luau/AstQuery.h" -#include "Luau/Module.h" -#include "Luau/Scope.h" -#include "Luau/TypeInfer.h" -#include "Luau/StringUtils.h" -#include "Luau/Common.h" +#include "lluz/AstQuery.h" +#include "lluz/Module.h" +#include "lluz/Scope.h" +#include "lluz/TypeInfer.h" +#include "lluz/StringUtils.h" +#include "lluz/Common.h" #include #include #include -LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4) -LUAU_FASTFLAGVARIABLE(LuauLintGlobalNeverReadBeforeWritten, false) +lluz_FASTINTVARIABLE(LluSuggestionDistance, 4) +lluz_FASTFLAGVARIABLE(LluLintGlobalNeverReadBeforeWritten, false) -namespace Luau +namespace lluz { // clang-format off @@ -51,7 +51,7 @@ static const char* kWarningNames[] = { }; // clang-format on -static_assert(std::size(kWarningNames) == unsigned(LintWarning::Code__Count), "did you forget to add warning to the list?"); +static_assert(std::size(kWarningNames) == unsigned(LintWarning::Code__Count), XorStr("did you forget to add warning to the list?")); struct LintContext { @@ -125,7 +125,7 @@ struct WarningComparator } }; -LUAU_PRINTF_ATTR(4, 5) +lluz_PRINTF_ATTR(4, 5) static void emitWarning(LintContext& context, LintWarning::Code code, const Location& location, const char* format, ...) { if (!context.warningEnabled(code)) @@ -205,7 +205,7 @@ static bool similar(AstExpr* lhs, AstExpr* rhs) CASE(AstExprIfElse) return similar(le->condition, re->condition) && similar(le->trueExpr, re->trueExpr) && similar(le->falseExpr, re->falseExpr); else { - LUAU_ASSERT(!"Unknown expression type"); + lluz_ASSERT(!XorStr("Unknown expression type")); return false; } @@ -215,7 +215,7 @@ static bool similar(AstExpr* lhs, AstExpr* rhs) class LintGlobalLocal : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintGlobalLocal pass; pass.context = &context; @@ -282,14 +282,14 @@ private: Global* g = globals.find(gv->name); if (!g || (!g->assigned && !g->builtin)) - emitWarning(*context, LintWarning::Code_UnknownGlobal, gv->location, "Unknown global '%s'", gv->name.value); + emitWarning(*context, LintWarning::Code_UnknownGlobal, gv->location, XorStr("Unknown global '%s'"), gv->name.value); else if (g->deprecated) { if (*g->deprecated) - emitWarning(*context, LintWarning::Code_DeprecatedGlobal, gv->location, "Global '%s' is deprecated, use '%s' instead", + emitWarning(*context, LintWarning::Code_DeprecatedGlobal, gv->location, XorStr("Global '%s' is deprecated, use '%s' instead"), gv->name.value, *g->deprecated); else - emitWarning(*context, LintWarning::Code_DeprecatedGlobal, gv->location, "Global '%s' is deprecated", gv->name.value); + emitWarning(*context, LintWarning::Code_DeprecatedGlobal, gv->location, XorStr("Global '%s' is deprecated"), gv->name.value); } } @@ -310,7 +310,7 @@ private: "Global '%s' is only used in the enclosing function defined at line %d; consider changing it to local", g.firstRef->name.value, top->location.begin.line + 1); } - else if (FFlag::LuauLintGlobalNeverReadBeforeWritten && g.assigned && !g.readBeforeWritten && !g.definedInModuleScope && + else if (FFlag::LluLintGlobalNeverReadBeforeWritten && g.assigned && !g.readBeforeWritten && !g.definedInModuleScope && g.firstRef->name != context->placeholder) { emitWarning(*context, LintWarning::Code_GlobalUsedAsLocal, g.firstRef->location, @@ -332,7 +332,7 @@ private: bool visit(AstExprGlobal* node) override { - if (FFlag::LuauLintGlobalNeverReadBeforeWritten && !functionStack.empty() && !functionStack.back().dominatedGlobals.contains(node->name)) + if (FFlag::LluLintGlobalNeverReadBeforeWritten && !functionStack.empty() && !functionStack.back().dominatedGlobals.contains(node->name)) { Global& g = globals[node->name]; g.readBeforeWritten = true; @@ -341,7 +341,7 @@ private: if (node->name == context->placeholder) emitWarning( - *context, LintWarning::Code_PlaceholderRead, node->location, "Placeholder value '_' is read here; consider using a named variable"); + *context, LintWarning::Code_PlaceholderRead, node->location, XorStr("Placeholder value '_' is read here; consider using a named variable")); return true; } @@ -350,7 +350,7 @@ private: { if (node->local->name == context->placeholder) emitWarning( - *context, LintWarning::Code_PlaceholderRead, node->location, "Placeholder value '_' is read here; consider using a named variable"); + *context, LintWarning::Code_PlaceholderRead, node->location, XorStr("Placeholder value '_' is read here; consider using a named variable")); return true; } @@ -365,7 +365,7 @@ private: { Global& g = globals[gv->name]; - if (FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (FFlag::LluLintGlobalNeverReadBeforeWritten) { if (functionStack.empty()) { @@ -416,7 +416,7 @@ private: else { g.assigned = true; - if (FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (FFlag::LluLintGlobalNeverReadBeforeWritten) { g.definedAsFunction = true; g.definedInModuleScope = functionStack.empty(); @@ -454,7 +454,7 @@ private: bool visit(AstStatIf* node) override { - if (!FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (!FFlag::LluLintGlobalNeverReadBeforeWritten) return true; HoldConditionalExecution ce(*this); @@ -468,7 +468,7 @@ private: bool visit(AstStatWhile* node) override { - if (!FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (!FFlag::LluLintGlobalNeverReadBeforeWritten) return true; HoldConditionalExecution ce(*this); @@ -480,7 +480,7 @@ private: bool visit(AstStatRepeat* node) override { - if (!FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (!FFlag::LluLintGlobalNeverReadBeforeWritten) return true; HoldConditionalExecution ce(*this); @@ -492,7 +492,7 @@ private: bool visit(AstStatFor* node) override { - if (!FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (!FFlag::LluLintGlobalNeverReadBeforeWritten) return true; HoldConditionalExecution ce(*this); @@ -509,7 +509,7 @@ private: bool visit(AstStatForIn* node) override { - if (!FFlag::LuauLintGlobalNeverReadBeforeWritten) + if (!FFlag::LluLintGlobalNeverReadBeforeWritten) return true; HoldConditionalExecution ce(*this); @@ -562,7 +562,7 @@ private: class LintSameLineStatement : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintSameLineStatement pass; @@ -599,7 +599,7 @@ private: continue; emitWarning(*context, LintWarning::Code_SameLineStatement, location, - "A new statement is on the same line; add semi-colon on previous statement to silence"); + XorStr("A new statement is on the same line; add semi-colon on previous statement to silence")); lastLine = location.begin.line; } @@ -611,7 +611,7 @@ private: class LintMultiLineStatement : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintMultiLineStatement pass; pass.context = &context; @@ -646,7 +646,7 @@ private: if (location.begin.column <= top.start.begin.column) { emitWarning( - *context, LintWarning::Code_MultiLineStatement, location, "Statement spans multiple lines; use indentation to silence"); + *context, LintWarning::Code_MultiLineStatement, location, XorStr("Statement spans multiple lines; use indentation to silence")); top.flagged = true; } @@ -691,7 +691,7 @@ private: class LintLocalHygiene : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintLocalHygiene pass; pass.context = &context; @@ -760,7 +760,7 @@ private: // don't warn on inter-function shadowing since it is much more fragile wrt refactoring if (shadow->functionDepth == local->functionDepth) - emitWarning(*context, LintWarning::Code_LocalShadow, local->location, "Variable '%s' shadows previous declaration at line %d", + emitWarning(*context, LintWarning::Code_LocalShadow, local->location, XorStr("Variable '%s' shadows previous declaration at line %d"), local->name.value, shadow->location.begin.line + 1); } else if (Global* global = globals.find(local->name)) @@ -769,12 +769,12 @@ private: ; // there are many builtins with common names like 'table'; some of them are deprecated as well else if (global->firstRef) { - emitWarning(*context, LintWarning::Code_LocalShadow, local->location, "Variable '%s' shadows a global variable used at line %d", + emitWarning(*context, LintWarning::Code_LocalShadow, local->location, XorStr("Variable '%s' shadows a global variable used at line %d"), local->name.value, global->firstRef->location.begin.line + 1); } else { - emitWarning(*context, LintWarning::Code_LocalShadow, local->location, "Variable '%s' shadows a global variable", local->name.value); + emitWarning(*context, LintWarning::Code_LocalShadow, local->location, XorStr("Variable '%s' shadows a global variable"), local->name.value); } } } @@ -785,13 +785,13 @@ private: return; if (info.function) - emitWarning(*context, LintWarning::Code_FunctionUnused, local->location, "Function '%s' is never used; prefix with '_' to silence", + emitWarning(*context, LintWarning::Code_FunctionUnused, local->location, XorStr("Function '%s' is never used; prefix with '_' to silence"), local->name.value); else if (info.import) - emitWarning(*context, LintWarning::Code_ImportUnused, local->location, "Import '%s' is never used; prefix with '_' to silence", + emitWarning(*context, LintWarning::Code_ImportUnused, local->location, XorStr("Import '%s' is never used; prefix with '_' to silence"), local->name.value); else - emitWarning(*context, LintWarning::Code_LocalUnused, local->location, "Variable '%s' is never used; prefix with '_' to silence", + emitWarning(*context, LintWarning::Code_LocalUnused, local->location, XorStr("Variable '%s' is never used; prefix with '_' to silence"), local->name.value); } @@ -805,7 +805,7 @@ private: if (!glob) return false; - return glob->name == "require"; + return glob->name == XorStr("require"); } bool visit(AstStatAssign* node) override @@ -893,7 +893,7 @@ private: AstLocal* astLocal = imports[*node->prefix]; Local& local = locals[astLocal]; - LUAU_ASSERT(local.import); + lluz_ASSERT(local.import); local.used = true; return true; @@ -914,7 +914,7 @@ private: class LintUnusedFunction : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintUnusedFunction pass; pass.context = &context; @@ -946,7 +946,7 @@ private: for (auto& g : globals) { if (g.second.function && !g.second.used && g.first.value[0] != '_') - emitWarning(*context, LintWarning::Code_FunctionUnused, g.second.location, "Function '%s' is never used; prefix with '_' to silence", + emitWarning(*context, LintWarning::Code_FunctionUnused, g.second.location, XorStr("Function '%s' is never used; prefix with '_' to silence"), g.first.value); } } @@ -981,7 +981,7 @@ private: class LintUnreachableCode : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintUnreachableCode pass; pass.context = &context; @@ -1010,19 +1010,19 @@ private: switch (status) { case Continue: - return "continue"; + return XorStr("continue"); case Break: - return "break"; + return XorStr("break"); case Return: - return "return"; + return XorStr("return"); case Error: - return "error"; + return XorStr("error"); default: - return "unknown"; + return XorStr("unknown"); } } @@ -1046,7 +1046,7 @@ private: if (step == Error && si->is() && next->is() && i + 2 == stat->body.size) return Error; - emitWarning(*context, LintWarning::Code_UnreachableCode, next->location, "Unreachable code (previous statement always %ss)", + emitWarning(*context, LintWarning::Code_UnreachableCode, next->location, XorStr("Unreachable code (previous statement always %ss)"), getReason(step)); return step; } @@ -1122,7 +1122,7 @@ private: class LintUnknownType : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintUnknownType pass; pass.context = &context; @@ -1143,11 +1143,11 @@ private: TypeKind getTypeKind(const std::string& name) { - if (name == "nil" || name == "boolean" || name == "userdata" || name == "number" || name == "string" || name == "table" || - name == "function" || name == "thread") + if (name == XorStr("nil") || name == XorStr("boolean") || name == XorStr("userdata") || name == XorStr("number") || name == XorStr("string") || name == XorStr("table") || + name == XorStr("function") || name == XorStr("thread")) return Kind_Primitive; - if (name == "vector") + if (name == XorStr("vector")) return Kind_Vector; if (std::optional maybeTy = context->scope->lookupType(name)) @@ -1163,7 +1163,7 @@ private: if (kind == Kind_Unknown) { - emitWarning(*context, LintWarning::Code_UnknownType, expr->location, "Unknown type '%s'", name.c_str()); + emitWarning(*context, LintWarning::Code_UnknownType, expr->location, XorStr("Unknown type '%s'"), name.c_str()); return; } @@ -1173,7 +1173,7 @@ private: return; } - emitWarning(*context, LintWarning::Code_UnknownType, expr->location, "Unknown type '%s' (expected %s)", name.c_str(), expectedString); + emitWarning(*context, LintWarning::Code_UnknownType, expr->location, XorStr("Unknown type '%s' (expected %s)"), name.c_str(), expectedString); } bool visit(AstExprBinary* node) override @@ -1193,13 +1193,13 @@ private: { AstExprGlobal* g = call->func->as(); - if (g && g->name == "type") + if (g && g->name == XorStr("type")) { - validateType(arg, {Kind_Primitive, Kind_Vector}, "primitive type"); + validateType(arg, {Kind_Primitive, Kind_Vector}, XorStr("primitive type")); } - else if (g && g->name == "typeof") + else if (g && g->name == XorStr("typeof")) { - validateType(arg, {Kind_Primitive, Kind_Userdata}, "primitive or userdata type"); + validateType(arg, {Kind_Primitive, Kind_Userdata}, XorStr("primitive or userdata type")); } } } @@ -1211,7 +1211,7 @@ private: class LintForRange : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintForRange pass; pass.context = &context; @@ -1242,22 +1242,22 @@ private: // for i=#t,1 do if (fu && fu->op == AstExprUnary::Len && tc && tc->value == 1.0) emitWarning( - *context, LintWarning::Code_ForRange, rangeLocation, "For loop should iterate backwards; did you forget to specify -1 as step?"); + *context, LintWarning::Code_ForRange, rangeLocation, XorStr("For loop should iterate backwards; did you forget to specify -1 as step?")); // for i=8,1 do else if (fc && tc && fc->value > tc->value) emitWarning( - *context, LintWarning::Code_ForRange, rangeLocation, "For loop should iterate backwards; did you forget to specify -1 as step?"); + *context, LintWarning::Code_ForRange, rangeLocation, XorStr("For loop should iterate backwards; did you forget to specify -1 as step?")); // for i=1,8.75 do else if (fc && tc && getLoopEnd(fc->value, tc->value) != tc->value) - emitWarning(*context, LintWarning::Code_ForRange, rangeLocation, "For loop ends at %g instead of %g; did you forget to specify step?", + emitWarning(*context, LintWarning::Code_ForRange, rangeLocation, XorStr("For loop ends at %g instead of %g; did you forget to specify step?"), getLoopEnd(fc->value, tc->value), tc->value); // for i=0,#t do else if (fc && tu && fc->value == 0.0 && tu->op == AstExprUnary::Len) - emitWarning(*context, LintWarning::Code_ForRange, rangeLocation, "For loop starts at 0, but arrays start at 1"); + emitWarning(*context, LintWarning::Code_ForRange, rangeLocation, XorStr("For loop starts at 0, but arrays start at 1")); // for i=#t,0 do else if (fu && fu->op == AstExprUnary::Len && tc && tc->value == 0.0) emitWarning(*context, LintWarning::Code_ForRange, rangeLocation, - "For loop should iterate backwards; did you forget to specify -1 as step? Also consider changing 0 to 1 since arrays start at 1"); + XorStr("For loop should iterate backwards; did you forget to specify -1 as step? Also consider changing 0 to 1 since arrays start at 1")); } return true; @@ -1267,7 +1267,7 @@ private: class LintUnbalancedAssignment : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintUnbalancedAssignment pass; pass.context = &context; @@ -1316,7 +1316,7 @@ private: class LintImplicitReturn : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintImplicitReturn pass; pass.context = &context; @@ -1393,7 +1393,7 @@ private: class LintFormatString : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintFormatString pass; pass.context = &context; @@ -1468,10 +1468,10 @@ private: } if (i == size) - return "unfinished format specifier"; + return XorStr("unfinished format specifier"); if (!strchr(options, data[i])) - return "invalid format specifier: must be a string format specifier or %"; + return XorStr("invalid format specifier: must be a string format specifier or %"); } } @@ -1486,16 +1486,16 @@ private: for (size_t i = 0; i < size; ++i) { if (!strchr(options, data[i])) - return "unexpected character; must be a pack specifier or space"; + return XorStr("unexpected character; must be a pack specifier or space"); if (data[i] == 'c' && (i + 1 == size || !isDigit(data[i + 1]))) - return "fixed-sized string format must specify the size"; + return XorStr("fixed-sized string format must specify the size"); if (data[i] == 'X' && (i + 1 == size || strchr(unsized, data[i + 1]))) - return "X must be followed by a size specifier"; + return XorStr("X must be followed by a size specifier"); if (fixed && (data[i] == 'z' || data[i] == 's')) - return "pack specifier must be fixed-size"; + return XorStr("pack specifier must be fixed-size"); if ((data[i] == '!' || data[i] == 'i' || data[i] == 'I' || data[i] == 'c' || data[i] == 's') && i + 1 < size && isDigit(data[i + 1])) { @@ -1509,10 +1509,10 @@ private: } if (i + 1 < size && isDigit(data[i + 1])) - return "size specifier is too large"; + return XorStr("size specifier is too large"); if (!isc && (v == 0 || v > 16)) - return "integer size must be in range [1,16]"; + return XorStr("integer size must be in range [1,16]"); } } @@ -1528,32 +1528,32 @@ private: i++; if (i == size) - return "unfinished character class"; + return XorStr("unfinished character class"); if (isDigit(data[i])) { - return "sets can not contain capture references"; + return XorStr("sets can not contain capture references"); } else if (isAlpha(data[i])) { // lower case lookup - upper case for every character class is defined as its inverse if (!strchr(classes, data[i] | ' ')) - return "invalid character class, must refer to a defined class or its inverse"; + return XorStr("invalid character class, must refer to a defined class or its inverse"); } else { // technically % can escape any non-alphanumeric character but this is error-prone if (!strchr(magic, data[i])) - return "expected a magic character after %"; + return XorStr("expected a magic character after %"); } if (i + 1 < size && data[i + 1] == '-') - return "character range can't include character sets"; + return XorStr("character range can't include character sets"); } else if (data[i] == '-') { if (i + 1 < size && data[i + 1] == '%') - return "character range can't include character sets"; + return XorStr("character range can't include character sets"); } } @@ -1575,35 +1575,35 @@ private: i++; if (i == size) - return "unfinished character class"; + return XorStr("unfinished character class"); if (isDigit(data[i])) { if (data[i] == '0') - return "invalid capture reference, must be 1-9"; + return XorStr("invalid capture reference, must be 1-9"); int captureIndex = data[i] - '0'; if (captureIndex > totalCaptures) - return "invalid capture reference, must refer to a valid capture"; + return XorStr("invalid capture reference, must refer to a valid capture"); for (int open : openCaptures) if (open == captureIndex) - return "invalid capture reference, must refer to a closed capture"; + return XorStr("invalid capture reference, must refer to a closed capture"); } else if (isAlpha(data[i])) { if (data[i] == 'b') { if (i + 2 >= size) - return "missing brace characters for balanced match"; + return XorStr("missing brace characters for balanced match"); i += 2; } else if (data[i] == 'f') { if (i + 1 >= size || data[i + 1] != '[') - return "missing set after a frontier pattern"; + return XorStr("missing set after a frontier pattern"); // we can parse the set with the regular logic } @@ -1611,14 +1611,14 @@ private: { // lower case lookup - upper case for every character class is defined as its inverse if (!strchr(classes, data[i] | ' ')) - return "invalid character class, must refer to a defined class or its inverse"; + return XorStr("invalid character class, must refer to a defined class or its inverse"); } } else { // technically % can escape any non-alphanumeric character but this is error-prone if (!strchr(magic, data[i])) - return "expected a magic character after %"; + return XorStr("expected a magic character after %"); } } else if (data[i] == '[') @@ -1643,12 +1643,12 @@ private: } if (j == size) - return "expected ] at the end of the string to close a set"; + return XorStr("expected ] at the end of the string to close a set"); if (const char* error = checkStringMatchSet(data + i + 1, j - i - 1, magic, classes)) return error; - LUAU_ASSERT(data[j] == ']'); + lluz_ASSERT(data[j] == ']'); i = j; } else if (data[i] == '(') @@ -1659,13 +1659,13 @@ private: else if (data[i] == ')') { if (openCaptures.empty()) - return "unexpected ) without a matching ("; + return XorStr("unexpected ) without a matching ("); openCaptures.pop_back(); } } if (!openCaptures.empty()) - return "expected ) at the end of the string to close a capture"; + return XorStr("expected ) at the end of the string to close a capture"); if (outCaptures) *outCaptures = totalCaptures; @@ -1682,13 +1682,13 @@ private: i++; if (i == size) - return "unfinished replacement"; + return XorStr("unfinished replacement"); if (data[i] != '%' && !isDigit(data[i])) - return "unexpected replacement character; must be a digit or %"; + return XorStr("unexpected replacement character; must be a digit or %"); if (isDigit(data[i]) && captures >= 0 && data[i] - '0' > captures) - return "invalid capture index, must refer to pattern capture"; + return XorStr("invalid capture index, must refer to pattern capture"); } } @@ -1706,14 +1706,14 @@ private: i++; if (i == size) - return "unfinished replacement"; + return XorStr("unfinished replacement"); if (data[i] != '%' && !strchr(options, data[i])) - return "unexpected replacement character; must be a date format specifier or %"; + return XorStr("unexpected replacement character; must be a date format specifier or %"); } if (data[i] == 0) - return "date format can not contain null characters"; + return XorStr("date format can not contain null characters"); } return nullptr; @@ -1721,31 +1721,31 @@ private: void matchStringCall(AstName name, AstExpr* self, AstArray args) { - if (name == "format") + if (name == XorStr("format")) { if (AstExprConstantString* fmt = self->as()) if (const char* error = checkStringFormat(fmt->value.data, fmt->value.size)) - emitWarning(*context, LintWarning::Code_FormatString, fmt->location, "Invalid format string: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, fmt->location, XorStr("Invalid format string: %s"), error); } - else if (name == "pack" || name == "packsize" || name == "unpack") + else if (name == XorStr("pack") || name == XorStr("packsize") || name == XorStr("unpack")) { if (AstExprConstantString* fmt = self->as()) - if (const char* error = checkStringPack(fmt->value.data, fmt->value.size, name == "packsize")) - emitWarning(*context, LintWarning::Code_FormatString, fmt->location, "Invalid pack format: %s", error); + if (const char* error = checkStringPack(fmt->value.data, fmt->value.size, name == XorStr("packsize"))) + emitWarning(*context, LintWarning::Code_FormatString, fmt->location, XorStr("Invalid pack format: %s"), error); } - else if ((name == "match" || name == "gmatch") && args.size > 0) + else if ((name == XorStr("match") || name == XorStr("gmatch")) && args.size > 0) { if (AstExprConstantString* pat = args.data[0]->as()) if (const char* error = checkStringMatch(pat->value.data, pat->value.size)) - emitWarning(*context, LintWarning::Code_FormatString, pat->location, "Invalid match pattern: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, pat->location, XorStr("Invalid match pattern: %s"), error); } - else if (name == "find" && args.size > 0 && args.size <= 2) + else if (name == XorStr("find") && args.size > 0 && args.size <= 2) { if (AstExprConstantString* pat = args.data[0]->as()) if (const char* error = checkStringMatch(pat->value.data, pat->value.size)) - emitWarning(*context, LintWarning::Code_FormatString, pat->location, "Invalid match pattern: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, pat->location, XorStr("Invalid match pattern: %s"), error); } - else if (name == "find" && args.size >= 3) + else if (name == XorStr("find") && args.size >= 3) { AstExprConstantBool* mode = args.data[2]->as(); @@ -1753,19 +1753,19 @@ private: if (mode && !mode->value) if (AstExprConstantString* pat = args.data[0]->as()) if (const char* error = checkStringMatch(pat->value.data, pat->value.size)) - emitWarning(*context, LintWarning::Code_FormatString, pat->location, "Invalid match pattern: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, pat->location, XorStr("Invalid match pattern: %s"), error); } - else if (name == "gsub" && args.size > 1) + else if (name == XorStr("gsub") && args.size > 1) { int captures = -1; if (AstExprConstantString* pat = args.data[0]->as()) if (const char* error = checkStringMatch(pat->value.data, pat->value.size, &captures)) - emitWarning(*context, LintWarning::Code_FormatString, pat->location, "Invalid match pattern: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, pat->location, XorStr("Invalid match pattern: %s"), error); if (AstExprConstantString* rep = args.data[1]->as()) if (const char* error = checkStringReplace(rep->value.data, rep->value.size, captures)) - emitWarning(*context, LintWarning::Code_FormatString, rep->location, "Invalid match replacement: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, rep->location, XorStr("Invalid match replacement: %s"), error); } } @@ -1792,7 +1792,7 @@ private: if (!lib) return; - if (lib->name == "string") + if (lib->name == XorStr("string")) { if (node->args.size > 0) { @@ -1801,13 +1801,13 @@ private: matchStringCall(func->index, node->args.data[0], rest); } } - else if (lib->name == "os") + else if (lib->name == XorStr("os")) { - if (func->index == "date" && node->args.size > 0) + if (func->index == XorStr("date") && node->args.size > 0) { if (AstExprConstantString* fmt = node->args.data[0]->as()) if (const char* error = checkDateFormat(fmt->value.data, fmt->value.size)) - emitWarning(*context, LintWarning::Code_FormatString, fmt->location, "Invalid date format: %s", error); + emitWarning(*context, LintWarning::Code_FormatString, fmt->location, XorStr("Invalid date format: %s"), error); } } } @@ -1822,7 +1822,7 @@ private: class LintTableLiteral : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintTableLiteral pass; pass.context = &context; @@ -1920,7 +1920,7 @@ private: class LintUninitializedLocal : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintUninitializedLocal pass; pass.context = &context; @@ -2026,7 +2026,7 @@ private: class LintDuplicateFunction : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintDuplicateFunction pass{&context}; context.root->visit(&pass); @@ -2092,7 +2092,7 @@ private: void report(const std::string& name, Location location, Location otherLocation) { - emitWarning(*context, LintWarning::Code_DuplicateFunction, location, "Duplicate function definition: '%s' also defined on line %d", + emitWarning(*context, LintWarning::Code_DuplicateFunction, location, XorStr("Duplicate function definition: '%s' also defined on line %d"), name.c_str(), otherLocation.begin.line + 1); } }; @@ -2100,7 +2100,7 @@ private: class LintDeprecatedApi : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { if (!context.module) return; @@ -2143,19 +2143,19 @@ private: void report(const Location& location, const Property& prop, const char* container, const char* field) { - std::string suggestion = prop.deprecatedSuggestion.empty() ? "" : format(", use '%s' instead", prop.deprecatedSuggestion.c_str()); + std::string suggestion = prop.deprecatedSuggestion.empty() ? "" : format(XorStr(", use '%s' instead"), prop.deprecatedSuggestion.c_str()); if (container) - emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s.%s' is deprecated%s", container, field, suggestion.c_str()); + emitWarning(*context, LintWarning::Code_DeprecatedApi, location, XorStr("Member '%s.%s' is deprecated%s"), container, field, suggestion.c_str()); else - emitWarning(*context, LintWarning::Code_DeprecatedApi, location, "Member '%s' is deprecated%s", field, suggestion.c_str()); + emitWarning(*context, LintWarning::Code_DeprecatedApi, location, XorStr("Member '%s' is deprecated%s"), field, suggestion.c_str()); } }; class LintTableOperations : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { if (!context.module) return; @@ -2184,7 +2184,7 @@ private: AstExpr** args = node->args.data; - if (func->index == "insert" && node->args.size == 2) + if (func->index == XorStr("insert") && node->args.size == 2) { if (AstExprCall* tail = args[1]->as()) { @@ -2195,37 +2195,37 @@ private: if (ret > 1) emitWarning(*context, LintWarning::Code_TableOperations, tail->location, "table.insert may change behavior if the call returns more than one result; consider adding parentheses around second " - "argument"); + XorStr("argument")); } } } - if (func->index == "insert" && node->args.size >= 3) + if (func->index == XorStr("insert") && node->args.size >= 3) { // table.insert(t, 0, ?) if (isConstant(args[1], 0.0)) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, - "table.insert uses index 0 but arrays are 1-based; did you mean 1 instead?"); + XorStr("table.insert uses index 0 but arrays are 1-based; did you mean 1 instead?")); // table.insert(t, #t, ?) if (isLength(args[1], args[0])) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, "table.insert will insert the value before the last element, which is likely a bug; consider removing the second argument or " - "wrap it in parentheses to silence"); + XorStr("wrap it in parentheses to silence")); // table.insert(t, #t+1, ?) if (AstExprBinary* add = args[1]->as(); add && add->op == AstExprBinary::Add && isLength(add->left, args[0]) && isConstant(add->right, 1.0)) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, - "table.insert will append the value to the table; consider removing the second argument for efficiency"); + XorStr("table.insert will append the value to the table; consider removing the second argument for efficiency")); } - if (func->index == "remove" && node->args.size >= 2) + if (func->index == XorStr("remove") && node->args.size >= 2) { // table.remove(t, 0) if (isConstant(args[1], 0.0)) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, - "table.remove uses index 0 but arrays are 1-based; did you mean 1 instead?"); + XorStr("table.remove uses index 0 but arrays are 1-based; did you mean 1 instead?")); // note: it's tempting to check for table.remove(t, #t), which is equivalent to table.remove(t), but it's correct, occurs frequently, // and also reads better. @@ -2235,33 +2235,33 @@ private: sub && sub->op == AstExprBinary::Sub && isLength(sub->left, args[0]) && isConstant(sub->right, 1.0)) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, "table.remove will remove the value before the last element, which is likely a bug; consider removing the second argument or " - "wrap it in parentheses to silence"); + XorStr("wrap it in parentheses to silence")); } - if (func->index == "move" && node->args.size >= 4) + if (func->index == XorStr("move") && node->args.size >= 4) { // table.move(t, 0, _, _) if (isConstant(args[1], 0.0)) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, - "table.move uses index 0 but arrays are 1-based; did you mean 1 instead?"); + XorStr("table.move uses index 0 but arrays are 1-based; did you mean 1 instead?")); // table.move(t, _, _, 0) else if (isConstant(args[3], 0.0)) emitWarning(*context, LintWarning::Code_TableOperations, args[3]->location, - "table.move uses index 0 but arrays are 1-based; did you mean 1 instead?"); + XorStr("table.move uses index 0 but arrays are 1-based; did you mean 1 instead?")); } - if (func->index == "create" && node->args.size == 2) + if (func->index == XorStr("create") && node->args.size == 2) { // table.create(n, {...}) if (args[1]->is()) emitWarning(*context, LintWarning::Code_TableOperations, args[1]->location, - "table.create with a table literal will reuse the same object for all elements; consider using a for loop instead"); + XorStr("table.create with a table literal will reuse the same object for all elements; consider using a for loop instead")); // table.create(n, {...} :: ?) if (AstExprTypeAssertion* as = args[1]->as(); as && as->expr->is()) emitWarning(*context, LintWarning::Code_TableOperations, as->expr->location, - "table.create with a table literal will reuse the same object for all elements; consider using a for loop instead"); + XorStr("table.create with a table literal will reuse the same object for all elements; consider using a for loop instead")); } return true; @@ -2303,7 +2303,7 @@ private: class LintDuplicateCondition : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintDuplicateCondition pass{&context}; context.root->visit(&pass); @@ -2470,7 +2470,7 @@ private: class LintDuplicateLocal : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintDuplicateLocal pass; pass.context = &context; @@ -2504,10 +2504,10 @@ private: if (local->shadow && locals[local->shadow] == node && !ignoreDuplicate(local)) { if (local->shadow->location.begin.line == local->location.begin.line) - emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, "Variable '%s' already defined on column %d", + emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, XorStr("Variable '%s' already defined on column %d"), local->name.value, local->shadow->location.begin.column + 1); else - emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, "Variable '%s' already defined on line %d", + emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, XorStr("Variable '%s' already defined on line %d"), local->name.value, local->shadow->location.begin.line + 1); } } @@ -2530,12 +2530,12 @@ private: if (local->shadow && locals[local->shadow] == node && !ignoreDuplicate(local)) { if (local->shadow == node->self) - emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, "Function parameter 'self' already defined implicitly"); + emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, XorStr("Function parameter 'self' already defined implicitly")); else if (local->shadow->location.begin.line == local->location.begin.line) - emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, "Function parameter '%s' already defined on column %d", + emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, XorStr("Function parameter '%s' already defined on column %d"), local->name.value, local->shadow->location.begin.column + 1); else - emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, "Function parameter '%s' already defined on line %d", + emitWarning(*context, LintWarning::Code_DuplicateLocal, local->location, XorStr("Function parameter '%s' already defined on line %d"), local->name.value, local->shadow->location.begin.line + 1); } } @@ -2545,14 +2545,14 @@ private: bool ignoreDuplicate(AstLocal* local) { - return local->name == "_"; + return local->name == XorStr("_"); } }; class LintMisleadingAndOr : AstVisitor { public: - LUAU_NOINLINE static void process(LintContext& context) + lluz_NOINLINE static void process(LintContext& context) { LintMisleadingAndOr pass; pass.context = &context; @@ -2616,10 +2616,10 @@ static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names, static const char* fuzzyMatch(std::string_view str, const char** array, size_t size) { - if (FInt::LuauSuggestionDistance == 0) + if (FInt::LluSuggestionDistance == 0) return nullptr; - size_t bestDistance = FInt::LuauSuggestionDistance; + size_t bestDistance = FInt::LluSuggestionDistance; size_t bestMatch = size; for (size_t i = 0; i < size; ++i) @@ -2649,14 +2649,14 @@ static void lintComments(LintContext& context, const std::vector& ho if (!hc.header) { emitWarning(context, LintWarning::Code_CommentDirective, hc.location, - "Comment directive is ignored because it is placed after the first non-comment token"); + XorStr("Comment directive is ignored because it is placed after the first non-comment token")); } else { - size_t space = hc.content.find_first_of(" \t"); + size_t space = hc.content.find_first_of(XorStr(" \t")); std::string_view first = std::string_view(hc.content).substr(0, space); - if (first == "nolint") + if (first == XorStr("nolint")) { size_t notspace = hc.content.find_first_not_of(" \t", space); @@ -2674,35 +2674,20 @@ static void lintComments(LintContext& context, const std::vector& ho "nolint directive refers to unknown lint rule '%s'; did you mean '%s'?", rule, suggestion); else emitWarning( - context, LintWarning::Code_CommentDirective, hc.location, "nolint directive refers to unknown lint rule '%s'", rule); + context, LintWarning::Code_CommentDirective, hc.location, XorStr("nolint directive refers to unknown lint rule '%s'"), rule); } } - else if (first == "nocheck" || first == "nonstrict" || first == "strict") + else if (first == XorStr("nocheck") || first == XorStr("nonstrict") || first == XorStr("strict")) { if (space != std::string::npos) emitWarning(context, LintWarning::Code_CommentDirective, hc.location, - "Comment directive with the type checking mode has extra symbols at the end of the line"); + XorStr("Comment directive with the type checking mode has extra symbols at the end of the line")); else if (seenMode) emitWarning(context, LintWarning::Code_CommentDirective, hc.location, - "Comment directive with the type checking mode has already been used"); + XorStr("Comment directive with the type checking mode has already been used")); else seenMode = true; } - else if (first == "optimize") - { - size_t notspace = hc.content.find_first_not_of(" \t", space); - - if (space == std::string::npos || notspace == std::string::npos) - emitWarning(context, LintWarning::Code_CommentDirective, hc.location, "optimize directive requires an optimization level"); - else - { - const char* level = hc.content.c_str() + notspace; - - if (strcmp(level, "0") && strcmp(level, "1") && strcmp(level, "2")) - emitWarning(context, LintWarning::Code_CommentDirective, hc.location, - "optimize directive uses unknown optimization level '%s', 0..2 expected", level); - } - } else { static const char* kHotComments[] = { @@ -2710,14 +2695,13 @@ static void lintComments(LintContext& context, const std::vector& ho "nocheck", "nonstrict", "strict", - "optimize", }; if (const char* suggestion = fuzzyMatch(first, kHotComments, std::size(kHotComments))) - emitWarning(context, LintWarning::Code_CommentDirective, hc.location, "Unknown comment directive '%.*s'; did you mean '%s'?", + emitWarning(context, LintWarning::Code_CommentDirective, hc.location, XorStr("Unknown comment directive '%.*s'; did you mean '%s'?"), int(first.size()), first.data(), suggestion); else - emitWarning(context, LintWarning::Code_CommentDirective, hc.location, "Unknown comment directive '%.*s'", int(first.size()), + emitWarning(context, LintWarning::Code_CommentDirective, hc.location, XorStr("Unknown comment directive '%.*s'"), int(first.size()), first.data()); } } @@ -2737,7 +2721,7 @@ std::vector lint(AstStat* root, const AstNameTable& names, const Sc context.options = options; context.root = root; - context.placeholder = names.get("_"); + context.placeholder = names.get(XorStr("_")); context.scope = env; context.module = module; @@ -2817,7 +2801,7 @@ std::vector lint(AstStat* root, const AstNameTable& names, const Sc const char* LintWarning::getName(Code code) { - LUAU_ASSERT(unsigned(code) < Code__Count); + lluz_ASSERT(unsigned(code) < Code__Count); return kWarningNames[code]; } @@ -2882,4 +2866,4 @@ void fuzzFormatString(const char* data, size_t size) LintFormatString::fuzz(data, size); } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Module.cpp b/Analysis/src/Module.cpp index dca18027..7be86a08 100644 --- a/Analysis/src/Module.cpp +++ b/Analysis/src/Module.cpp @@ -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 -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 varargPack = FFlag::DebugLuauDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack; + TypePackId returnType = FFlag::DebugLluDeferredConstraintResolution ? moduleScope2->returnType : moduleScope->returnType; + std::optional varargPack = FFlag::DebugLluDeferredConstraintResolution ? std::nullopt : moduleScope->varargPack; std::unordered_map* 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 diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index a96b557d..3a6606c1 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -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 -#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& 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& 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(&ctvRef); @@ -222,7 +208,7 @@ struct Normalize final : TypeVarVisitor std::vector 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(utvRef); - - // TODO: Clip tempOptions and optionsRef when clipping FFlag::LuauFixNormalizationOfCyclicUnions - std::vector tempOptions; - if (!FFlag::LuauFixNormalizationOfCyclicUnions) - tempOptions = std::move(utv->options); - - std::vector& optionsRef = FFlag::LuauFixNormalizationOfCyclicUnions ? utv->options : tempOptions; + std::vector 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 newOptions = normalizeUnion(optionsRef); + std::vector 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(itvRef); - if (FFlag::LuauFixNormalizationOfCyclicUnions) + std::vector oldParts = std::move(itv->parts); + + for (TypeId part : oldParts) + traverse(part); + + std::vector tables; + for (TypeId part : oldParts) { - std::vector oldParts = itv->parts; - IntersectionTypeVar newIntersection; - - for (TypeId part : oldParts) - traverse(part); - - std::vector tables; - for (TypeId part : oldParts) + part = follow(part); + if (get(part)) + tables.push_back(part); + else { - part = follow(part); - if (get(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(tables[0]); - LUAU_ASSERT(first); - - TypeId newTable = arena.addType(TableTypeVar{first->state, first->level}); - TableTypeVar* ttv = getMutable(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 oldParts = std::move(itv->parts); + const TableTypeVar* first = get(tables[0]); + lluz_ASSERT(first); - for (TypeId part : oldParts) - traverse(part); - - std::vector tables; - for (TypeId part : oldParts) + TypeId newTable = arena.addType(TableTypeVar{first->state, first->level}); + TableTypeVar* ttv = getMutable(newTable); + for (TypeId part : tables) { - part = follow(part); - if (get(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(tables[0]); - LUAU_ASSERT(first); + itv->parts.push_back(newTable); + } - TypeId newTable = arena.addType(TableTypeVar{first->state, first->level}); - TableTypeVar* ttv = getMutable(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 result; for (TypeId part : options) - { - // AnyTypeVar always win the battle no matter what we do, so we're done. - if (FFlag::LuauUnknownAndNeverType && get(follow(part))) - return {part}; - combineIntoUnion(result, part); - } return result; } @@ -508,17 +427,7 @@ struct Normalize final : TypeVarVisitor if (auto utv = get(ty)) { for (TypeId t : utv) - { - // AnyTypeVar always win the battle no matter what we do, so we're done. - if (FFlag::LuauUnknownAndNeverType && get(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(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(a) && !get(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(a)) { - if (FFlag::LuauNormalizeCombineTableFix && !get(b)) + if (FFlag::LluNormalizeCombineTableFix && !get(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 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 normalize(TypeId ty, const ModulePtr& module, InternalEr std::pair 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 normalize(TypePackId tp, const ModulePtr& module, In return normalize(tp, module->internalTypes, ice); } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Quantify.cpp b/Analysis/src/Quantify.cpp index 72eb4012..9d39165b 100644 --- a/Analysis/src/Quantify.cpp +++ b/Analysis/src/Quantify.cpp @@ -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 generics; std::vector 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(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 opts = std::move(ctv->parts); @@ -109,7 +109,7 @@ struct Quantifier final : TypeVarOnceVisitor bool visit(TypeId ty, const TableTypeVar&) override { - LUAU_ASSERT(getMutable(ty)); + lluz_ASSERT(getMutable(ty)); TableTypeVar& ttv = *getMutable(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(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(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 insertedGenerics; std::vector 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(ty)) { @@ -288,7 +288,7 @@ struct PureQuantifier : Substitution { TypeId result = arena->addType(TableTypeVar{}); TableTypeVar* resultTable = getMutable(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 result = quantifier.substitute(ty); - LUAU_ASSERT(result); + lluz_ASSERT(result); FunctionTypeVar* ftv = getMutable(*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 diff --git a/Analysis/src/RequireTracer.cpp b/Analysis/src/RequireTracer.cpp index c036a7a5..577ad881 100644 --- a/Analysis/src/RequireTracer.cpp +++ b/Analysis/src/RequireTracer.cpp @@ -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(); - 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 diff --git a/Analysis/src/Scope.cpp b/Analysis/src/Scope.cpp index 083aa085..dd018d66 100644 --- a/Analysis/src/Scope.cpp +++ b/Analysis/src/Scope.cpp @@ -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 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 Scope::lookupType(const Name& name) { const Scope* scope = this; @@ -105,51 +121,51 @@ std::optional Scope::linearSearchForBinding(const std::string& name, bo return std::nullopt; } -std::optional Scope::lookup(Symbol sym) +std::optional 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 Scope::lookupTypeBinding(const Name& name) +std::optional 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 Scope::lookupTypePackBinding(const Name& name) +std::optional 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 diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index 7245403c..b379b3a7 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -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 #include -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(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(ty); FFlag::LuauLowerBoundsCalculation && btv) + if (BoundTypeVar* btv = log->getMutable(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(ty)) { ftv->argTypes = replace(ftv->argTypes); @@ -451,7 +446,7 @@ void Substitution::replaceChildren(TypeId ty) } else if (TableTypeVar* ttv = getMutable(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(tp)) { for (TypeId& tv : tpp->head) @@ -511,4 +503,4 @@ void Substitution::replaceChildren(TypePackId tp) } } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Symbol.cpp b/Analysis/src/Symbol.cpp index 5922bb50..36e764f4 100644 --- a/Analysis/src/Symbol.cpp +++ b/Analysis/src/Symbol.cpp @@ -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 diff --git a/Analysis/src/ToDot.cpp b/Analysis/src/ToDot.cpp index 6b677bb8..3c5d297f 100644 --- a/Analysis/src/ToDot.cpp +++ b/Analysis/src/ToDot.cpp @@ -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 #include -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(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(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(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(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(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 diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index f1dc9211..a70833bb 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -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 #include -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& cycles, std::set& cycleTPs, T } // namespace -static std::pair> canUseTypeNameInScope(ScopePtr scope, const std::string& name) +static std::pair> 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(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(&stv)) - state.emit(bs->value ? "true" : "false"); - else if (const StringSingleton* ss = Luau::get(&stv)) + if (const BooleanSingleton* bs = lluz::get(&stv)) + state.emit(bs->value ? XorStr("true" : "false")); + else if (const StringSingleton* ss = lluz::get(&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(""); + state.emit(XorStr("")); 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(""); + state.emit(XorStr("")); 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(""); + state.emit(XorStr("")); return; } @@ -726,12 +751,12 @@ struct TypeVarStringifier bool needParens = !state.cycleNames.count(el) && (get(el) || get(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(""); + state.emit(XorStr("")); return; } @@ -788,12 +810,12 @@ struct TypeVarStringifier bool needParens = !state.cycleNames.count(el) && (get(el) || get(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 ? "" : "*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(""); + state.emit(XorStr("")); 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(tail); !vtp || (!FFlag::DebugLuauVerboseTypeNames && !vtp->hidden)) + if (auto vtp = get(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 ? "" : "*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(""); - } + state.emit(XorStr("...")); + if (FFlag::DebugLluVerboseTypeNames && pack.hidden) + state.emit(XorStr("")); 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 += "... "; + result.name += "... "; } 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 += "... "; - } + result.name += "... "; 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(*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(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, "Non-exhaustive constraint switch"); + static_assert(always_false_v, 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 diff --git a/Analysis/src/TopoSortStatements.cpp b/Analysis/src/TopoSortStatements.cpp index 1ea2e27d..1c266538 100644 --- a/Analysis/src/TopoSortStatements.cpp +++ b/Analysis/src/TopoSortStatements.cpp @@ -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 #include @@ -40,7 +40,7 @@ #include #include -namespace Luau +namespace lluz { // For some reason, natvis interacts really poorly with anonymous data types @@ -127,7 +127,7 @@ std::optional 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 mkName(const AstExpr& expr) @@ -147,9 +147,9 @@ std::optional 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& 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& 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& 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& stats) std::swap(stats, result); } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/Transpiler.cpp b/Analysis/src/Transpiler.cpp index 9feff1c0..b69b71e9 100644 --- a/Analysis/src/Transpiler.cpp +++ b/Analysis/src/Transpiler.cpp @@ -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 #include @@ -32,7 +32,7 @@ const std::vector 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()) { if (!forVarArg) - writer.symbol("..."); + writer.symbol(XorStr("...")); visualizeTypeAnnotation(*variadicTp->variadicType); } else if (const AstTypePackGeneric* genericTp = annotation.as()) { writer.symbol(genericTp->genericName.value); - writer.symbol("..."); + writer.symbol(XorStr("...")); } else if (const AstTypePackExplicit* explicitTp = annotation.as()) { - 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()) { - writer.symbol("("); + writer.symbol(XorStr("(")); visualize(*a->expr); - writer.symbol(")"); + writer.symbol(XorStr(")")); } else if (expr.is()) { - writer.keyword("nil"); + writer.keyword(XorStr("nil")); } else if (const auto& a = expr.as()) { 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()) { 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()) { - writer.symbol("..."); + writer.symbol(XorStr("...")); } else if (const auto& a = expr.as()) { 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()) { @@ -377,18 +377,18 @@ struct Printer else if (const auto& a = expr.as()) { visualize(*a->expr); - writer.symbol("["); + writer.symbol(XorStr("[")); visualize(*a->index); - writer.symbol("]"); + writer.symbol(XorStr("]")); } else if (const auto& a = expr.as()) { - writer.keyword("function"); + writer.keyword(XorStr("function")); visualizeFunctionBody(*a); } else if (const auto& a = expr.as()) { - 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()) @@ -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()) { - 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()) { - 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()) { - 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()) { - writer.keyword("if"); + writer.keyword(XorStr("if")); visualizeElseIf(*a); } else if (const auto& a = program.as()) { - 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()) { - 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()) - writer.keyword("break"); + writer.keyword(XorStr("break")); else if (program.is()) - writer.keyword("continue"); + writer.keyword(XorStr("continue")); else if (const auto& a = program.as()) { - 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()) { - 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()) { - 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()) { - 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()) { - writer.keyword("function"); + writer.keyword(XorStr("function")); visualize(*a->name); visualizeFunctionBody(*a->func); } else if (const auto& a = program.as()) { - 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()) { - 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()) 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()) { - 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()) @@ -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()) { AstTypeReference* indexType = a->indexer ? a->indexer->indexType->as() : 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()) { - 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()) { @@ -1063,24 +1063,24 @@ struct Printer AstType* r = a->types.data[1]; auto lta = l->as(); - 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(); - if (rta && rta->name == "nil") + if (rta && rta->name == XorStr("nil")) { bool wrap = l->as() || l->as(); 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() || a->types.data[i]->as(); 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()) @@ -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() || a->types.data[i]->as(); 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()) { - writer.keyword(a->value ? "true" : "false"); + writer.keyword(a->value ? XorStr("true" : "false")); } else if (const auto& a = typeAnnotation.as()) { @@ -1135,11 +1135,11 @@ struct Printer } else if (typeAnnotation.is()) { - 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 diff --git a/Analysis/src/TxnLog.cpp b/Analysis/src/TxnLog.cpp index b3f60d30..5794ae8e 100644 --- a/Analysis/src/TxnLog.cpp +++ b/Analysis/src/TxnLog.cpp @@ -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 #include -LUAU_FASTFLAG(LuauUnknownAndNeverType) +lluz_FASTFLAG(LluNonCopyableTypeVarFields) -namespace Luau +namespace lluz { const std::string nullPendingResult = ""; @@ -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 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(*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(*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(this) != nullptr); + lluz_ASSERT(static_cast(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(this) != nullptr); + lluz_ASSERT(static_cast(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 newBoundTo) { - LUAU_ASSERT(get(ty)); + lluz_ASSERT(get(ty)); PendingType* newTy = queue(ty); - if (TableTypeVar* ttv = Luau::getMutable(newTy)) + if (TableTypeVar* ttv = lluz::getMutable(newTy)) ttv->boundTo = newBoundTo; return newTy; @@ -251,37 +289,32 @@ PendingType* TxnLog::bindTable(TypeId ty, std::optional newBoundTo) PendingType* TxnLog::changeLevel(TypeId ty, TypeLevel newLevel) { - LUAU_ASSERT(get(ty) || get(ty) || get(ty) || get(ty)); + lluz_ASSERT(get(ty) || get(ty) || get(ty)); PendingType* newTy = queue(ty); - if (FreeTypeVar* ftv = Luau::getMutable(newTy)) + if (FreeTypeVar* ftv = lluz::getMutable(newTy)) { ftv->level = newLevel; } - else if (TableTypeVar* ttv = Luau::getMutable(newTy)) + else if (TableTypeVar* ttv = lluz::getMutable(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(newTy)) + else if (FunctionTypeVar* ftv = lluz::getMutable(newTy)) { ftv->level = newLevel; } - else if (ConstrainedTypeVar* ctv = Luau::getMutable(newTy)) - { - if (FFlag::LuauUnknownAndNeverType) - ctv->level = newLevel; - } return newTy; } PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel) { - LUAU_ASSERT(get(tp)); + lluz_ASSERT(get(tp)); PendingTypePack* newTp = queue(tp); - if (FreeTypePack* ftp = Luau::getMutable(newTp)) + if (FreeTypePack* ftp = lluz::getMutable(newTp)) { ftp->level = newLevel; } @@ -291,10 +324,10 @@ PendingTypePack* TxnLog::changeLevel(TypePackId tp, TypeLevel newLevel) PendingType* TxnLog::changeIndexer(TypeId ty, std::optional indexer) { - LUAU_ASSERT(get(ty)); + lluz_ASSERT(get(ty)); PendingType* newTy = queue(ty); - if (TableTypeVar* ttv = Luau::getMutable(newTy)) + if (TableTypeVar* ttv = lluz::getMutable(newTy)) { ttv->indexer = indexer; } @@ -316,7 +349,7 @@ std::optional 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 diff --git a/Analysis/src/TypeArena.cpp b/Analysis/src/TypeArena.cpp index 0c89d130..386c9b7f 100644 --- a/Analysis/src/TypeArena.cpp +++ b/Analysis/src/TypeArena.cpp @@ -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 diff --git a/Analysis/src/TypeAttach.cpp b/Analysis/src/TypeAttach.cpp index 2bc89cf6..005004ec 100644 --- a/Analysis/src/TypeAttach.cpp +++ b/Analysis/src/TypeAttach.cpp @@ -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 -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 -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; -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(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(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(); - 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(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& 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(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(Location(), unionTypes); } @@ -327,7 +327,7 @@ public: intersectionTypes.data = static_cast(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(Location(), intersectionTypes); } @@ -335,14 +335,6 @@ public: { return allocator->alloc(Location(), std::nullopt, AstName("")); } - AstType* operator()(const UnknownTypeVar& ttv) - { - return allocator->alloc(Location(), std::nullopt, AstName{"unknown"}); - } - AstType* operator()(const NeverTypeVar& ttv) - { - return allocator->alloc(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(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(Location(), AstTypeList{head, tail}); } @@ -390,7 +382,7 @@ public: if (vtp.hidden) return nullptr; - return allocator->alloc(Location(), Luau::visit(*typeVisitor, vtp.ty->ty)); + return allocator->alloc(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 typeAstPack(TypePackId type) + AstArray typeAstPack(TypePackId type) { const auto& [v, tail] = flatten(type); @@ -463,7 +455,7 @@ public: result.data = static_cast(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 diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 85749671..74d741d1 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -1,19 +1,19 @@ -#include "Luau/TypeChecker2.h" +#include "lluz/TypeChecker2.h" #include -#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 exprs, TypeArena& arena) { - if (exprs.size == 0) - return arena.addTypePack(TypePack{{}, std::nullopt}); - std::vector 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(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(pack)) { - auto tp = get(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(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 diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 371ef77a..433a2bf3 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -1,62 +1,57 @@ -// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details -#include "Luau/TypeInfer.h" +// This file is part of the lluz programming language and is licensed under MIT License; see LICENSE.txt for details +#include "lluz/TypeInfer.h" -#include "Luau/Clone.h" -#include "Luau/Common.h" -#include "Luau/Instantiation.h" -#include "Luau/ModuleResolver.h" -#include "Luau/Normalize.h" -#include "Luau/Parser.h" -#include "Luau/Quantify.h" -#include "Luau/RecursionCounter.h" -#include "Luau/Scope.h" -#include "Luau/Substitution.h" -#include "Luau/TimeTrace.h" -#include "Luau/TopoSortStatements.h" -#include "Luau/ToString.h" -#include "Luau/ToString.h" -#include "Luau/TypePack.h" -#include "Luau/TypeUtils.h" -#include "Luau/TypeVar.h" -#include "Luau/VisitTypeVar.h" +#include "lluz/Clone.h" +#include "lluz/Common.h" +#include "lluz/Instantiation.h" +#include "lluz/ModuleResolver.h" +#include "lluz/Normalize.h" +#include "lluz/Parser.h" +#include "lluz/Quantify.h" +#include "lluz/RecursionCounter.h" +#include "lluz/Scope.h" +#include "lluz/Substitution.h" +#include "lluz/TimeTrace.h" +#include "lluz/TopoSortStatements.h" +#include "lluz/ToString.h" +#include "lluz/ToString.h" +#include "lluz/TypePack.h" +#include "lluz/TypeUtils.h" +#include "lluz/TypeVar.h" +#include "lluz/VisitTypeVar.h" #include #include -LUAU_FASTFLAGVARIABLE(DebugLuauMagicTypes, false) -LUAU_FASTINTVARIABLE(LuauTypeInferRecursionLimit, 165) -LUAU_FASTINTVARIABLE(LuauTypeInferIterationLimit, 20000) -LUAU_FASTINTVARIABLE(LuauTypeInferTypePackLoopLimit, 5000) -LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300) -LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500) -LUAU_FASTFLAG(LuauKnowsTheDataModel3) -LUAU_FASTFLAG(LuauAutocompleteDynamicLimits) -LUAU_FASTFLAGVARIABLE(LuauExpectedTableUnionIndexerType, false) -LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false) -LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false) -LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) -LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false) -LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false) -LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false. -LUAU_FASTFLAG(LuauNormalizeFlagIsConservative) -LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false); -LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false); -LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false) -LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false) -LUAU_FASTFLAG(LuauQuantifyConstrained) -LUAU_FASTFLAGVARIABLE(LuauFalsyPredicateReturnsNilInstead, false) -LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false) -LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false) -LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false) -LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false) -LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false) +lluz_FASTFLAGVARIABLE(DebugLluMagicTypes, false) +lluz_FASTINTVARIABLE(LluTypeInferRecursionLimit, 165) +lluz_FASTINTVARIABLE(LluTypeInferIterationLimit, 20000) +lluz_FASTINTVARIABLE(LluTypeInferTypePackLoopLimit, 5000) +lluz_FASTINTVARIABLE(LluCheckRecursionLimit, 300) +lluz_FASTINTVARIABLE(LluVisitRecursionLimit, 500) +lluz_FASTFLAG(LluKnowsTheDataModel3) +lluz_FASTFLAG(LlluztocompleteDynamicLimits) +lluz_FASTFLAGVARIABLE(LluLowerBoundsCalculation, false) +lluz_FASTFLAGVARIABLE(DebugLluFreezeDuringUnification, false) +lluz_FASTFLAGVARIABLE(LluSelfCallAutocompleteFix2, false) +lluz_FASTFLAGVARIABLE(LluReduceUnionRecursion, false) +lluz_FASTFLAGVARIABLE(LluReturnAnyInsteadOfICE, false) // Eventually removed as false. +lluz_FASTFLAG(LluNormalizeFlagIsConservative) +lluz_FASTFLAGVARIABLE(LluReturnTypeInferenceInNonstrict, false) +lluz_FASTFLAGVARIABLE(DebugLluSharedSelf, false); +lluz_FASTFLAGVARIABLE(LluAlwaysQuantify, false); +lluz_FASTFLAGVARIABLE(LluReportErrorsOnIndexerKeyMismatch, false) +lluz_FASTFLAG(LluQuantifyConstrained) +lluz_FASTFLAGVARIABLE(LluFalsyPredicateReturnsNilInstead, false) +lluz_FASTFLAGVARIABLE(LluNonCopyableTypeVarFields, false) +lluz_FASTFLAGVARIABLE(LluCheckLenMT, false) -namespace Luau +namespace lluz { const char* TimeLimitError::what() const throw() { - return "Typeinfer failed to complete in allotted time"; + return XorStr("Typeinfer failed to complete in allotted time"); } static bool typeCouldHaveMetatable(TypeId ty) @@ -64,23 +59,23 @@ static bool typeCouldHaveMetatable(TypeId ty) return get(follow(ty)) || get(follow(ty)) || get(follow(ty)); } -static void defaultLuauPrintLine(const std::string& s) +static void defaultlluzPrintLine(const std::string& s) { printf("%s\n", s.c_str()); } -using PrintLineProc = decltype(&defaultLuauPrintLine); +using PrintLineProc = decltype(&defaultlluzPrintLine); -static PrintLineProc luauPrintLine = &defaultLuauPrintLine; +static PrintLineProc lluzPrintLine = &defaultlluzPrintLine; void setPrintLine(PrintLineProc pl) { - luauPrintLine = pl; + lluzPrintLine = pl; } void resetPrintLine() { - luauPrintLine = &defaultLuauPrintLine; + lluzPrintLine = &defaultlluzPrintLine; } bool doesCallError(const AstExprCall* call) @@ -89,9 +84,9 @@ bool doesCallError(const AstExprCall* call) if (!global) return false; - if (global->name == "error") + if (global->name == XorStr("error")) return true; - else if (global->name == "assert") + else if (global->name == XorStr("assert")) { // assert() will error because it is missing the first argument if (call->args.size == 0) @@ -243,9 +238,9 @@ const AstStat* getFallthrough(const AstStat* node) static bool isMetamethod(const Name& name) { - return name == "__index" || name == "__newindex" || name == "__call" || name == "__concat" || name == "__unm" || name == "__add" || - name == "__sub" || name == "__mul" || name == "__div" || name == "__mod" || name == "__pow" || name == "__tostring" || - name == "__metatable" || name == "__eq" || name == "__lt" || name == "__le" || name == "__mode" || name == "__iter" || name == "__len"; + return name == XorStr("__index") || name == XorStr("__newindex") || name == XorStr("__call") || name == XorStr("__concat") || name == XorStr("__unm") || name == XorStr("__add") || + name == XorStr("__sub") || name == XorStr("__mul") || name == XorStr("__div") || name == XorStr("__mod") || name == XorStr("__pow") || name == XorStr("__tostring") || + name == XorStr("__metatable") || name == XorStr("__eq") || name == XorStr("__lt") || name == XorStr("__le") || name == XorStr("__mode") || name == XorStr("__iter") || name == XorStr("__len"); } size_t HashBoolNamePair::operator()(const std::pair& pair) const @@ -263,11 +258,7 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan , booleanType(getSingletonTypes().booleanType) , threadType(getSingletonTypes().threadType) , anyType(getSingletonTypes().anyType) - , unknownType(getSingletonTypes().unknownType) - , neverType(getSingletonTypes().neverType) , anyTypePack(getSingletonTypes().anyTypePack) - , neverTypePack(getSingletonTypes().neverTypePack) - , uninhabitableTypePack(getSingletonTypes().uninhabitableTypePack) , duplicateTypeAliases{{false, {}}} { globalScope = std::make_shared(globalTypes.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}})); @@ -278,11 +269,6 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, InternalErrorReporter* iceHan globalScope->exportedTypeBindings["string"] = TypeFun{{}, stringType}; globalScope->exportedTypeBindings["boolean"] = TypeFun{{}, booleanType}; globalScope->exportedTypeBindings["thread"] = TypeFun{{}, threadType}; - if (FFlag::LuauUnknownAndNeverType) - { - globalScope->exportedTypeBindings["unknown"] = TypeFun{{}, unknownType}; - globalScope->exportedTypeBindings["never"] = TypeFun{{}, neverType}; - } } ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optional environmentScope) @@ -300,8 +286,8 @@ ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optiona ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mode mode, std::optional environmentScope) { - LUAU_TIMETRACE_SCOPE("TypeChecker::check", "TypeChecker"); - LUAU_TIMETRACE_ARGUMENT("module", module.name.c_str()); + lluz_TIMETRACE_SCOPE(XorStr("TypeChecker::check", "TypeChecker")); + lluz_TIMETRACE_ARGUMENT("module", module.name.c_str()); currentModule.reset(new Module()); currentModule->type = module.type; @@ -310,10 +296,10 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo iceHandler->moduleName = module.name; - if (FFlag::LuauAutocompleteDynamicLimits) + if (FFlag::LlluztocompleteDynamicLimits) { - unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; - unifierState.counters.iterationLimit = unifierIterationLimit ? *unifierIterationLimit : FInt::LuauTypeInferIterationLimit; + unifierState.counters.recursionLimit = FInt::LluTypeInferRecursionLimit; + unifierState.counters.iterationLimit = unifierIterationLimit ? *unifierIterationLimit : FInt::LluTypeInferIterationLimit; } ScopePtr parentScope = environmentScope.value_or(globalScope); @@ -343,10 +329,10 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo currentModule->timeout = true; } - if (FFlag::DebugLuauSharedSelf) + if (FFlag::DebugLluSharedSelf) { for (auto& [ty, scope] : deferredQuantification) - Luau::quantify(ty, scope->level); + lluz::quantify(ty, scope->level); deferredQuantification.clear(); } @@ -438,7 +424,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStat& program) currentModule->errors.resize(oldSize); } else - ice("Unknown AstStat"); + ice(XorStr("Unknown AstStat")); if (finishTime && TimeTrace::getClock() > *finishTime) throw TimeLimitError(); @@ -454,7 +440,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatBlock& block) void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block) { RecursionCounter _rc(&checkRecursionCount); - if (FInt::LuauCheckRecursionLimit > 0 && checkRecursionCount >= FInt::LuauCheckRecursionLimit) + if (FInt::LluCheckRecursionLimit > 0 && checkRecursionCount >= FInt::LluCheckRecursionLimit) { reportErrorCodeTooComplex(block.location); return; @@ -470,59 +456,6 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block) } } -struct InplaceDemoter : TypeVarOnceVisitor -{ - TypeLevel newLevel; - TypeArena* arena; - - InplaceDemoter(TypeLevel level, TypeArena* arena) - : newLevel(level) - , arena(arena) - { - } - - bool demote(TypeId ty) - { - if (auto level = getMutableLevel(ty)) - { - if (level->subsumesStrict(newLevel)) - { - *level = newLevel; - return true; - } - } - - return false; - } - - bool visit(TypeId ty, const BoundTypeVar& btyRef) override - { - return true; - } - - bool visit(TypeId ty) override - { - if (ty->owningArena != arena) - return false; - return demote(ty); - } - - bool visit(TypePackId tp, const FreeTypePack& ftpRef) override - { - if (tp->owningArena != arena) - return false; - - FreeTypePack* ftp = &const_cast(ftpRef); - if (ftp->level.subsumesStrict(newLevel)) - { - ftp->level = newLevel; - return true; - } - - return false; - } -}; - void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const AstStatBlock& block) { int subLevel = 0; @@ -547,7 +480,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A auto isLocalLambda = [](AstStat* stat) -> AstStatLocal* { AstStatLocal* local = stat->as(); - if (FFlag::LuauLowerBoundsCalculation && local && local->vars.size == 1 && local->values.size == 1 && + if (FFlag::LluLowerBoundsCalculation && local && local->vars.size == 1 && local->values.size == 1 && local->values.data[0]->is()) return local; else @@ -557,13 +490,13 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A auto checkBody = [&](AstStat* stat) { if (auto fun = stat->as()) { - LUAU_ASSERT(functionDecls.count(stat)); + lluz_ASSERT(functionDecls.count(stat)); auto [funTy, funScope] = functionDecls[stat]; check(scope, funTy, funScope, *fun); } else if (auto fun = stat->as()) { - LUAU_ASSERT(functionDecls.count(stat)); + lluz_ASSERT(functionDecls.count(stat)); auto [funTy, funScope] = functionDecls[stat]; check(scope, funTy, funScope, *fun); } @@ -601,7 +534,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A // function f(x:a):a local x: number = g(37) return x end // function g(x:number):number return f(x) end // ``` - if (containsFunctionCallOrReturn(**protoIter) || (FFlag::LuauLowerBoundsCalculation && isLocalLambda(*protoIter))) + if (containsFunctionCallOrReturn(**protoIter) || (FFlag::LluLowerBoundsCalculation && isLocalLambda(*protoIter))) { while (checkIter != protoIter) { @@ -618,7 +551,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A std::optional selfType; std::optional expectedType; - if (FFlag::DebugLuauSharedSelf) + if (FFlag::DebugLluSharedSelf) { if (auto name = fun->name->as()) { @@ -626,7 +559,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A tablify(baseTy); if (!fun->func->self) - expectedType = getIndexTypeFromType(scope, baseTy, name->index.value, name->indexLocation, /* addErrors= */ false); + expectedType = getIndexTypeFromType(scope, baseTy, name->index.value, name->indexLocation, false); else if (auto ttv = getMutableTableType(baseTy)) { if (!baseTy->persistent && ttv->state != TableState::Sealed && !ttv->selfTy) @@ -646,7 +579,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A if (auto name = fun->name->as()) { TypeId exprTy = checkExpr(scope, *name->expr).type; - expectedType = getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, /* addErrors= */ false); + expectedType = getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, false); } } } @@ -686,7 +619,7 @@ void TypeChecker::checkBlockWithoutRecursionCheck(const ScopePtr& scope, const A checkBlockTypeAliases(scope, sorted); } -LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std::vector& sorted) +lluz_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std::vector& sorted) { for (const auto& stat : sorted) { @@ -701,8 +634,15 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std TypeId type = bindings[name].type; if (get(follow(type))) { - TypeVar* mty = asMutable(follow(type)); - mty->reassign(*errorRecoveryType(anyType)); + if (FFlag::LluNonCopyableTypeVarFields) + { + TypeVar* mty = asMutable(follow(type)); + mty->reassign(*errorRecoveryType(anyType)); + } + else + { + *asMutable(type) = *errorRecoveryType(anyType); + } reportError(TypeError{typealias->location, OccursCheckFailed{}}); } @@ -741,7 +681,7 @@ static std::optional tryGetTypeGuardPredicate(const AstExprBinary& ex // If ssval is not a valid constant string, we'll find out later when resolving predicate. Name ssval(str->value.data, str->value.size); - bool isTypeof = callee->name == "typeof"; + bool isTypeof = callee->name == XorStr("typeof"); std::optional lvalue = tryGetLValue(*call->args.data[0]); if (!lvalue) @@ -835,14 +775,14 @@ struct Demoter : Substitution TypeId clean(TypeId ty) override { auto ftv = get(ty); - LUAU_ASSERT(ftv); + lluz_ASSERT(ftv); return addType(FreeTypeVar{demotedLevel(ftv->level)}); } TypePackId clean(TypePackId tp) override { auto ftp = get(tp); - LUAU_ASSERT(ftp); + lluz_ASSERT(ftp); return addTypePack(TypePackVar{FreeTypePack{demotedLevel(ftp->level)}}); } @@ -853,7 +793,7 @@ struct Demoter : Substitution void demote(std::vector>& expectedTypes) { - if (!FFlag::LuauQuantifyConstrained) + if (!FFlag::LluQuantifyConstrained) return; for (std::optional& ty : expectedTypes) { @@ -890,7 +830,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_) TypePackId retPack = checkExprList(scope, return_.location, return_.list, false, {}, expectedTypes).type; - if (useConstrainedIntersections()) + if (FFlag::LluReturnTypeInferenceInNonstrict ? FFlag::LluLowerBoundsCalculation : useConstrainedIntersections()) { unifyLowerBound(retPack, scope->returnType, demoter.demotedLevel(scope->level), return_.location); return; @@ -916,12 +856,12 @@ ErrorVec TypeChecker::tryUnify_(Id subTy, Id superTy, const Location& location) { Unifier state = mkUnifier(location); - if (FFlag::DebugLuauFreezeDuringUnification) + if (FFlag::DebugLluFreezeDuringUnification) freeze(currentModule->internalTypes); state.tryUnify(subTy, superTy); - if (FFlag::DebugLuauFreezeDuringUnification) + if (FFlag::DebugLluFreezeDuringUnification) unfreeze(currentModule->internalTypes); if (state.errors.empty()) @@ -1127,7 +1067,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local) } else if (const AstExprCall* call = rhs->as()) { - if (const AstExprGlobal* global = call->func->as(); global && global->name == "setmetatable") + if (const AstExprGlobal* global = call->func->as(); global && global->name == XorStr("setmetatable")) { MetatableTypeVar* mtv = getMutable(follow(*ty)); if (mtv) @@ -1184,10 +1124,10 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatFor& expr) loopScope->bindings[expr.var] = {loopVarType, expr.var->location}; if (!expr.from) - ice("Bad AstStatFor has no from expr"); + ice(XorStr("Bad AstStatFor has no from expr")); if (!expr.to) - ice("Bad AstStatFor has no to expr"); + ice(XorStr("Bad AstStatFor has no to expr")); unify(checkExpr(loopScope, *expr.from).type, loopVarType, expr.from->location); unify(checkExpr(loopScope, *expr.to).type, loopVarType, expr.to->location); @@ -1232,7 +1172,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin) // (free) -> ((free) -> R, Table | nil, K | nil) if (!firstValue) - ice("expected at least an iterator function value, but we parsed nothing"); + ice(XorStr("expected at least an iterator function value, but we parsed nothing")); TypeId iterTy = nullptr; TypePackId callRetPack = nullptr; @@ -1266,7 +1206,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin) iterTy = instantiate(scope, checkExpr(scope, *firstValue).type, firstValue->location); } - if (std::optional iterMM = findMetatableEntry(iterTy, "__iter", firstValue->location, /* addErrors= */ true)) + if (std::optional iterMM = findMetatableEntry(iterTy, "__iter", firstValue->location)) { // if __iter metamethod is present, it will be called and the results are going to be called as if they are functions // TODO: this needs to typecheck all returned values by __iter as if they were for loop arguments @@ -1292,11 +1232,6 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin) for (size_t i = 2; i < varTypes.size(); ++i) unify(nilType, varTypes[i], forin.location); } - else if (isNonstrictMode()) - { - for (TypeId var : varTypes) - unify(anyType, var, forin.location); - } else { TypeId varTy = errorRecoveryType(loopScope); @@ -1318,7 +1253,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin) for (TypeId var : varTypes) unify(varTy, var, forin.location); - if (!get(iterTy) && !get(iterTy) && !get(iterTy) && !get(iterTy)) + if (!get(iterTy) && !get(iterTy) && !get(iterTy)) reportError(firstValue->location, CannotCallNonFunction{iterTy}); return check(loopScope, *forin.body); @@ -1390,7 +1325,12 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco // If in nonstrict mode and allowing redefinition of global function, restore the previous definition type // in case this function has a differing signature. The signature discrepancy will be caught in checkBlock. if (previouslyDefined) + { + if (FFlag::LluReturnTypeInferenceInNonstrict && FFlag::LluLowerBoundsCalculation) + quantify(funScope, ty, exprName->location); + globalBindings[name] = oldBinding; + } else globalBindings[name] = {quantify(funScope, ty, exprName->location), exprName->location}; @@ -1410,7 +1350,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco TypeId exprTy = checkExpr(scope, *name->expr).type; TableTypeVar* ttv = getMutableTableType(exprTy); - if (!getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, /* addErrors= */ false)) + if (!getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, false)) { if (ttv || isTableIntersection(exprTy)) reportError(TypeError{function.location, CannotExtendTable{exprTy, CannotExtendTable::Property, name->index.value}}); @@ -1427,27 +1367,21 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco { const FunctionTypeVar* funTy = get(ty); if (!funTy) - ice("Methods should be functions"); + ice(XorStr("Methods should be functions")); std::optional arg0 = first(funTy->argTypes); if (!arg0) - ice("Methods should always have at least 1 argument (self)"); + ice(XorStr("Methods should always have at least 1 argument (self)")); } checkFunctionBody(funScope, ty, *function.func); - if (FFlag::LuauUnknownAndNeverType) - { - InplaceDemoter demoter{funScope->level, ¤tModule->internalTypes}; - demoter.traverse(ty); - } - if (ttv && ttv->state != TableState::Sealed) ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation}; } else { - LUAU_ASSERT(function.name->is()); + lluz_ASSERT(function.name->is()); ty = follow(ty); @@ -1505,7 +1439,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias TypeId ty = freshType(aliasScope); FreeTypeVar* ftv = getMutable(ty); - LUAU_ASSERT(ftv); + lluz_ASSERT(ftv); ftv->forwardedTypeAlias = true; bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty}; @@ -1520,7 +1454,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias return; if (!binding) - ice("Not predeclared"); + ice(XorStr("Not predeclared")); ScopePtr aliasScope = childScope(scope, typealias.location); aliasScope->level = scope->level.incr(); @@ -1528,14 +1462,14 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias for (auto param : binding->typeParams) { auto generic = get(param.ty); - LUAU_ASSERT(generic); + lluz_ASSERT(generic); aliasScope->privateTypeBindings[generic->name] = TypeFun{{}, param.ty}; } for (auto param : binding->typePackParams) { auto generic = get(param.tp); - LUAU_ASSERT(generic); + lluz_ASSERT(generic); aliasScope->privateTypePackBindings[generic->name] = param.tp; } @@ -1572,7 +1506,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias bool isNormal = ty->normal; ty = addType(std::move(clone)); - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) asMutable(ty)->normal = isNormal; } } @@ -1601,7 +1535,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatTypeAlias& typealias if (unify(ty, bindingType, typealias.location)) bindingType = ty; - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) { auto [t, ok] = normalize(bindingType, currentModule, *iceHandler); bindingType = t; @@ -1626,13 +1560,13 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar } // We don't have generic classes, so this assertion _should_ never be hit. - LUAU_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0); + lluz_ASSERT(lookupType->typeParams.size() == 0 && lookupType->typePackParams.size() == 0); superTy = lookupType->type; if (!get(follow(*superTy))) { reportError(declaredClass.location, - GenericError{format("Cannot use non-class type '%s' as a superclass of class '%s'", superName.c_str(), declaredClass.name.value)}); + GenericError{format(XorStr("Cannot use non-class type '%s' as a superclass of class '%s'"), superName.c_str(), declaredClass.name.value)}); return; } @@ -1666,7 +1600,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}}); ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes}); - if (FFlag::LuauSelfCallAutocompleteFix3) + if (FFlag::LluSelfCallAutocompleteFix2) ftv->hasSelf = true; } } @@ -1706,7 +1640,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& declar } else { - reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())}); + reportError(declaredClass.location, GenericError{format(XorStr("Cannot overload non-function class member '%s'"), propName.c_str())}); } } } @@ -1748,7 +1682,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFunction& glo WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExpr& expr, std::optional expectedType, bool forceSingleton) { RecursionCounter _rc(&checkRecursionCount); - if (FInt::LuauCheckRecursionLimit > 0 && checkRecursionCount >= FInt::LuauCheckRecursionLimit) + if (FInt::LluCheckRecursionLimit > 0 && checkRecursionCount >= FInt::LluCheckRecursionLimit) { reportErrorCodeTooComplex(expr.location); return {errorRecoveryType(scope)}; @@ -1795,7 +1729,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp else if (auto a = expr.as()) result = checkExpr(scope, *a); else if (auto a = expr.as()) - result = checkExpr(scope, *a, FFlag::LuauBinaryNeedsExpectedTypesToo ? expectedType : std::nullopt); + result = checkExpr(scope, *a); else if (auto a = expr.as()) result = checkExpr(scope, *a); else if (auto a = expr.as()) @@ -1803,7 +1737,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp else if (auto a = expr.as()) result = checkExpr(scope, *a, expectedType); else - ice("Unhandled AstExpr?"); + ice(XorStr("Unhandled AstExpr?")); result.type = follow(result.type); @@ -1819,7 +1753,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprLocal& expr) { std::optional lvalue = tryGetLValue(expr); - LUAU_ASSERT(lvalue); // Guaranteed to not be nullopt - AstExprLocal is an LValue. + lluz_ASSERT(lvalue); // Guaranteed to not be nullopt - AstExprLocal is an LValue. if (std::optional ty = resolveLValue(scope, *lvalue)) return {*ty, {TruthyPredicate{std::move(*lvalue), expr.location}}}; @@ -1833,7 +1767,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprGlobal& expr) { std::optional lvalue = tryGetLValue(expr); - LUAU_ASSERT(lvalue); // Guaranteed to not be nullopt - AstExprGlobal is an LValue. + lluz_ASSERT(lvalue); // Guaranteed to not be nullopt - AstExprGlobal is an LValue. if (std::optional ty = resolveLValue(scope, *lvalue)) return {*ty, {TruthyPredicate{std::move(*lvalue), expr.location}}}; @@ -1869,7 +1803,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp return {errorRecoveryType(scope)}; } else - ice("Unknown TypePack type in checkExpr(AstExprVarargs)!"); + ice(XorStr("Unknown TypePack type in checkExpr(AstExprVarargs)!")); } WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprCall& expr) @@ -1883,7 +1817,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp } else if (const FreeTypePack* ftp = get(retPack)) { - TypeLevel level = FFlag::LuauLowerBoundsCalculation ? ftp->level : scope->level; + TypeLevel level = FFlag::LluLowerBoundsCalculation ? ftp->level : scope->level; TypeId head = freshType(level); TypePackId pack = addTypePack(TypePackVar{TypePack{{head}, freshTypePack(level)}}); unify(pack, retPack, expr.location); @@ -1895,7 +1829,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp return {vtp->ty, std::move(result.predicates)}; else if (get(retPack)) { - if (FFlag::LuauReturnAnyInsteadOfICE) + if (FFlag::LluReturnAnyInsteadOfICE) return {anyType, std::move(result.predicates)}; else ice("Unexpected abstract type pack!", expr.location); @@ -1917,57 +1851,42 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp lhsType = stripFromNilAndReport(lhsType, expr.expr->location); - if (std::optional ty = getIndexTypeFromType(scope, lhsType, name, expr.location, /* addErrors= */ true)) + if (std::optional ty = getIndexTypeFromType(scope, lhsType, name, expr.location, true)) return {*ty}; return {errorRecoveryType(scope)}; } -std::optional TypeChecker::findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location, bool addErrors) +std::optional TypeChecker::findTablePropertyRespectingMeta(TypeId lhsType, Name name, const Location& location) { ErrorVec errors; - auto result = Luau::findTablePropertyRespectingMeta(errors, lhsType, name, location); - if (!FFlag::LuauIndexSilenceErrors || addErrors) - reportErrors(errors); + auto result = lluz::findTablePropertyRespectingMeta(errors, lhsType, name, location); + reportErrors(errors); return result; } -std::optional TypeChecker::findMetatableEntry(TypeId type, std::string entry, const Location& location, bool addErrors) +std::optional TypeChecker::findMetatableEntry(TypeId type, std::string entry, const Location& location) { ErrorVec errors; - auto result = Luau::findMetatableEntry(errors, type, entry, location); - if (!FFlag::LuauIndexSilenceErrors || addErrors) - reportErrors(errors); + auto result = lluz::findMetatableEntry(errors, type, entry, location); + reportErrors(errors); return result; } std::optional TypeChecker::getIndexTypeFromType( - const ScopePtr& scope, TypeId type, const Name& name, const Location& location, bool addErrors) -{ - size_t errorCount = currentModule->errors.size(); - - std::optional result = getIndexTypeFromTypeImpl(scope, type, name, location, addErrors); - - if (FFlag::LuauIndexSilenceErrors && !addErrors) - LUAU_ASSERT(errorCount == currentModule->errors.size()); - - return result; -} - -std::optional TypeChecker::getIndexTypeFromTypeImpl( - const ScopePtr& scope, TypeId type, const Name& name, const Location& location, bool addErrors) + const ScopePtr& scope, TypeId type, const std::string& name, const Location& location, bool addErrors) { type = follow(type); - if (get(type) || get(type) || get(type)) + if (get(type) || get(type)) return type; tablify(type); if (isString(type)) { - std::optional mtIndex = findMetatableEntry(stringType, "__index", location, addErrors); - LUAU_ASSERT(mtIndex); + std::optional mtIndex = findMetatableEntry(stringType, "__index", location); + lluz_ASSERT(mtIndex); type = *mtIndex; } @@ -1980,7 +1899,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( // TODO: Property lookup should work with string singletons or unions thereof as the indexer key type. ErrorVec errors = tryUnify(stringType, indexer->indexType, location); - if (FFlag::LuauReportErrorsOnIndexerKeyMismatch) + if (FFlag::LluReportErrorsOnIndexerKeyMismatch) { if (errors.empty()) return indexer->indexResultType; @@ -2000,7 +1919,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( return result; } - if (auto found = findTablePropertyRespectingMeta(type, name, location, addErrors)) + if (auto found = findTablePropertyRespectingMeta(type, name, location)) return *found; } else if (const ClassTypeVar* cls = get(type)) @@ -2016,13 +1935,13 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( for (TypeId t : utv) { - RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit); + RecursionLimiter _rl(&recursionCount, FInt::LluTypeInferRecursionLimit); // Not needed when we normalize types. if (get(follow(t))) return t; - if (std::optional ty = getIndexTypeFromType(scope, t, name, location, /* addErrors= */ false)) + if (std::optional ty = getIndexTypeFromType(scope, t, name, location, false)) goodOptions.push_back(*ty); else badOptions.push_back(t); @@ -2040,7 +1959,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( return std::nullopt; } - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) { auto [t, ok] = normalize(addType(UnionTypeVar{std::move(goodOptions)}), currentModule, *iceHandler); // FIXME Inefficient. We craft a UnionTypeVar and immediately throw it away. @@ -2053,8 +1972,6 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( else { std::vector result = reduceUnion(goodOptions); - if (FFlag::LuauUnknownAndNeverType && result.empty()) - return neverType; if (result.size() == 1) return result[0]; @@ -2068,9 +1985,9 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( for (TypeId t : itv->parts) { - RecursionLimiter _rl(&recursionCount, FInt::LuauTypeInferRecursionLimit); + RecursionLimiter _rl(&recursionCount, FInt::LluTypeInferRecursionLimit); - if (std::optional ty = getIndexTypeFromType(scope, t, name, location, /* addErrors= */ false)) + if (std::optional ty = getIndexTypeFromType(scope, t, name, location, false)) parts.push_back(*ty); } @@ -2100,22 +2017,17 @@ std::vector TypeChecker::reduceUnion(const std::vector& types) for (TypeId t : types) { t = follow(t); - if (get(t)) - continue; - if (get(t) || get(t)) return {t}; if (const UnionTypeVar* utv = get(t)) { - if (FFlag::LuauReduceUnionRecursion) + if (FFlag::LluReduceUnionRecursion) { for (TypeId ty : utv) { - if (FFlag::LuauNormalizeFlagIsConservative) + if (FFlag::LluNormalizeFlagIsConservative) ty = follow(ty); - if (get(ty)) - continue; if (get(ty) || get(ty)) return {ty}; @@ -2129,8 +2041,6 @@ std::vector TypeChecker::reduceUnion(const std::vector& types) for (TypeId ty : r) { ty = follow(ty); - if (get(ty)) - continue; if (get(ty) || get(ty)) return {ty}; @@ -2307,7 +2217,7 @@ TypeId TypeChecker::checkExprTable( WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprTable& expr, std::optional expectedType) { RecursionCounter _rc(&checkRecursionCount); - if (FInt::LuauCheckRecursionLimit > 0 && checkRecursionCount >= FInt::LuauCheckRecursionLimit) + if (FInt::LluCheckRecursionLimit > 0 && checkRecursionCount >= FInt::LluCheckRecursionLimit) { reportErrorCodeTooComplex(expr.location); return {errorRecoveryType(scope)}; @@ -2365,16 +2275,9 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp { std::vector expectedResultTypes; for (TypeId expectedOption : expectedUnion) - { if (const TableTypeVar* ttv = get(follow(expectedOption))) - { if (auto prop = ttv->props.find(key->value.data); prop != ttv->props.end()) expectedResultTypes.push_back(prop->second.type); - else if (FFlag::LuauExpectedTableUnionIndexerType && ttv->indexer && maybeString(ttv->indexer->indexType)) - expectedResultTypes.push_back(ttv->indexer->indexResultType); - } - } - if (expectedResultTypes.size() == 1) expectedResultType = expectedResultTypes[0]; else if (expectedResultTypes.size() > 1) @@ -2411,14 +2314,14 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp return {booleanType, {NotPredicate{std::move(result.predicates)}}}; case AstExprUnary::Minus: { - const bool operandIsAny = get(operandType) || get(operandType) || get(operandType); + const bool operandIsAny = get(operandType) || get(operandType); if (operandIsAny) return {operandType}; if (typeCouldHaveMetatable(operandType)) { - if (auto fnt = findMetatableEntry(operandType, "__unm", expr.location, /* addErrors= */ true)) + if (auto fnt = findMetatableEntry(operandType, "__unm", expr.location)) { TypeId actualFunctionType = instantiate(scope, *fnt, expr.location); TypePackId arguments = addTypePack({operandType}); @@ -2439,7 +2342,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp } reportError(expr.location, - GenericError{format("Unary operator '%s' not supported by type '%s'", toString(expr.op).c_str(), toString(operandType).c_str())}); + GenericError{format(XorStr("Unary operator '%s' not supported by type '%s'"), toString(expr.op).c_str(), toString(operandType).c_str())}); return {errorRecoveryType(scope)}; } @@ -2452,21 +2355,14 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp operandType = stripFromNilAndReport(operandType, expr.location); - // # operator is guaranteed to return number - if ((FFlag::LuauNeverTypesAndOperatorsInference && get(operandType)) || get(operandType) || - get(operandType)) - { - if (FFlag::LuauNeverTypesAndOperatorsInference) - return {numberType}; - else - return {!FFlag::LuauUnknownAndNeverType ? errorRecoveryType(scope) : operandType}; - } + if (get(operandType)) + return {errorRecoveryType(scope)}; DenseHashSet seen{nullptr}; - if (typeCouldHaveMetatable(operandType)) + if (FFlag::LluCheckLenMT && typeCouldHaveMetatable(operandType)) { - if (auto fnt = findMetatableEntry(operandType, "__len", expr.location, /* addErrors= */ true)) + if (auto fnt = findMetatableEntry(operandType, "__len", expr.location)) { TypeId actualFunctionType = instantiate(scope, *fnt, expr.location); TypePackId arguments = addTypePack({operandType}); @@ -2497,29 +2393,29 @@ std::string opToMetaTableEntry(const AstExprBinary::Op& op) { case AstExprBinary::CompareNe: case AstExprBinary::CompareEq: - return "__eq"; + return XorStr("__eq"); case AstExprBinary::CompareLt: case AstExprBinary::CompareGe: - return "__lt"; + return XorStr("__lt"); case AstExprBinary::CompareLe: case AstExprBinary::CompareGt: - return "__le"; + return XorStr("__le"); case AstExprBinary::Add: - return "__add"; + return XorStr("__add"); case AstExprBinary::Sub: - return "__sub"; + return XorStr("__sub"); case AstExprBinary::Mul: - return "__mul"; + return XorStr("__mul"); case AstExprBinary::Div: - return "__div"; + return XorStr("__div"); case AstExprBinary::Mod: - return "__mod"; + return XorStr("__mod"); case AstExprBinary::Pow: - return "__pow"; + return XorStr("__pow"); case AstExprBinary::Concat: - return "__concat"; + return XorStr("__concat"); default: - return ""; + return XorStr(""); } } @@ -2537,9 +2433,6 @@ TypeId TypeChecker::unionOfTypes(TypeId a, TypeId b, const Location& location, b return a; std::vector types = reduceUnion({a, b}); - if (FFlag::LuauUnknownAndNeverType && types.empty()) - return neverType; - if (types.size() == 1) return types[0]; @@ -2592,7 +2485,7 @@ TypeId TypeChecker::checkRelationalOperation( // If we know nothing at all about the lhs type, we can usually say nothing about the result. // The notable exception to this is the equality and inequality operators, which always produce a boolean. - const bool lhsIsAny = get(lhsType) || get(lhsType) || get(lhsType); + const bool lhsIsAny = get(lhsType) || get(lhsType); // Peephole check for `cond and a or b -> type(a)|type(b)` // TODO: Kill this when singleton types arrive. :( @@ -2615,7 +2508,7 @@ TypeId TypeChecker::checkRelationalOperation( if (isNonstrictMode() && (isNil(lhsType) || isNil(rhsType))) return booleanType; - const bool rhsIsAny = get(rhsType) || get(rhsType) || get(rhsType); + const bool rhsIsAny = get(rhsType) || get(rhsType); if (lhsIsAny || rhsIsAny) return booleanType; @@ -2626,13 +2519,6 @@ TypeId TypeChecker::checkRelationalOperation( case AstExprBinary::CompareGe: case AstExprBinary::CompareLe: { - if (FFlag::LuauNeverTypesAndOperatorsInference) - { - // If one of the operand is never, it doesn't make sense to unify these. - if (get(lhsType) || get(rhsType)) - return booleanType; - } - /* Subtlety here: * We need to do this unification first, but there are situations where we don't actually want to * report any problems that might have been surfaced as a result of this step because we might already @@ -2654,7 +2540,7 @@ TypeId TypeChecker::checkRelationalOperation( if (!isEquality && state.errors.empty() && (get(leftType) || isBoolean(leftType))) { - reportError(expr.location, GenericError{format("Type '%s' cannot be compared with relational operator %s", toString(leftType).c_str(), + reportError(expr.location, GenericError{format(XorStr("Type '%s' cannot be compared with relational operator %s"), toString(leftType).c_str(), toString(expr.op).c_str())}); } @@ -2702,7 +2588,7 @@ TypeId TypeChecker::checkRelationalOperation( if (!matches) { reportError( - expr.location, GenericError{format("Types %s and %s cannot be compared with %s because they do not have the same metatable", + expr.location, GenericError{format(XorStr("Types %s and %s cannot be compared with %s because they do not have the same metatable"), toString(lhsType).c_str(), toString(rhsType).c_str(), toString(expr.op).c_str())}); return errorRecoveryType(booleanType); } @@ -2710,7 +2596,7 @@ TypeId TypeChecker::checkRelationalOperation( if (leftMetatable) { - std::optional metamethod = findMetatableEntry(lhsType, metamethodName, expr.location, /* addErrors= */ true); + std::optional metamethod = findMetatableEntry(lhsType, metamethodName, expr.location); if (metamethod) { if (const FunctionTypeVar* ftv = get(*metamethod)) @@ -2722,7 +2608,7 @@ TypeId TypeChecker::checkRelationalOperation( if (!state.errors.empty()) { - reportError(expr.location, GenericError{format("Metamethod '%s' must return type 'boolean'", metamethodName.c_str())}); + reportError(expr.location, GenericError{format(XorStr("Metamethod '%s' must return type 'boolean'"), metamethodName.c_str())}); return errorRecoveryType(booleanType); } @@ -2744,7 +2630,7 @@ TypeId TypeChecker::checkRelationalOperation( else if (needsMetamethod) { reportError( - expr.location, GenericError{format("Table %s does not offer metamethod %s", toString(lhsType).c_str(), metamethodName.c_str())}); + expr.location, GenericError{format(XorStr("Table %s does not offer metamethod %s"), toString(lhsType).c_str(), metamethodName.c_str())}); return errorRecoveryType(booleanType); } } @@ -2758,7 +2644,7 @@ TypeId TypeChecker::checkRelationalOperation( if (needsMetamethod) { - reportError(expr.location, GenericError{format("Type %s cannot be compared with %s because it has no metatable", + reportError(expr.location, GenericError{format(XorStr("Type %s cannot be compared with %s because it has no metatable"), toString(lhsType).c_str(), toString(expr.op).c_str())}); return errorRecoveryType(booleanType); } @@ -2775,8 +2661,8 @@ TypeId TypeChecker::checkRelationalOperation( return lhsType; return unionOfTypes(lhsType, rhsType, expr.location); default: - LUAU_ASSERT(0); - ice(format("checkRelationalOperation called with incorrect binary expression '%s'", toString(expr.op).c_str()), expr.location); + lluz_ASSERT(0); + ice(format(XorStr("checkRelationalOperation called with incorrect binary expression '%s'"), toString(expr.op).c_str()), expr.location); } } @@ -2810,10 +2696,8 @@ TypeId TypeChecker::checkBinaryOperation( // If we know nothing at all about the lhs type, we can usually say nothing about the result. // The notable exception to this is the equality and inequality operators, which always produce a boolean. - const bool lhsIsAny = get(lhsType) || get(lhsType) || - (FFlag::LuauUnknownAndNeverType && FFlag::LuauNeverTypesAndOperatorsInference && get(lhsType)); - const bool rhsIsAny = get(rhsType) || get(rhsType) || - (FFlag::LuauUnknownAndNeverType && FFlag::LuauNeverTypesAndOperatorsInference && get(rhsType)); + const bool lhsIsAny = get(lhsType) || get(lhsType); + const bool rhsIsAny = get(rhsType) || get(rhsType); if (lhsIsAny) return lhsType; @@ -2873,15 +2757,15 @@ TypeId TypeChecker::checkBinaryOperation( }; std::string op = opToMetaTableEntry(expr.op); - if (auto fnt = findMetatableEntry(lhsType, op, expr.location, /* addErrors= */ true)) + if (auto fnt = findMetatableEntry(lhsType, op, expr.location)) return checkMetatableCall(*fnt, lhsType, rhsType); - if (auto fnt = findMetatableEntry(rhsType, op, expr.location, /* addErrors= */ true)) + if (auto fnt = findMetatableEntry(rhsType, op, expr.location)) { // Note the intentionally reversed arguments here. return checkMetatableCall(*fnt, rhsType, lhsType); } - reportError(expr.location, GenericError{format("Binary operator '%s' not supported by types '%s' and '%s'", toString(expr.op).c_str(), + reportError(expr.location, GenericError{format(XorStr("Binary operator '%s' not supported by types '%s' and '%s'"), toString(expr.op).c_str(), toString(lhsType).c_str(), toString(rhsType).c_str())}); return errorRecoveryType(scope); @@ -2904,32 +2788,32 @@ TypeId TypeChecker::checkBinaryOperation( return numberType; default: // These should have been handled with checkRelationalOperation - LUAU_ASSERT(0); + lluz_ASSERT(0); return anyType; } } -WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBinary& expr, std::optional expectedType) +WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExprBinary& expr) { if (expr.op == AstExprBinary::And) { - auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType); + auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left); ScopePtr innerScope = childScope(scope, expr.location); resolve(lhsPredicates, innerScope, true); - auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType); + auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right); return {checkBinaryOperation(scope, expr, lhsTy, rhsTy), {AndPredicate{std::move(lhsPredicates), std::move(rhsPredicates)}}}; } else if (expr.op == AstExprBinary::Or) { - auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left, expectedType); + auto [lhsTy, lhsPredicates] = checkExpr(scope, *expr.left); ScopePtr innerScope = childScope(scope, expr.location); resolve(lhsPredicates, innerScope, false); - auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right, expectedType); + auto [rhsTy, rhsPredicates] = checkExpr(innerScope, *expr.right); // Because of C++, I'm not sure if lhsPredicates was not moved out by the time we call checkBinaryOperation. TypeId result = checkBinaryOperation(scope, expr, lhsTy, rhsTy, lhsPredicates); @@ -2940,8 +2824,6 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp if (auto predicate = tryGetTypeGuardPredicate(expr)) return {booleanType, {std::move(*predicate)}}; - // For these, passing expectedType is worse than simply forcing them, because their implementation - // may inadvertently check if expectedTypes exist first and use it, instead of forceSingleton first. WithPredicate lhs = checkExpr(scope, *expr.left, std::nullopt, /*forceSingleton=*/true); WithPredicate rhs = checkExpr(scope, *expr.right, std::nullopt, /*forceSingleton=*/true); @@ -2960,7 +2842,6 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp } else { - // Expected types are not useful for other binary operators. WithPredicate lhs = checkExpr(scope, *expr.left); WithPredicate rhs = checkExpr(scope, *expr.right); @@ -3015,8 +2896,6 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp return {trueType.type}; std::vector types = reduceUnion({trueType.type, falseType.type}); - if (FFlag::LuauUnknownAndNeverType && types.empty()) - return {neverType}; return {types.size() == 1 ? types[0] : addType(UnionTypeVar{std::move(types)})}; } @@ -3048,10 +2927,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExpr& exp TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprLocal& expr) { if (std::optional ty = scope->lookup(expr.local)) - { - ty = follow(*ty); - return get(*ty) ? unknownType : *ty; - } + return *ty; reportError(expr.location, UnknownSymbol{expr.local->name.value, UnknownSymbol::Binding}); return errorRecoveryType(scope); @@ -3065,10 +2941,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprGloba const auto it = moduleScope->bindings.find(expr.name); if (it != moduleScope->bindings.end()) - { - TypeId ty = follow(it->second.typeId); - return get(ty) ? unknownType : ty; - } + return it->second.typeId; TypeId result = freshType(scope); Binding& binding = moduleScope->bindings[expr.name]; @@ -3089,9 +2962,6 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex if (get(lhs) || get(lhs)) return lhs; - if (get(lhs)) - return unknownType; - tablify(lhs); Name name = expr.index.value; @@ -3153,7 +3023,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex } else if (get(lhs)) { - if (std::optional ty = getIndexTypeFromType(scope, lhs, name, expr.location, /* addErrors= */ false)) + if (std::optional ty = getIndexTypeFromType(scope, lhs, name, expr.location, false)) return *ty; // If intersection has a table part, report that it cannot be extended just as a sealed table @@ -3180,9 +3050,6 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex if (get(exprType) || get(exprType)) return exprType; - if (get(exprType)) - return unknownType; - AstExprConstantString* value = expr.index->as(); if (value) @@ -3289,7 +3156,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T if (!ttv || ttv->state == TableState::Sealed) { - if (auto ty = getIndexTypeFromType(scope, lhsType, indexName->index.value, indexName->indexLocation, /* addErrors= */ false)) + if (auto ty = getIndexTypeFromType(scope, lhsType, indexName->index.value, indexName->indexLocation, false)) return *ty; return errorRecoveryType(scope); @@ -3334,7 +3201,7 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& if (expectedType) { - LUAU_ASSERT(!expr.self); + lluz_ASSERT(!expr.self); if (auto ftv = get(follow(*expectedType))) { @@ -3361,12 +3228,9 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& } } - if (!FFlag::LuauCheckGenericHOFTypes) - { - // We do not infer type binders, so if a generic function is required we do not propagate - if (expectedFunctionType && !(expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())) - expectedFunctionType = nullptr; - } + // We do not infer type binders, so if a generic function is required we do not propagate + if (expectedFunctionType && !(expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())) + expectedFunctionType = nullptr; } auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks); @@ -3374,10 +3238,9 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& TypePackId retPack; if (expr.returnAnnotation) retPack = resolveTypePack(funScope, *expr.returnAnnotation); - else if (isNonstrictMode()) + else if (FFlag::LluReturnTypeInferenceInNonstrict ? (!FFlag::LluLowerBoundsCalculation && isNonstrictMode()) : isNonstrictMode()) retPack = anyTypePack; - else if (expectedFunctionType && - (!FFlag::LuauCheckGenericHOFTypes || (expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty()))) + else if (expectedFunctionType) { auto [head, tail] = flatten(expectedFunctionType->retTypes); @@ -3422,7 +3285,7 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& funScope->varargPack = anyTypePack; } } - else if (FFlag::LuauLowerBoundsCalculation && !isNonstrictMode()) + else if (FFlag::LluLowerBoundsCalculation && !isNonstrictMode()) { funScope->varargPack = addTypePack(TypePackVar{VariadicTypePack{anyType, /*hidden*/ true}}); } @@ -3431,7 +3294,7 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& funScope->returnType = retPack; - if (FFlag::DebugLuauSharedSelf) + if (FFlag::DebugLluSharedSelf) { if (expr.self) { @@ -3508,50 +3371,16 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& defn.originalNameLocation = originalName.value_or(Location(expr.location.begin, 0)); std::vector genericTys; - // if we have a generic expected function type and no generics, we should use the expected ones. - if (FFlag::LuauCheckGenericHOFTypes) - { - if (expectedFunctionType && generics.empty()) - { - genericTys = expectedFunctionType->generics; - } - else - { - genericTys.reserve(generics.size()); - for (const GenericTypeDefinition& generic : generics) - genericTys.push_back(generic.ty); - } - } - else - { - genericTys.reserve(generics.size()); - std::transform(generics.begin(), generics.end(), std::back_inserter(genericTys), [](auto&& el) { - return el.ty; - }); - } + genericTys.reserve(generics.size()); + std::transform(generics.begin(), generics.end(), std::back_inserter(genericTys), [](auto&& el) { + return el.ty; + }); std::vector genericTps; - // if we have a generic expected function type and no generic typepacks, we should use the expected ones. - if (FFlag::LuauCheckGenericHOFTypes) - { - if (expectedFunctionType && genericPacks.empty()) - { - genericTps = expectedFunctionType->genericPacks; - } - else - { - genericTps.reserve(genericPacks.size()); - for (const GenericTypePackDefinition& generic : genericPacks) - genericTps.push_back(generic.tp); - } - } - else - { - genericTps.reserve(genericPacks.size()); - std::transform(genericPacks.begin(), genericPacks.end(), std::back_inserter(genericTps), [](auto&& el) { - return el.tp; - }); - } + genericTps.reserve(genericPacks.size()); + std::transform(genericPacks.begin(), genericPacks.end(), std::back_inserter(genericTps), [](auto&& el) { + return el.tp; + }); TypeId funTy = addType(FunctionTypeVar(funScope->level, std::move(genericTys), std::move(genericTps), argPack, retPack, std::move(defn), bool(expr.self))); @@ -3597,12 +3426,12 @@ static Location getEndLocation(const AstExprFunction& function) void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstExprFunction& function) { - LUAU_TIMETRACE_SCOPE("TypeChecker::checkFunctionBody", "TypeChecker"); + lluz_TIMETRACE_SCOPE(XorStr("TypeChecker::checkFunctionBody", "TypeChecker")); if (function.debugname.value) - LUAU_TIMETRACE_ARGUMENT("name", function.debugname.value); + lluz_TIMETRACE_ARGUMENT("name", function.debugname.value); else - LUAU_TIMETRACE_ARGUMENT("line", std::to_string(function.location.begin.line).c_str()); + lluz_TIMETRACE_ARGUMENT("line", std::to_string(function.location.begin.line).c_str()); if (FunctionTypeVar* funTy = getMutable(ty)) { @@ -3639,31 +3468,15 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes}); } } - - if (!currentModule->astTypes.find(&function)) - currentModule->astTypes[&function] = ty; } else - ice("Checking non functional type"); + ice(XorStr("Checking non functional type")); } WithPredicate TypeChecker::checkExprPack(const ScopePtr& scope, const AstExpr& expr) -{ - if (FFlag::LuauUnknownAndNeverType) - { - WithPredicate result = checkExprPackHelper(scope, expr); - if (containsNever(result.type)) - return {uninhabitableTypePack}; - return result; - } - else - return checkExprPackHelper(scope, expr); -} - -WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope, const AstExpr& expr) { if (auto a = expr.as()) - return checkExprPackHelper(scope, *a); + return checkExprPack(scope, *a); else if (expr.is()) { if (!scope->varargPack) @@ -3803,10 +3616,7 @@ void TypeChecker::checkArgumentList( } TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}}); - if (FFlag::LuauReturnsFromCallsitesAreNotWidened) - state.tryUnify(tail, varPack); - else - state.tryUnify(varPack, tail); + state.tryUnify(varPack, tail); return; } } @@ -3826,7 +3636,7 @@ void TypeChecker::checkArgumentList( size_t minParams = getMinParameterCount(&state.log, paramPack); std::optional tail = flatten(paramPack, state.log).second; - bool isVariadic = tail && Luau::isVariadic(*tail); + bool isVariadic = tail && lluz::isVariadic(*tail); state.reportError(TypeError{state.location, CountMismatch{minParams, paramIndex, CountMismatch::Context::Arg, isVariadic}}); return; @@ -3858,7 +3668,7 @@ void TypeChecker::checkArgumentList( } else if (auto vtp = state.log.getMutable(tail)) { - if (FFlag::LuauLowerBoundsCalculation && vtp->hidden) + if (FFlag::LluLowerBoundsCalculation && vtp->hidden) { // We know that this function can technically be oversaturated, but we have its definition and we // know that it's useless. @@ -3929,7 +3739,7 @@ void TypeChecker::checkArgumentList( } } -WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope, const AstExprCall& expr) +WithPredicate TypeChecker::checkExprPack(const ScopePtr& scope, const AstExprCall& expr) { // evaluate type of function // decompose an intersection into its component overloads @@ -3948,12 +3758,12 @@ WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope { AstExprIndexName* indexExpr = expr.func->as(); if (!indexExpr) - ice("method call expression has no 'self'"); + ice(XorStr("method call expression has no 'self'")); selfType = checkExpr(scope, *indexExpr->expr).type; selfType = stripFromNilAndReport(selfType, expr.func->location); - if (std::optional propTy = getIndexTypeFromType(scope, selfType, indexExpr->index.value, expr.location, /* addErrors= */ true)) + if (std::optional propTy = getIndexTypeFromType(scope, selfType, indexExpr->index.value, expr.location, true)) { functionType = *propTy; actualFunctionType = instantiate(scope, functionType, expr.func->location); @@ -3971,7 +3781,7 @@ WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope } TypePackId retPack; - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) { retPack = freshTypePack(scope->level); } @@ -4003,25 +3813,11 @@ WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope if (get(argPack)) return {errorRecoveryTypePack(scope)}; - TypePack* args = nullptr; - if (FFlag::LuauUnknownAndNeverType) - { - if (expr.self) - { - argPack = addTypePack(TypePack{{selfType}, argPack}); - argListResult.type = argPack; - } - args = getMutable(argPack); - LUAU_ASSERT(args); - } - else - { - args = getMutable(argPack); - LUAU_ASSERT(args != nullptr); + TypePack* args = getMutable(argPack); + lluz_ASSERT(args != nullptr); - if (expr.self) - args->head.insert(args->head.begin(), selfType); - } + if (expr.self) + args->head.insert(args->head.begin(), selfType); std::vector argLocations; argLocations.reserve(expr.args.size + 1); @@ -4080,10 +3876,7 @@ std::vector> TypeChecker::getExpectedTypesForCall(const st else { std::vector result = reduceUnion({*el, ty}); - if (FFlag::LuauUnknownAndNeverType && result.empty()) - el = neverType; - else - el = result.size() == 1 ? result[0] : addType(UnionTypeVar{std::move(result)}); + el = result.size() == 1 ? result[0] : addType(UnionTypeVar{std::move(result)}); } } }; @@ -4122,7 +3915,7 @@ std::optional> TypeChecker::checkCallOverload(const Sc TypePackId argPack, TypePack* args, const std::vector* argLocations, const WithPredicate& argListResult, std::vector& overloadsThatMatchArgCount, std::vector& overloadsThatDont, std::vector& errors) { - LUAU_ASSERT(argLocations); + lluz_ASSERT(argLocations); fn = stripFromNilAndReport(fn, expr.func->location); @@ -4137,9 +3930,6 @@ std::optional> TypeChecker::checkCallOverload(const Sc return {{errorRecoveryTypePack(scope)}}; } - if (get(fn)) - return {{uninhabitableTypePack}}; - if (auto ftv = get(fn)) { // fn is one of the overloads of actualFunctionType, which @@ -4185,7 +3975,7 @@ std::optional> TypeChecker::checkCallOverload(const Sc // Might be a callable table if (const MetatableTypeVar* mttv = get(fn)) { - if (std::optional ty = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, /* addErrors= */ false)) + if (std::optional ty = getIndexTypeFromType(scope, mttv->metatable, "__call", expr.func->location, false)) { // Construct arguments with 'self' added in front TypePackId metaCallArgPack = addTypePack(TypePackVar(TypePack{args->head, args->tail})); @@ -4367,7 +4157,7 @@ void TypeChecker::reportOverloadResolutionError(const ScopePtr& scope, const Ast return ftv == std::get<2>(e); }); - LUAU_ASSERT(error != errors.end()); + lluz_ASSERT(error != errors.end()); reportErrors(std::get<0>(*error)); // If only one overload matched, we don't need this error because we provided the previous errors. @@ -4412,7 +4202,6 @@ void TypeChecker::reportOverloadResolutionError(const ScopePtr& scope, const Ast WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, const Location& location, const AstArray& exprs, bool substituteFreeForNil, const std::vector& instantiateGenerics, const std::vector>& expectedTypes) { - bool uninhabitable = false; TypePackId pack = addTypePack(TypePack{}); PredicateVec predicates; // At the moment we will be pushing all predicate sets into this. Do we need some way to split them up? @@ -4443,14 +4232,7 @@ WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, cons auto [typePack, exprPredicates] = checkExprPack(scope, *expr); insert(exprPredicates); - if (FFlag::LuauUnknownAndNeverType && containsNever(typePack)) - { - // f(), g() where f() returns (never, string) or (string, never) means this whole TypePackId is uninhabitable, so return (never, - // ...never) - uninhabitable = true; - continue; - } - else if (std::optional firstTy = first(typePack)) + if (std::optional firstTy = first(typePack)) { if (!currentModule->astTypes.find(expr)) currentModule->astTypes[expr] = follow(*firstTy); @@ -4466,14 +4248,6 @@ WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, cons auto [type, exprPredicates] = checkExpr(scope, *expr, expectedType); insert(exprPredicates); - if (FFlag::LuauUnknownAndNeverType && get(type)) - { - // f(), g() where f() returns (never, string) or (string, never) means this whole TypePackId is uninhabitable, so return (never, - // ...never) - uninhabitable = true; - continue; - } - TypeId actualType = substituteFreeForNil && expr->is() ? freshType(scope) : type; if (instantiateGenerics.size() > i && instantiateGenerics[i]) @@ -4498,8 +4272,6 @@ WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, cons for (TxnLog& log : inverseLogs) log.commit(); - if (FFlag::LuauUnknownAndNeverType && uninhabitable) - return {uninhabitableTypePack}; return {pack, predicates}; } @@ -4522,8 +4294,8 @@ std::optional TypeChecker::matchRequire(const AstExprCall& call) TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& moduleInfo, const Location& location) { - LUAU_TIMETRACE_SCOPE("TypeChecker::checkRequire", "TypeChecker"); - LUAU_TIMETRACE_ARGUMENT("moduleInfo", moduleInfo.name.c_str()); + lluz_TIMETRACE_SCOPE(XorStr("TypeChecker::checkRequire", "TypeChecker")); + lluz_TIMETRACE_ARGUMENT("moduleInfo", moduleInfo.name.c_str()); if (moduleInfo.name.empty()) { @@ -4704,7 +4476,7 @@ bool Anyification::isDirty(TypePackId tp) TypeId Anyification::clean(TypeId ty) { - LUAU_ASSERT(isDirty(ty)); + lluz_ASSERT(isDirty(ty)); if (const TableTypeVar* ttv = log->getMutable(ty)) { TableTypeVar clone = TableTypeVar{ttv->props, ttv->indexer, ttv->level, TableState::Sealed}; @@ -4718,7 +4490,7 @@ TypeId Anyification::clean(TypeId ty) } else if (auto ctv = get(ty)) { - if (FFlag::LuauQuantifyConstrained) + if (FFlag::LluQuantifyConstrained) { std::vector copy = ctv->parts; for (TypeId& ty : copy) @@ -4743,7 +4515,7 @@ TypeId Anyification::clean(TypeId ty) TypePackId Anyification::clean(TypePackId tp) { - LUAU_ASSERT(isDirty(tp)); + lluz_ASSERT(isDirty(tp)); return anyTypePack; } @@ -4751,16 +4523,16 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location { ty = follow(ty); - if (FFlag::DebugLuauSharedSelf) + if (FFlag::DebugLluSharedSelf) { if (auto ftv = get(ty)) - Luau::quantify(ty, scope->level); + lluz::quantify(ty, scope->level); else if (auto ttv = getTableType(ty); ttv && ttv->selfTy) - Luau::quantify(ty, scope->level); + lluz::quantify(ty, scope->level); - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) { - auto [t, ok] = Luau::normalize(ty, currentModule, *iceHandler); + auto [t, ok] = lluz::normalize(ty, currentModule, *iceHandler); if (!ok) reportError(location, NormalizationTooComplex{}); return t; @@ -4770,20 +4542,20 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location { const FunctionTypeVar* ftv = get(ty); - if (FFlag::LuauAlwaysQuantify) + if (FFlag::LluAlwaysQuantify) { if (ftv) - Luau::quantify(ty, scope->level); + lluz::quantify(ty, scope->level); } else { if (ftv && ftv->generics.empty() && ftv->genericPacks.empty()) - Luau::quantify(ty, scope->level); + lluz::quantify(ty, scope->level); } - if (FFlag::LuauLowerBoundsCalculation && ftv) + if (FFlag::LluLowerBoundsCalculation && ftv) { - auto [t, ok] = Luau::normalize(ty, currentModule, *iceHandler); + auto [t, ok] = lluz::normalize(ty, currentModule, *iceHandler); if (!ok) reportError(location, NormalizationTooComplex{}); return t; @@ -4803,7 +4575,7 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat Instantiation instantiation{log, ¤tModule->internalTypes, scope->level}; - if (FFlag::LuauAutocompleteDynamicLimits && instantiationChildLimit) + if (FFlag::LlluztocompleteDynamicLimits && instantiationChildLimit) instantiation.childLimit = *instantiationChildLimit; std::optional instantiated = instantiation.substitute(ty); @@ -4818,7 +4590,7 @@ TypeId TypeChecker::instantiate(const ScopePtr& scope, TypeId ty, Location locat TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location) { - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) { auto [t, ok] = normalize(ty, currentModule, *iceHandler); if (!ok) @@ -4841,7 +4613,7 @@ TypeId TypeChecker::anyify(const ScopePtr& scope, TypeId ty, Location location) TypePackId TypeChecker::anyify(const ScopePtr& scope, TypePackId ty, Location location) { - if (FFlag::LuauLowerBoundsCalculation) + if (FFlag::LluLowerBoundsCalculation) { auto [t, ok] = normalize(ty, currentModule, *iceHandler); if (!ok) @@ -4930,7 +4702,7 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d break; ctv = get(*ctv->parent); - LUAU_ASSERT(ctv); + lluz_ASSERT(ctv); } } @@ -4938,7 +4710,7 @@ void TypeChecker::diagnoseMissingTableKey(UnknownProperty* utk, TypeErrorData& d data = TypeErrorData(UnknownPropButFoundLikeProp{utk->table, utk->key, candidates}); } -LUAU_NOINLINE void TypeChecker::reportErrorCodeTooComplex(const Location& location) +lluz_NOINLINE void TypeChecker::reportErrorCodeTooComplex(const Location& location) { reportError(TypeError{location, CodeTooComplex{}}); } @@ -4964,7 +4736,7 @@ ScopePtr TypeChecker::childScope(const ScopePtr& parent, const Location& locatio void TypeChecker::merge(RefinementMap& l, const RefinementMap& r) { - Luau::merge(l, r, [this](TypeId a, TypeId b) { + lluz::merge(l, r, [this](TypeId a, TypeId b) { // TODO: normalize(UnionTypeVar{{a, b}}) std::unordered_set set; @@ -5051,36 +4823,22 @@ TypeIdPredicate TypeChecker::mkTruthyPredicate(bool sense) return sense ? std::nullopt : std::optional(ty); // at this point, anything else is kept if sense is true, or replaced by nil - if (FFlag::LuauFalsyPredicateReturnsNilInstead) + if (FFlag::LluFalsyPredicateReturnsNilInstead) return sense ? ty : nilType; else return sense ? std::optional(ty) : std::nullopt; }; } -std::optional TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate predicate) +std::optional TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate) { - std::vector types = Luau::filterMap(type, predicate); + std::vector types = lluz::filterMap(type, predicate); if (!types.empty()) return types.size() == 1 ? types[0] : addType(UnionTypeVar{std::move(types)}); return std::nullopt; } -std::pair, bool> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate) -{ - if (FFlag::LuauUnknownAndNeverType) - { - TypeId ty = filterMapImpl(type, predicate).value_or(neverType); - return {ty, !bool(get(ty))}; - } - else - { - std::optional ty = filterMapImpl(type, predicate); - return {ty, bool(ty)}; - } -} - -std::pair, bool> TypeChecker::pickTypesFromSense(TypeId type, bool sense) +std::optional TypeChecker::pickTypesFromSense(TypeId type, bool sense) { return filterMap(type, mkTruthyPredicate(sense)); } @@ -5140,14 +4898,14 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno if (lit->prefix) tf = scope->lookupImportedType(lit->prefix->value, lit->name.value); - else if (FFlag::DebugLuauMagicTypes && lit->name == "_luau_ice") - ice("_luau_ice encountered", lit->location); + else if (FFlag::DebugLluMagicTypes && lit->name == XorStr("_lluz_ice")) + ice("_lluz_ice encountered", lit->location); - else if (FFlag::DebugLuauMagicTypes && lit->name == "_luau_print") + else if (FFlag::DebugLluMagicTypes && lit->name == XorStr("_lluz_print")) { if (lit->parameters.size != 1 || !lit->parameters.data[0].type) { - reportError(TypeError{annotation.location, GenericError{"_luau_print requires one generic parameter"}}); + reportError(TypeError{annotation.location, GenericError{"_lluz_print requires one generic parameter"}}); return errorRecoveryType(anyType); } @@ -5157,7 +4915,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno opts.useLineBreaks = true; TypeId param = resolveType(scope, *lit->parameters.data[0].type); - luauPrintLine(format("_luau_print\t%s\t|\t%s", toString(param, opts).c_str(), toString(lit->location).c_str())); + lluzPrintLine(format(XorStr("_lluz_print\t%s\t|\t%s"), toString(param, opts).c_str(), toString(lit->location).c_str())); return param; } @@ -5487,7 +5245,7 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack } else { - ice("Unknown AstTypePack kind"); + ice(XorStr("Unknown AstTypePack kind")); } currentModule->astResolvedTypePacks[&annotation] = result; @@ -5535,14 +5293,14 @@ bool ApplyTypeFunction::ignoreChildren(TypePackId tp) TypeId ApplyTypeFunction::clean(TypeId ty) { TypeId& arg = typeArguments[ty]; - LUAU_ASSERT(arg); + lluz_ASSERT(arg); return arg; } TypePackId ApplyTypeFunction::clean(TypePackId tp) { TypePackId& arg = typePackArguments[tp]; - LUAU_ASSERT(arg); + lluz_ASSERT(arg); return arg; } @@ -5612,7 +5370,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, GenericTypeDefinitions TypeChecker::createGenericTypes(const ScopePtr& scope, std::optional levelOpt, const AstNode& node, const AstArray& genericNames, const AstArray& genericPackNames, bool useCache) { - LUAU_ASSERT(scope->parent); + lluz_ASSERT(scope->parent); const TypeLevel level = levelOpt.value_or(scope->level); @@ -5707,25 +5465,17 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const // If we do not have a key, it means we're not trying to discriminate anything, so it's a simple matter of just filtering for a subset. if (!key) { - auto [result, ok] = filterMap(*ty, predicate); - if (FFlag::LuauUnknownAndNeverType) - { + if (std::optional result = filterMap(*ty, predicate)) addRefinement(refis, *target, *result); - } else - { - if (ok) - addRefinement(refis, *target, *result); - else - addRefinement(refis, *target, errorRecoveryType(scope)); - } + addRefinement(refis, *target, errorRecoveryType(scope)); return; } // Otherwise, we'll want to walk each option of ty, get its index type, and filter that. auto utv = get(follow(*ty)); - LUAU_ASSERT(utv); + lluz_ASSERT(utv); std::unordered_set viableTargetOptions; std::unordered_set viableChildOptions; // There may be additional refinements that apply. We add those here too. @@ -5733,30 +5483,18 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const for (TypeId option : utv) { std::optional discriminantTy; - if (auto field = Luau::get(*key)) // need to fully qualify Luau::get because of ADL. - discriminantTy = getIndexTypeFromType(scope, option, field->key, Location(), /* addErrors= */ false); + if (auto field = lluz::get(*key)) // need to fully qualify lluz::get because of ADL. + discriminantTy = getIndexTypeFromType(scope, option, field->key, Location(), false); else - LUAU_ASSERT(!"Unhandled LValue alternative?"); + lluz_ASSERT(!XorStr("Unhandled LValue alternative?")); if (!discriminantTy) return; // Do nothing. An error was already reported, as per usual. - auto [result, ok] = filterMap(*discriminantTy, predicate); - if (FFlag::LuauUnknownAndNeverType) + if (std::optional result = filterMap(*discriminantTy, predicate)) { - if (!get(*result)) - { - viableTargetOptions.insert(option); - viableChildOptions.insert(*result); - } - } - else - { - if (ok) - { - viableTargetOptions.insert(option); - viableChildOptions.insert(*result); - } + viableTargetOptions.insert(option); + viableChildOptions.insert(*result); } } @@ -5835,12 +5573,12 @@ std::optional TypeChecker::resolveLValue(const ScopePtr& scope, const LV continue; else if (auto field = get(key)) { - found = getIndexTypeFromType(scope, *found, field->key, Location(), /* addErrors= */ false); + found = getIndexTypeFromType(scope, *found, field->key, Location(), false); if (!found) return std::nullopt; // Turns out this type doesn't have the property at all. We're done. } else - LUAU_ASSERT(!"New LValue alternative not handled here."); + lluz_ASSERT(!XorStr("New LValue alternative not handled here.")); } return found; @@ -5894,7 +5632,7 @@ void TypeChecker::resolve(const Predicate& predicate, RefinementMap& refis, cons else if (auto eqP = get(predicate)) resolve(*eqP, refis, scope, sense); else - ice("Unhandled predicate kind"); + ice(XorStr("Unhandled predicate kind")); } void TypeChecker::resolve(const TruthyPredicate& truthyP, RefinementMap& refis, const ScopePtr& scope, bool sense, bool fromOr) @@ -5974,12 +5712,12 @@ void TypeChecker::resolve(const IsAPredicate& isaP, RefinementMap& refis, const // We can only have (any, Instance) because the rhs is never undecidable right now. // So we can just return the right hand side immediately. - // typeof(x) == "Instance" where x : any + // typeof(x) == XorStr("Instance") where x : any auto ttv = get(option); if (isUndecidable(option) || (ttv && ttv->state == TableState::Free)) return sense ? isaP.ty : option; - // typeof(x) == "Instance" where x : Instance + // typeof(x) == XorStr("Instance") where x : Instance if (sense) return isaP.ty; } @@ -5994,17 +5732,17 @@ void TypeChecker::resolve(const IsAPredicate& isaP, RefinementMap& refis, const void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& refis, const ScopePtr& scope, bool sense) { - // Rewrite the predicate 'type(foo) == "vector"' to be 'typeof(foo) == "Vector3"'. They're exactly identical. + // Rewrite the predicate 'type(foo) == XorStr("vector")' to be 'typeof(foo) == XorStr("Vector3")'. They're exactly identical. // This allows us to avoid writing in edge cases. - if (!typeguardP.isTypeof && typeguardP.kind == "vector") - return resolve(TypeGuardPredicate{std::move(typeguardP.lvalue), typeguardP.location, "Vector3", true}, refis, scope, sense); + if (!typeguardP.isTypeof && typeguardP.kind == XorStr("vector")) + return resolve(TypeGuardPredicate{std::move(typeguardP.lvalue), typeguardP.location, XorStr("Vector3"), true}, refis, scope, sense); std::optional ty = resolveLValue(refis, scope, typeguardP.lvalue); if (!ty) return; - // In certain cases, the value may actually be nil, but Luau doesn't know about it. So we whitelist this. - if (sense && typeguardP.kind == "nil") + // In certain cases, the value may actually be nil, but lluz doesn't know about it. So we whitelist this. + if (sense && typeguardP.kind == XorStr("nil")) { addRefinement(refis, typeguardP.lvalue, nilType); return; @@ -6015,9 +5753,6 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r auto mkFilter = [](ConditionFunc f, std::optional other = std::nullopt) -> SenseToTypeIdPredicate { return [f, other](bool sense) -> TypeIdPredicate { return [f, other, sense](TypeId ty) -> std::optional { - if (FFlag::LuauUnknownAndNeverType && sense && get(ty)) - return other.value_or(ty); - if (f(ty) == sense) return ty; @@ -6114,26 +5849,19 @@ bool TypeChecker::isNonstrictMode() const bool TypeChecker::useConstrainedIntersections() const { - return FFlag::LuauLowerBoundsCalculation && !isNonstrictMode(); + return FFlag::LluLowerBoundsCalculation && !isNonstrictMode(); } std::vector TypeChecker::unTypePack(const ScopePtr& scope, TypePackId tp, size_t expectedLength, const Location& location) { TypePackId expectedTypePack = addTypePack({}); TypePack* expectedPack = getMutable(expectedTypePack); - LUAU_ASSERT(expectedPack); + lluz_ASSERT(expectedPack); for (size_t i = 0; i < expectedLength; ++i) expectedPack->head.push_back(freshType(scope)); - size_t oldErrorsSize = currentModule->errors.size(); - unify(tp, expectedTypePack, location); - // HACK: tryUnify would undo the changes to the expectedTypePack if the length mismatches, but - // we want to tie up free types to be error types, so we do this instead. - if (FFlag::LuauUnknownAndNeverType) - currentModule->errors.resize(oldErrorsSize); - for (TypeId& tp : expectedPack->head) tp = follow(tp); @@ -6145,4 +5873,4 @@ std::vector> TypeChecker::getScopes() const return currentModule->scopes; } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/TypePack.cpp b/Analysis/src/TypePack.cpp index d4544483..eac0ab4f 100644 --- a/Analysis/src/TypePack.cpp +++ b/Analysis/src/TypePack.cpp @@ -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 -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 TypePackIterator::tail() { - LUAU_ASSERT(!tp); + lluz_ASSERT(!tp); return currentTypePack ? std::optional{currentTypePack} : std::nullopt; } @@ -227,7 +238,7 @@ TypePackId follow(TypePackId tp, std::function 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 first(TypePackId tp, bool ignoreHiddenVariadics) return std::nullopt; } -TypePackVar* asMutable(TypePackId tp) -{ - return const_cast(tp); -} - -TypePack* asMutable(const TypePack* tp) -{ - return const_cast(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(follow(*it))) - return true; - ++it; - } - - if (auto tail = it.tail()) - { - if (auto vtp = get(*tail); vtp && get(follow(vtp->ty))) - return true; - } - - return false; + return const_cast(tp); } -} // namespace Luau +TypePack* asMutable(const TypePack* tp) +{ + return const_cast(tp); +} +} // namespace lluz diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index 66b38cf3..ea505f24 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -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 findMetatableEntry(ErrorVec& errors, TypeId type, std::string entry, Location location) @@ -24,7 +24,7 @@ std::optional 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 findTablePropertyRespectingMeta(ErrorVec& errors, TypeId t return std::nullopt; } -} // namespace Luau +} // namespace lluz diff --git a/Analysis/src/TypeVar.cpp b/Analysis/src/TypeVar.cpp index ebaf5906..e28cf3ba 100644 --- a/Analysis/src/TypeVar.cpp +++ b/Analysis/src/TypeVar.cpp @@ -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 #include @@ -18,31 +18,19 @@ #include #include -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> magicFunctionFormat( TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate withPredicate); -static std::optional> magicFunctionGmatch( - TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate withPredicate); - -static std::optional> magicFunctionMatch( - TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate withPredicate); - -static std::optional> magicFunctionFind( - TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate withPredicate); - TypeId follow(TypeId t) { return follow(t, [](TypeId t) { @@ -66,7 +54,7 @@ TypeId follow(TypeId t, std::function mapper) { TypeId res = ltv->thunk(); if (get(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 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(get(ty))) + if (isPrim(ty, PrimitiveTypeVar::String) || get(get(follow(ty)))) return true; - if (auto utv = get(ty)) + if (auto utv = get(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(ty) || (FFlag::LuauUnknownAndNeverType && get(ty))) + if (get(ty)) return true; auto utv = get(ty); @@ -242,8 +228,6 @@ bool isOverloadedFunction(TypeId ty) std::optional getMetatable(TypeId type) { - type = follow(type); - if (const MetatableTypeVar* mtType = get(type)) return mtType->metatable; else if (const ClassTypeVar* classType = get(type)) @@ -251,7 +235,7 @@ std::optional getMetatable(TypeId type) else if (isString(type)) { auto ptv = get(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(ty)) - return true; - - if (auto ttv = get(ty)) - { - // TODO: recurse on table types CLI-39914 - (void)ttv; - return true; - } - - if (auto itv = get(ty)) - { - return std::any_of(begin(itv), end(itv), maybeGeneric); - } - - return isGeneric(ty); - } - ty = follow(ty); if (get(ty)) return true; @@ -399,7 +361,7 @@ bool maybeSingleton(TypeId ty) bool hasLength(TypeId ty, DenseHashSet& 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(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(ty)) return &ftv->level; - else if (auto ctv = get(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(*cls->parent); - LUAU_ASSERT(cls); + lluz_ASSERT(cls); } return false; } -const std::vector& 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& getTypes(const IntersectionTypeVar* itv) +UnionTypeVarIterator& UnionTypeVarIterator::operator++() { - return itv->parts; + advance(); + descend(); + return *this; } -const std::vector& 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(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(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 parseFormatString(TypeChecker& typechecker, const char* data, size_t size) { const char* options = "cdiouxXeEfgGqs"; @@ -1135,197 +1144,6 @@ std::optional> magicFunctionFormat( return WithPredicate{arena.addTypePack({typechecker.stringType})}; } -static std::vector parsePatternString(TypeChecker& typechecker, const char* data, size_t size) -{ - std::vector 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(); - - if (result.empty()) - result.push_back(typechecker.stringType); - - return result; -} - -static std::optional> magicFunctionGmatch( - TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate 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(); - - if (!pattern) - return std::nullopt; - - std::vector 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{arena.addTypePack({iteratorType})}; -} - -static std::optional> magicFunctionMatch( - TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate 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(); - - if (!pattern) - return std::nullopt; - - std::vector 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{returnList}; -} - -static std::optional> magicFunctionFind( - TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate 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(); - - 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(); - plain = p && p->value; - } - - std::vector 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{returnList}; -} - std::vector 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(*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 diff --git a/Analysis/src/TypedAllocator.cpp b/Analysis/src/TypedAllocator.cpp index c7f31822..8c7237fe 100644 --- a/Analysis/src/TypedAllocator.cpp +++ b/Analysis/src/TypedAllocator.cpp @@ -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 -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 diff --git a/Analysis/src/Unifiable.cpp b/Analysis/src/Unifiable.cpp index 63d8647d..980fef1a 100644 --- a/Analysis/src/Unifiable.cpp +++ b/Analysis/src/Unifiable.cpp @@ -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 diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index e099817f..52d1fa1f 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -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 -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 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 + void cycle(TID) + { + } + template + 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(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(ty); - LUAU_ASSERT(stv); + lluz_ASSERT(stv); if (get(stv)) return getSingletonTypes().stringType; else { // If this assert trips, it's likely we now have number singletons. - LUAU_ASSERT(get(stv)); + lluz_ASSERT(get(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 hasUnificationTooComplex(const ErrorVec& errors) } // Used for tagged union matching heuristic, returns first singleton type field -static std::optional> getTableMatchTag(TypeId type) +static std::optional> 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(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(superTy) || get(superTy) || get(superTy)) + if (get(superTy) || get(superTy)) return tryUnifyWithAny(subTy, superTy); if (get(subTy)) @@ -474,10 +482,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool return tryUnifyWithAny(superTy, subTy); } - if (log.get(subTy)) - return tryUnifyWithAny(superTy, subTy); - - if (log.get(subTy)) + if (get(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(superTy) && - (log.get(subTy) || log.get(subTy))) - { - tryUnifyScalarShape(subTy, superTy, /*reversed*/ false); - } - else if (FFlag::LuauScalarShapeSubtyping && log.get(subTy) && - (log.get(superTy) || log.get(superTy))) - { - tryUnifyScalarShape(subTy, superTy, /*reversed*/ true); - } // tryUnifyWithMetatable assumes its first argument is a MetatableTypeVar. The check is otherwise symmetrical. else if (log.getMutable(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(newTail)); + lluz_ASSERT(canGrow()); + lluz_ASSERT(log.getMutable(newTail)); level = log.getMutable(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(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(superTy); const PrimitiveTypeVar* subPrim = get(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(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(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(subTy); if (!superTable || !subTable) - ice("passed non-table types to unifyTables"); + ice(XorStr("passed non-table types to unifyTables")); std::vector missingProperties; std::vector extraProperties; @@ -1448,7 +1443,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection) { PendingType* pendingSub = log.queue(subTy); TableTypeVar* ttv = getMutable(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(superTy); !ttv || ttv->state != TableState::Free) - return reportError(TypeError{location, TypeMismatch{osuperTy, osubTy}}); - - auto fail = [&](std::optional 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(*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 seen) { ty = follow(ty); @@ -1689,7 +1630,7 @@ void Unifier::tryUnifyWithMetatable(TypeId subTy, TypeId superTy, bool reversed) { const MetatableTypeVar* superMetatable = get(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(superTy); if (!superClass) - ice("tryUnifyClass invoked with non-class TypeVar"); + ice(XorStr("tryUnifyClass invoked with non-class TypeVar")); if (const ClassTypeVar* subClass = get(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(subTy)) { @@ -1854,7 +1795,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever const VariadicTypePack* superVariadic = log.getMutable(superTp); if (!superVariadic) - ice("passed non-variadic pack to tryUnifyVariadics"); + ice(XorStr("passed non-variadic pack to tryUnifyVariadics")); if (const VariadicTypePack* subVariadic = get(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& queue, Unifier& state, DenseHas if (state.log.getMutable(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(ty)) @@ -1961,33 +1901,27 @@ static void tryUnifyWithAny(std::vector& queue, Unifier& state, DenseHas void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy) { - LUAU_ASSERT(get(anyTy) || get(anyTy) || get(anyTy) || get(anyTy)); + lluz_ASSERT(get(anyTy) || get(anyTy)); // These types are not visited in general loop below if (get(subTy) || get(subTy) || get(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(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}}); - } + const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{getSingletonTypes().anyType}}); + + const TypePackId anyTP = get(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}}); std::vector 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(anyTp)); + lluz_ASSERT(get(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 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(subTy); if (!subConstrained) - ice("tryUnifyWithConstrainedSubTypeVar received non-ConstrainedTypeVar subTy!"); + ice(XorStr("tryUnifyWithConstrainedSubTypeVar received non-ConstrainedTypeVar subTy!")); const std::vector& subTyParts = subConstrained->parts; @@ -2053,7 +1987,7 @@ void Unifier::tryUnifyWithConstrainedSuperTypeVar(TypeId subTy, TypeId superTy) { ConstrainedTypeVar* superC = log.getMutable(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(log.replace(tailPack, TypePack{})); @@ -2190,7 +2124,7 @@ void Unifier::occursCheck(TypeId needle, TypeId haystack) void Unifier::occursCheck(DenseHashSet& 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& seen, TypeId needle, TypeId hays return; if (!log.getMutable(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& seen, TypePackId needle, Typ return; if (!log.getMutable(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(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