From fb2f146123dcb423de6bc789d4f7d01f4981170c Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 9 Dec 2022 11:57:01 -0800 Subject: [PATCH] Sync to upstream/release/556 (#782) * The AST JSON encoder will now stringify infinity and NaN according to the JSON5 spec using the tokens `Infinity`, `-Infinity`, and `NaN`. * Improve autocompletion of table keys if the type of that key is a union of string singletons. --- Analysis/include/Luau/BuiltinDefinitions.h | 9 +- Analysis/include/Luau/Constraint.h | 7 + .../include/Luau/ConstraintGraphBuilder.h | 12 + Analysis/include/Luau/ConstraintSolver.h | 9 +- Analysis/include/Luau/Error.h | 6 +- Analysis/include/Luau/Linter.h | 1 + Analysis/include/Luau/Normalize.h | 22 +- Analysis/include/Luau/Scope.h | 1 - Analysis/include/Luau/ToString.h | 26 +- Analysis/include/Luau/TxnLog.h | 6 +- Analysis/include/Luau/TypeUtils.h | 4 +- Analysis/include/Luau/TypeVar.h | 7 +- Analysis/src/AstJsonEncoder.cpp | 27 ++- Analysis/src/AstQuery.cpp | 22 +- Analysis/src/Autocomplete.cpp | 47 +++- Analysis/src/BuiltinDefinitions.cpp | 13 + Analysis/src/ConstraintGraphBuilder.cpp | 44 ++-- Analysis/src/ConstraintSolver.cpp | 44 ++-- Analysis/src/Error.cpp | 1 + Analysis/src/Frontend.cpp | 29 +-- Analysis/src/Normalize.cpp | 195 ++++++--------- Analysis/src/ToString.cpp | 13 + Analysis/src/TxnLog.cpp | 1 + Analysis/src/TypeChecker2.cpp | 110 ++++++++- Analysis/src/TypeInfer.cpp | 4 +- Analysis/src/TypeUtils.cpp | 97 -------- Analysis/src/TypeVar.cpp | 7 - Analysis/src/Unifier.cpp | 11 +- Ast/include/Luau/Ast.h | 1 + Ast/include/Luau/Location.h | 136 ++--------- Ast/src/Location.cpp | 122 +++++++++- CLI/Ast.cpp | 1 + CodeGen/src/CodeGen.cpp | 100 ++++---- CodeGen/src/EmitCommonX64.cpp | 34 +-- CodeGen/src/EmitCommonX64.h | 22 +- CodeGen/src/EmitInstructionX64.cpp | 226 +++++++++--------- CodeGen/src/EmitInstructionX64.h | 64 ++--- tests/AstJsonEncoder.test.cpp | 7 + tests/Autocomplete.test.cpp | 65 +++++ tests/Fixture.cpp | 48 +++- tests/Fixture.h | 49 +--- tests/Frontend.test.cpp | 4 - tests/Normalize.test.cpp | 1 - tests/ToString.test.cpp | 12 +- tests/TypeInfer.aliases.test.cpp | 14 +- tests/TypeInfer.intersectionTypes.test.cpp | 2 +- tests/TypeInfer.negations.test.cpp | 3 +- tests/TypeInfer.operators.test.cpp | 86 +++++-- tests/TypeInfer.provisional.test.cpp | 30 +++ tests/TypeInfer.refinements.test.cpp | 82 +++---- tests/TypeInfer.tables.test.cpp | 69 ++---- tests/TypeInfer.tryUnify.test.cpp | 4 +- tools/faillist.txt | 58 +++-- 53 files changed, 1097 insertions(+), 918 deletions(-) diff --git a/Analysis/include/Luau/BuiltinDefinitions.h b/Analysis/include/Luau/BuiltinDefinitions.h index 4702995d4..16cccafe9 100644 --- a/Analysis/include/Luau/BuiltinDefinitions.h +++ b/Analysis/include/Luau/BuiltinDefinitions.h @@ -1,13 +1,18 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once -#include "Luau/Frontend.h" #include "Luau/Scope.h" -#include "Luau/TypeInfer.h" +#include "Luau/TypeVar.h" + +#include namespace Luau { +struct Frontend; +struct TypeChecker; +struct TypeArena; + void registerBuiltinTypes(Frontend& frontend); void registerBuiltinGlobals(TypeChecker& typeChecker); diff --git a/Analysis/include/Luau/Constraint.h b/Analysis/include/Luau/Constraint.h index e13613ed8..9eea9c288 100644 --- a/Analysis/include/Luau/Constraint.h +++ b/Analysis/include/Luau/Constraint.h @@ -3,6 +3,7 @@ #include "Luau/Ast.h" // Used for some of the enumerations #include "Luau/Def.h" +#include "Luau/DenseHash.h" #include "Luau/NotNull.h" #include "Luau/TypeVar.h" #include "Luau/Variant.h" @@ -67,6 +68,12 @@ struct BinaryConstraint TypeId leftType; TypeId rightType; TypeId resultType; + + // When we dispatch this constraint, we update the key at this map to record + // the overload that we selected. + AstExpr* expr; + DenseHashMap* astOriginalCallTypes; + DenseHashMap* astOverloadResolvedTypes; }; // iteratee is iterable diff --git a/Analysis/include/Luau/ConstraintGraphBuilder.h b/Analysis/include/Luau/ConstraintGraphBuilder.h index d81fe9189..c25a55371 100644 --- a/Analysis/include/Luau/ConstraintGraphBuilder.h +++ b/Analysis/include/Luau/ConstraintGraphBuilder.h @@ -76,15 +76,27 @@ struct ConstraintGraphBuilder // A mapping of AST node to TypeId. DenseHashMap astTypes{nullptr}; + // A mapping of AST node to TypePackId. DenseHashMap astTypePacks{nullptr}; + + // If the node was applied as a function, this is the unspecialized type of + // that expression. DenseHashMap astOriginalCallTypes{nullptr}; + + // If overload resolution was performed on this element, this is the + // overload that was selected. + DenseHashMap astOverloadResolvedTypes{nullptr}; + // Types resolved from type annotations. Analogous to astTypes. DenseHashMap astResolvedTypes{nullptr}; + // Type packs resolved from type annotations. Analogous to astTypePacks. DenseHashMap astResolvedTypePacks{nullptr}; + // Defining scopes for AST nodes. DenseHashMap astTypeAliasDefiningScopes{nullptr}; + NotNull dfg; ConnectiveArena connectiveArena; diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index e05f6f1f4..c02cd4d5c 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -2,12 +2,13 @@ #pragma once -#include "Luau/Error.h" -#include "Luau/Variant.h" #include "Luau/Constraint.h" -#include "Luau/TypeVar.h" -#include "Luau/ToString.h" +#include "Luau/Error.h" +#include "Luau/Module.h" #include "Luau/Normalize.h" +#include "Luau/ToString.h" +#include "Luau/TypeVar.h" +#include "Luau/Variant.h" #include diff --git a/Analysis/include/Luau/Error.h b/Analysis/include/Luau/Error.h index 893880464..739354f87 100644 --- a/Analysis/include/Luau/Error.h +++ b/Analysis/include/Luau/Error.h @@ -1,16 +1,16 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once -#include "Luau/FileResolver.h" #include "Luau/Location.h" #include "Luau/TypeVar.h" #include "Luau/Variant.h" -#include "Luau/TypeArena.h" namespace Luau { -struct TypeError; +struct FileResolver; +struct TypeArena; +struct TypeError; struct TypeMismatch { diff --git a/Analysis/include/Luau/Linter.h b/Analysis/include/Luau/Linter.h index 0e3d98803..6bbc3d660 100644 --- a/Analysis/include/Luau/Linter.h +++ b/Analysis/include/Luau/Linter.h @@ -4,6 +4,7 @@ #include "Luau/Location.h" #include +#include #include namespace Luau diff --git a/Analysis/include/Luau/Normalize.h b/Analysis/include/Luau/Normalize.h index d7e104ee5..392573155 100644 --- a/Analysis/include/Luau/Normalize.h +++ b/Analysis/include/Luau/Normalize.h @@ -42,6 +42,7 @@ class TypeIds void retain(const TypeIds& tys); void clear(); + TypeId front() const; iterator begin(); iterator end(); const_iterator begin() const; @@ -107,18 +108,7 @@ namespace Luau /** A normalized string type is either `string` (represented by `nullopt`) or a * union of string singletons. * - * When FFlagLuauNegatedStringSingletons is unset, the representation is as - * follows: - * - * * The `string` data type is represented by the option `singletons` having the - * value `std::nullopt`. - * * The type `never` is represented by `singletons` being populated with an - * empty map. - * * A union of string singletons is represented by a map populated by the names - * and TypeIds of the singletons contained therein. - * - * When FFlagLuauNegatedStringSingletons is set, the representation is as - * follows: + * The representation is as follows: * * * A union of string singletons is finite and includes the singletons named by * the `singletons` field. @@ -138,9 +128,7 @@ struct NormalizedStringType // eg string & ~"a" & ~"b" & ... bool isCofinite = false; - // TODO: This field cannot be nullopt when FFlagLuauNegatedStringSingletons - // is set. When clipping that flag, we can remove the wrapping optional. - std::optional> singletons; + std::map singletons; void resetToString(); void resetToNever(); @@ -161,8 +149,8 @@ struct NormalizedStringType static const NormalizedStringType never; - NormalizedStringType() = default; - NormalizedStringType(bool isCofinite, std::optional> singletons); + NormalizedStringType(); + NormalizedStringType(bool isCofinite, std::map singletons); }; bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr); diff --git a/Analysis/include/Luau/Scope.h b/Analysis/include/Luau/Scope.h index a26f506d6..851ed1a7d 100644 --- a/Analysis/include/Luau/Scope.h +++ b/Analysis/include/Luau/Scope.h @@ -1,7 +1,6 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once -#include "Luau/Constraint.h" #include "Luau/Location.h" #include "Luau/NotNull.h" #include "Luau/TypeVar.h" diff --git a/Analysis/include/Luau/ToString.h b/Analysis/include/Luau/ToString.h index 186cc9a5b..71c0e3595 100644 --- a/Analysis/include/Luau/ToString.h +++ b/Analysis/include/Luau/ToString.h @@ -2,13 +2,12 @@ #pragma once #include "Luau/Common.h" -#include "Luau/TypeVar.h" -#include "Luau/ConstraintGraphBuilder.h" -#include -#include #include +#include #include +#include +#include LUAU_FASTINT(LuauTableTypeMaximumStringifierLength) LUAU_FASTINT(LuauTypeMaximumStringifierLength) @@ -16,6 +15,22 @@ LUAU_FASTINT(LuauTypeMaximumStringifierLength) namespace Luau { +class AstExpr; + +struct Scope; + +struct TypeVar; +using TypeId = const TypeVar*; + +struct TypePackVar; +using TypePackId = const TypePackVar*; + +struct FunctionTypeVar; +struct Constraint; + +struct Position; +struct Location; + struct ToStringNameMap { std::unordered_map typeVars; @@ -125,4 +140,7 @@ std::string dump(const std::shared_ptr& scope, const char* name); std::string generateName(size_t n); +std::string toString(const Position& position); +std::string toString(const Location& location); + } // namespace Luau diff --git a/Analysis/include/Luau/TxnLog.h b/Analysis/include/Luau/TxnLog.h index b1a834126..82605bff7 100644 --- a/Analysis/include/Luau/TxnLog.h +++ b/Analysis/include/Luau/TxnLog.h @@ -1,12 +1,12 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once -#include -#include - #include "Luau/TypeVar.h" #include "Luau/TypePack.h" +#include +#include + namespace Luau { diff --git a/Analysis/include/Luau/TypeUtils.h b/Analysis/include/Luau/TypeUtils.h index aa9cdde2a..6ed70f468 100644 --- a/Analysis/include/Luau/TypeUtils.h +++ b/Analysis/include/Luau/TypeUtils.h @@ -4,6 +4,7 @@ #include "Luau/Error.h" #include "Luau/Location.h" #include "Luau/TypeVar.h" +#include "Luau/TypePack.h" #include #include @@ -12,6 +13,7 @@ namespace Luau { struct TxnLog; +struct TypeArena; using ScopePtr = std::shared_ptr; @@ -19,8 +21,6 @@ std::optional findMetatableEntry( NotNull singletonTypes, ErrorVec& errors, TypeId type, const std::string& entry, Location location); std::optional findTablePropertyRespectingMeta( NotNull singletonTypes, ErrorVec& errors, TypeId ty, const std::string& name, Location location); -std::optional getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull singletonTypes, - TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle); // Returns the minimum and maximum number of types the argument list can accept. std::pair> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics = false); diff --git a/Analysis/include/Luau/TypeVar.h b/Analysis/include/Luau/TypeVar.h index d355746a5..852a40547 100644 --- a/Analysis/include/Luau/TypeVar.h +++ b/Analysis/include/Luau/TypeVar.h @@ -264,12 +264,14 @@ using DcrMagicFunction = bool (*)(MagicFunctionCallContext); struct MagicRefinementContext { ScopePtr scope; + NotNull cgb; NotNull dfg; NotNull connectiveArena; + std::vector argumentConnectives; const class AstExprCall* callSite; }; -using DcrMagicRefinement = std::vector (*)(MagicRefinementContext); +using DcrMagicRefinement = std::vector (*)(const MagicRefinementContext&); struct FunctionTypeVar { @@ -666,9 +668,6 @@ struct SingletonTypes const TypePackId errorTypePack; }; -// Clip with FFlagLuauNoMoreGlobalSingletonTypes -SingletonTypes& DEPRECATED_getSingletonTypes(); - void persist(TypeId ty); void persist(TypePackId tp); diff --git a/Analysis/src/AstJsonEncoder.cpp b/Analysis/src/AstJsonEncoder.cpp index 8d589037f..57c8c90b4 100644 --- a/Analysis/src/AstJsonEncoder.cpp +++ b/Analysis/src/AstJsonEncoder.cpp @@ -6,6 +6,8 @@ #include "Luau/StringUtils.h" #include "Luau/Common.h" +#include + namespace Luau { @@ -103,9 +105,28 @@ struct AstJsonEncoder : public AstVisitor void write(double d) { - char b[32]; - snprintf(b, sizeof(b), "%.17g", d); - writeRaw(b); + switch (fpclassify(d)) + { + case FP_INFINITE: + if (d < 0) + writeRaw("-Infinity"); + else + writeRaw("Infinity"); + break; + + case FP_NAN: + writeRaw("NaN"); + break; + + case FP_NORMAL: + case FP_SUBNORMAL: + case FP_ZERO: + default: + char b[32]; + snprintf(b, sizeof(b), "%.17g", d); + writeRaw(b); + break; + } } void writeString(std::string_view sv) diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index 85d2320ae..e6e7f3d93 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -11,6 +11,8 @@ #include +LUAU_FASTFLAG(LuauCompleteTableKeysBetter); + namespace Luau { @@ -29,12 +31,24 @@ struct AutocompleteNodeFinder : public AstVisitor bool visit(AstExpr* expr) override { - if (expr->location.begin < pos && pos <= expr->location.end) + if (FFlag::LuauCompleteTableKeysBetter) { - ancestry.push_back(expr); - return true; + if (expr->location.begin <= pos && pos <= expr->location.end) + { + ancestry.push_back(expr); + return true; + } + return false; + } + else + { + if (expr->location.begin < pos && pos <= expr->location.end) + { + ancestry.push_back(expr); + return true; + } + return false; } - return false; } bool visit(AstStat* stat) override diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index 5374c6b17..83a6f0217 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -12,6 +12,8 @@ #include #include +LUAU_FASTFLAGVARIABLE(LuauCompleteTableKeysBetter, false); + static const std::unordered_set kStatementStartingKeywords = { "while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"}; @@ -966,13 +968,28 @@ T* extractStat(const std::vector& ancestry) if (!parent) return nullptr; - if (T* t = parent->as(); t && parent->is()) - return t; - AstNode* grandParent = ancestry.size() >= 3 ? ancestry.rbegin()[2] : nullptr; AstNode* greatGrandParent = ancestry.size() >= 4 ? ancestry.rbegin()[3] : nullptr; - if (!grandParent || !greatGrandParent) - return nullptr; + + if (FFlag::LuauCompleteTableKeysBetter) + { + if (!grandParent) + return nullptr; + + if (T* t = parent->as(); t && grandParent->is()) + return t; + + if (!greatGrandParent) + return nullptr; + } + else + { + if (T* t = parent->as(); t && parent->is()) + return t; + + if (!grandParent || !greatGrandParent) + return nullptr; + } if (T* t = greatGrandParent->as(); t && grandParent->is() && parent->is() && isIdentifier(node)) return t; @@ -1469,6 +1486,26 @@ static AutocompleteResult autocomplete(const SourceModule& sourceModule, const M { auto result = autocompleteProps(*module, &typeArena, singletonTypes, *it, PropIndexType::Key, ancestry); + if (FFlag::LuauCompleteTableKeysBetter) + { + if (auto nodeIt = module->astExpectedTypes.find(node->asExpr())) + autocompleteStringSingleton(*nodeIt, !node->is(), result); + + if (!key) + { + // If there is "no key," it may be that the user + // intends for the current token to be the key, but + // has yet to type the `=` sign. + // + // If the key type is a union of singleton strings, + // suggest those too. + if (auto ttv = get(follow(*it)); ttv && ttv->indexer) + { + autocompleteStringSingleton(ttv->indexer->indexType, false, result); + } + } + } + // Remove keys that are already completed for (const auto& item : exprTable->items) { diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index 39568674c..612812c56 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -7,6 +7,7 @@ #include "Luau/Common.h" #include "Luau/ToString.h" #include "Luau/ConstraintSolver.h" +#include "Luau/ConstraintGraphBuilder.h" #include "Luau/TypeInfer.h" #include "Luau/TypePack.h" #include "Luau/TypeVar.h" @@ -46,6 +47,8 @@ static bool dcrMagicFunctionSelect(MagicFunctionCallContext context); static bool dcrMagicFunctionRequire(MagicFunctionCallContext context); static bool dcrMagicFunctionPack(MagicFunctionCallContext context); +static std::vector dcrMagicRefinementAssert(const MagicRefinementContext& context); + TypeId makeUnion(TypeArena& arena, std::vector&& types) { return arena.addType(UnionTypeVar{std::move(types)}); @@ -478,6 +481,7 @@ void registerBuiltinGlobals(Frontend& frontend) } attachMagicFunction(getGlobalBinding(frontend, "assert"), magicFunctionAssert); + attachDcrMagicRefinement(getGlobalBinding(frontend, "assert"), dcrMagicRefinementAssert); attachMagicFunction(getGlobalBinding(frontend, "setmetatable"), magicFunctionSetMetaTable); attachMagicFunction(getGlobalBinding(frontend, "select"), magicFunctionSelect); attachDcrMagicFunction(getGlobalBinding(frontend, "select"), dcrMagicFunctionSelect); @@ -703,6 +707,15 @@ static std::optional> magicFunctionAssert( return WithPredicate{arena.addTypePack(TypePack{std::move(head), tail})}; } +static std::vector dcrMagicRefinementAssert(const MagicRefinementContext& ctx) +{ + if (ctx.argumentConnectives.empty()) + return {}; + + ctx.cgb->applyRefinements(ctx.scope, ctx.callSite->location, ctx.argumentConnectives[0]); + return {}; +} + static std::optional> magicFunctionPack( TypeChecker& typechecker, const ScopePtr& scope, const AstExprCall& expr, WithPredicate withPredicate) { diff --git a/Analysis/src/ConstraintGraphBuilder.cpp b/Analysis/src/ConstraintGraphBuilder.cpp index d41c77723..f0bd958cf 100644 --- a/Analysis/src/ConstraintGraphBuilder.cpp +++ b/Analysis/src/ConstraintGraphBuilder.cpp @@ -2,16 +2,12 @@ #include "Luau/ConstraintGraphBuilder.h" #include "Luau/Ast.h" -#include "Luau/Clone.h" #include "Luau/Common.h" #include "Luau/Constraint.h" #include "Luau/DcrLogger.h" #include "Luau/ModuleResolver.h" #include "Luau/RecursionCounter.h" #include "Luau/Scope.h" -#include "Luau/Substitution.h" -#include "Luau/ToString.h" -#include "Luau/TxnLog.h" #include "Luau/TypeUtils.h" #include "Luau/TypeVar.h" @@ -1068,16 +1064,9 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa else expectedArgs = extendTypePack(*arena, singletonTypes, expectedArgPack, exprArgs.size() - 1); - std::vector connectives; - if (auto ftv = get(follow(fnType)); ftv && ftv->dcrMagicRefinement) - { - MagicRefinementContext ctx{globalScope, dfg, NotNull{&connectiveArena}, call}; - connectives = ftv->dcrMagicRefinement(ctx); - } - - std::vector args; std::optional argTail; + std::vector argumentConnectives; Checkpoint argCheckpoint = checkpoint(this); @@ -1101,7 +1090,11 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa args.push_back(arena->freshType(scope.get())); } else if (i < exprArgs.size() - 1 || !(arg->is() || arg->is())) - args.push_back(check(scope, arg, expectedType).ty); + { + auto [ty, connective] = check(scope, arg, expectedType); + args.push_back(ty); + argumentConnectives.push_back(connective); + } else argTail = checkPack(scope, arg, {}).tp; // FIXME? not sure about expectedTypes here } @@ -1114,6 +1107,13 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa constraint->dependencies.push_back(extractArgsConstraint); }); + std::vector returnConnectives; + if (auto ftv = get(follow(fnType)); ftv && ftv->dcrMagicRefinement) + { + MagicRefinementContext ctx{scope, NotNull{this}, dfg, NotNull{&connectiveArena}, std::move(argumentConnectives), call}; + returnConnectives = ftv->dcrMagicRefinement(ctx); + } + if (matchSetmetatable(*call)) { TypePack argTailPack; @@ -1133,7 +1133,7 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa if (AstExprLocal* targetLocal = targetExpr->as()) scope->bindings[targetLocal->local].typeId = resultTy; - return InferencePack{arena->addTypePack({resultTy}), std::move(connectives)}; + return InferencePack{arena->addTypePack({resultTy}), std::move(returnConnectives)}; } else { @@ -1172,7 +1172,7 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa fcc->dependencies.emplace_back(constraint.get()); }); - return InferencePack{rets, std::move(connectives)}; + return InferencePack{rets, std::move(returnConnectives)}; } } @@ -1468,16 +1468,22 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* bi auto [leftType, rightType, connective] = checkBinary(scope, binary, expectedType); TypeId resultType = arena->addType(BlockedTypeVar{}); - addConstraint(scope, binary->location, BinaryConstraint{binary->op, leftType, rightType, resultType}); + addConstraint(scope, binary->location, BinaryConstraint{binary->op, leftType, rightType, resultType, binary, &astOriginalCallTypes, &astOverloadResolvedTypes}); return Inference{resultType, std::move(connective)}; } Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional expectedType) { - check(scope, ifElse->condition); + ScopePtr condScope = childScope(ifElse->condition, scope); + auto [_, connective] = check(scope, ifElse->condition); + + ScopePtr thenScope = childScope(ifElse->trueExpr, scope); + applyRefinements(thenScope, ifElse->trueExpr->location, connective); + TypeId thenType = check(thenScope, ifElse->trueExpr, expectedType).ty; - TypeId thenType = check(scope, ifElse->trueExpr, expectedType).ty; - TypeId elseType = check(scope, ifElse->falseExpr, expectedType).ty; + ScopePtr elseScope = childScope(ifElse->falseExpr, scope); + applyRefinements(elseScope, ifElse->falseExpr->location, connectiveArena.negation(connective)); + TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty; if (ifElse->hasElse) { diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index d59ea70ae..d73c14a60 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -15,7 +15,6 @@ #include "Luau/TypeVar.h" #include "Luau/Unifier.h" #include "Luau/VisitTypeVar.h" -#include "Luau/TypeUtils.h" LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false); LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false); @@ -635,9 +634,17 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNullscope}; + std::optional instantiatedMm = instantiation.substitute(*mm); + if (!instantiatedMm) + { + reportError(CodeTooComplex{}, constraint->location); + return true; + } + // TODO: Is a table with __call legal here? // TODO: Overloads - if (const FunctionTypeVar* ftv = get(follow(*mm))) + if (const FunctionTypeVar* ftv = get(follow(*instantiatedMm))) { TypePackId inferredArgs; // For >= and > we invoke __lt and __le respectively with @@ -673,6 +680,9 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNullty.emplace(mmResult); unblock(resultType); + + (*c.astOriginalCallTypes)[c.expr] = *mm; + (*c.astOverloadResolvedTypes)[c.expr] = *instantiatedMm; return true; } } @@ -743,19 +753,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNulladdType(IntersectionTypeVar{{singletonTypes->falsyType, leftType}}); - // TODO: normaliztion here should be replaced by a more limited 'simplification' - const NormalizedType* normalized = normalizer->normalize(arena->addType(UnionTypeVar{{leftFilteredTy, rightType}})); - - if (!normalized) - { - reportError(CodeTooComplex{}, constraint->location); - asMutable(resultType)->ty.emplace(errorRecoveryType()); - } - else - { - asMutable(resultType)->ty.emplace(normalizer->typeFromNormal(*normalized)); - } - + asMutable(resultType)->ty.emplace(arena->addType(UnionTypeVar{{leftFilteredTy, rightType}})); unblock(resultType); return true; } @@ -763,21 +761,9 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNulladdType(IntersectionTypeVar{{singletonTypes->truthyType, leftType}}); - - // TODO: normaliztion here should be replaced by a more limited 'simplification' - const NormalizedType* normalized = normalizer->normalize(arena->addType(UnionTypeVar{{rightFilteredTy, rightType}})); - - if (!normalized) - { - reportError(CodeTooComplex{}, constraint->location); - asMutable(resultType)->ty.emplace(errorRecoveryType()); - } - else - { - asMutable(resultType)->ty.emplace(normalizer->typeFromNormal(*normalized)); - } + TypeId leftFilteredTy = arena->addType(IntersectionTypeVar{{singletonTypes->truthyType, leftType}}); + asMutable(resultType)->ty.emplace(arena->addType(UnionTypeVar{{leftFilteredTy, rightType}})); unblock(resultType); return true; } diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index aefaa2c71..748cf20fa 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -3,6 +3,7 @@ #include "Luau/Clone.h" #include "Luau/Common.h" +#include "Luau/FileResolver.h" #include "Luau/StringUtils.h" #include "Luau/ToString.h" diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 356ced0b3..e21e42e14 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -21,16 +21,15 @@ #include #include #include +#include LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(LuauInferInNoCheckMode) -LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false) LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100) LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false) LUAU_FASTFLAG(DebugLuauLogSolverToJson); -LUAU_FASTFLAGVARIABLE(LuauFixMarkDirtyReverseDeps, false) namespace Luau { @@ -409,7 +408,7 @@ double getTimestamp() } // namespace Frontend::Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options) - : singletonTypes(NotNull{FFlag::LuauNoMoreGlobalSingletonTypes ? &singletonTypes_ : &DEPRECATED_getSingletonTypes()}) + : singletonTypes(NotNull{&singletonTypes_}) , fileResolver(fileResolver) , moduleResolver(this) , moduleResolverForAutocomplete(this) @@ -819,26 +818,13 @@ void Frontend::markDirty(const ModuleName& name, std::vector* marked sourceNode.dirtyModule = true; sourceNode.dirtyModuleForAutocomplete = true; - if (FFlag::LuauFixMarkDirtyReverseDeps) - { - if (0 == reverseDeps.count(next)) - continue; - - sourceModules.erase(next); - - const std::vector& dependents = reverseDeps[next]; - queue.insert(queue.end(), dependents.begin(), dependents.end()); - } - else - { - if (0 == reverseDeps.count(name)) - continue; + if (0 == reverseDeps.count(next)) + continue; - sourceModules.erase(name); + sourceModules.erase(next); - const std::vector& dependents = reverseDeps[name]; - queue.insert(queue.end(), dependents.begin(), dependents.end()); - } + const std::vector& dependents = reverseDeps[next]; + queue.insert(queue.end(), dependents.begin(), dependents.end()); } } @@ -919,6 +905,7 @@ ModulePtr Frontend::check( result->astTypes = std::move(cgb.astTypes); result->astTypePacks = std::move(cgb.astTypePacks); result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes); + result->astOverloadResolvedTypes = std::move(cgb.astOverloadResolvedTypes); result->astResolvedTypes = std::move(cgb.astResolvedTypes); result->astResolvedTypePacks = std::move(cgb.astResolvedTypePacks); result->type = sourceModule.type; diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index fa3503fdd..09f0595d6 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -19,12 +19,11 @@ LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200); LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000); LUAU_FASTFLAGVARIABLE(LuauNormalizeCombineTableFix, false); LUAU_FASTFLAGVARIABLE(LuauTypeNormalization2, false); -LUAU_FASTFLAGVARIABLE(LuauNegatedStringSingletons, false); LUAU_FASTFLAGVARIABLE(LuauNegatedFunctionTypes, false); LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAG(LuauOverloadedFunctionSubtypingPerf); -LUAU_FASTFLAG(LuauUninhabitedSubAnything) +LUAU_FASTFLAG(LuauUninhabitedSubAnything2) namespace Luau { @@ -46,6 +45,11 @@ void TypeIds::clear() hash = 0; } +TypeId TypeIds::front() const +{ + return order.at(0); +} + TypeIds::iterator TypeIds::begin() { return order.begin(); @@ -111,94 +115,68 @@ bool TypeIds::operator==(const TypeIds& there) const return hash == there.hash && types == there.types; } -NormalizedStringType::NormalizedStringType(bool isCofinite, std::optional> singletons) +NormalizedStringType::NormalizedStringType() +{} + +NormalizedStringType::NormalizedStringType(bool isCofinite, std::map singletons) : isCofinite(isCofinite) , singletons(std::move(singletons)) { - if (!FFlag::LuauNegatedStringSingletons) - LUAU_ASSERT(!isCofinite); } void NormalizedStringType::resetToString() { - if (FFlag::LuauNegatedStringSingletons) - { - isCofinite = true; - singletons->clear(); - } - else - singletons.reset(); + isCofinite = true; + singletons.clear(); } void NormalizedStringType::resetToNever() { - if (FFlag::LuauNegatedStringSingletons) - { - isCofinite = false; - singletons.emplace(); - } - else - { - if (singletons) - singletons->clear(); - else - singletons.emplace(); - } + isCofinite = false; + singletons.clear(); } bool NormalizedStringType::isNever() const { - if (FFlag::LuauNegatedStringSingletons) - return !isCofinite && singletons->empty(); - else - return singletons && singletons->empty(); + return !isCofinite && singletons.empty(); } bool NormalizedStringType::isString() const { - if (FFlag::LuauNegatedStringSingletons) - return isCofinite && singletons->empty(); - else - return !singletons; + return isCofinite && singletons.empty(); } bool NormalizedStringType::isUnion() const { - if (FFlag::LuauNegatedStringSingletons) - return !isCofinite; - else - return singletons.has_value(); + return !isCofinite; } bool NormalizedStringType::isIntersection() const { - if (FFlag::LuauNegatedStringSingletons) - return isCofinite; - else - return false; + return isCofinite; } bool NormalizedStringType::includes(const std::string& str) const { if (isString()) return true; - else if (isUnion() && singletons->count(str)) + else if (isUnion() && singletons.count(str)) return true; - else if (isIntersection() && !singletons->count(str)) + else if (isIntersection() && !singletons.count(str)) return true; else return false; } -const NormalizedStringType NormalizedStringType::never{false, {{}}}; +const NormalizedStringType NormalizedStringType::never; bool isSubtype(const NormalizedStringType& subStr, const NormalizedStringType& superStr) { if (subStr.isUnion() && superStr.isUnion()) { - for (auto [name, ty] : *subStr.singletons) + for (auto [name, ty] : subStr.singletons) { - if (!superStr.singletons->count(name)) + if (!superStr.singletons.count(name)) return false; } } @@ -251,17 +229,21 @@ static bool isShallowInhabited(const NormalizedType& norm) bool isInhabited_DEPRECATED(const NormalizedType& norm) { - LUAU_ASSERT(!FFlag::LuauUninhabitedSubAnything); + LUAU_ASSERT(!FFlag::LuauUninhabitedSubAnything2); return isShallowInhabited(norm); } bool Normalizer::isInhabited(const NormalizedType* norm, std::unordered_set seen) { + // If normalization failed, the type is complex, and so is more likely than not to be inhabited. + if (!norm) + return true; + if (!get(norm->tops) || !get(norm->booleans) || !get(norm->errors) || !get(norm->nils) || !get(norm->numbers) || !get(norm->threads) || !norm->classes.empty() || !norm->strings.isNever() || !norm->functions.isNever()) return true; - + for (const auto& [_, intersect] : norm->tyvars) { if (isInhabited(intersect.get(), seen)) @@ -372,7 +354,7 @@ static bool isNormalizedString(const NormalizedStringType& ty) if (ty.isString()) return true; - for (auto& [str, ty] : *ty.singletons) + for (auto& [str, ty] : ty.singletons) { if (const SingletonTypeVar* stv = get(ty)) { @@ -682,56 +664,46 @@ void Normalizer::unionClasses(TypeIds& heres, const TypeIds& theres) void Normalizer::unionStrings(NormalizedStringType& here, const NormalizedStringType& there) { - if (FFlag::LuauNegatedStringSingletons) + if (there.isString()) + here.resetToString(); + else if (here.isUnion() && there.isUnion()) + here.singletons.insert(there.singletons.begin(), there.singletons.end()); + else if (here.isUnion() && there.isIntersection()) { - if (there.isString()) - here.resetToString(); - else if (here.isUnion() && there.isUnion()) - here.singletons->insert(there.singletons->begin(), there.singletons->end()); - else if (here.isUnion() && there.isIntersection()) - { - here.isCofinite = true; - for (const auto& pair : *there.singletons) - { - auto it = here.singletons->find(pair.first); - if (it != end(*here.singletons)) - here.singletons->erase(it); - else - here.singletons->insert(pair); - } - } - else if (here.isIntersection() && there.isUnion()) + here.isCofinite = true; + for (const auto& pair : there.singletons) { - for (const auto& [name, ty] : *there.singletons) - here.singletons->erase(name); + auto it = here.singletons.find(pair.first); + if (it != end(here.singletons)) + here.singletons.erase(it); + else + here.singletons.insert(pair); } - else if (here.isIntersection() && there.isIntersection()) - { - auto iter = begin(*here.singletons); - auto endIter = end(*here.singletons); + } + else if (here.isIntersection() && there.isUnion()) + { + for (const auto& [name, ty] : there.singletons) + here.singletons.erase(name); + } + else if (here.isIntersection() && there.isIntersection()) + { + auto iter = begin(here.singletons); + auto endIter = end(here.singletons); - while (iter != endIter) + while (iter != endIter) + { + if (!there.singletons.count(iter->first)) { - if (!there.singletons->count(iter->first)) - { - auto eraseIt = iter; - ++iter; - here.singletons->erase(eraseIt); - } - else - ++iter; + auto eraseIt = iter; + ++iter; + here.singletons.erase(eraseIt); } + else + ++iter; } - else - LUAU_ASSERT(!"Unreachable"); } else - { - if (there.isString()) - here.resetToString(); - else if (here.isUnion()) - here.singletons->insert(there.singletons->begin(), there.singletons->end()); - } + LUAU_ASSERT(!"Unreachable"); } std::optional Normalizer::unionOfTypePacks(TypePackId here, TypePackId there) @@ -1116,22 +1088,14 @@ bool Normalizer::unionNormalWithTy(NormalizedType& here, TypeId there, int ignor here.booleans = unionOfBools(here.booleans, there); else if (const StringSingleton* sstv = get(stv)) { - if (FFlag::LuauNegatedStringSingletons) + if (here.strings.isCofinite) { - if (here.strings.isCofinite) - { - auto it = here.strings.singletons->find(sstv->value); - if (it != here.strings.singletons->end()) - here.strings.singletons->erase(it); - } - else - here.strings.singletons->insert({sstv->value, there}); + auto it = here.strings.singletons.find(sstv->value); + if (it != here.strings.singletons.end()) + here.strings.singletons.erase(it); } else - { - if (here.strings.isUnion()) - here.strings.singletons->insert({sstv->value, there}); - } + here.strings.singletons.insert({sstv->value, there}); } else LUAU_ASSERT(!"Unreachable"); @@ -1278,7 +1242,6 @@ void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty) here.threads = singletonTypes->neverType; break; case PrimitiveTypeVar::Function: - LUAU_ASSERT(FFlag::LuauNegatedStringSingletons); here.functions.resetToNever(); break; } @@ -1286,20 +1249,18 @@ void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty) void Normalizer::subtractSingleton(NormalizedType& here, TypeId ty) { - LUAU_ASSERT(FFlag::LuauNegatedStringSingletons); - const SingletonTypeVar* stv = get(ty); LUAU_ASSERT(stv); if (const StringSingleton* ss = get(stv)) { if (here.strings.isCofinite) - here.strings.singletons->insert({ss->value, ty}); + here.strings.singletons.insert({ss->value, ty}); else { - auto it = here.strings.singletons->find(ss->value); - if (it != here.strings.singletons->end()) - here.strings.singletons->erase(it); + auto it = here.strings.singletons.find(ss->value); + if (it != here.strings.singletons.end()) + here.strings.singletons.erase(it); } } else if (const BooleanSingleton* bs = get(stv)) @@ -1417,12 +1378,12 @@ void Normalizer::intersectStrings(NormalizedStringType& here, const NormalizedSt if (here.isString()) here.resetToNever(); - for (auto it = here.singletons->begin(); it != here.singletons->end();) + for (auto it = here.singletons.begin(); it != here.singletons.end();) { - if (there.singletons->count(it->first)) + if (there.singletons.count(it->first)) it++; else - it = here.singletons->erase(it); + it = here.singletons.erase(it); } } @@ -2096,12 +2057,12 @@ bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there) else if (const StringSingleton* sstv = get(stv)) { if (strings.includes(sstv->value)) - here.strings.singletons->insert({sstv->value, there}); + here.strings.singletons.insert({sstv->value, there}); } else LUAU_ASSERT(!"Unreachable"); } - else if (const NegationTypeVar* ntv = get(there); FFlag::LuauNegatedStringSingletons && ntv) + else if (const NegationTypeVar* ntv = get(there)) { TypeId t = follow(ntv->ty); if (const PrimitiveTypeVar* ptv = get(t)) @@ -2171,14 +2132,14 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm) result.push_back(singletonTypes->stringType); else if (norm.strings.isUnion()) { - for (auto& [_, ty] : *norm.strings.singletons) + for (auto& [_, ty] : norm.strings.singletons) result.push_back(ty); } - else if (FFlag::LuauNegatedStringSingletons && norm.strings.isIntersection()) + else if (norm.strings.isIntersection()) { std::vector parts; parts.push_back(singletonTypes->stringType); - for (const auto& [name, ty] : *norm.strings.singletons) + for (const auto& [name, ty] : norm.strings.singletons) parts.push_back(arena->addType(NegationTypeVar{ty})); result.push_back(arena->addType(IntersectionTypeVar{std::move(parts)})); diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index ddcc15a81..01144a3d0 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -1,6 +1,8 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/ToString.h" +#include "Luau/Constraint.h" +#include "Luau/Location.h" #include "Luau/Scope.h" #include "Luau/TypeInfer.h" #include "Luau/TypePack.h" @@ -1582,4 +1584,15 @@ std::optional getFunctionNameAsString(const AstExpr& expr) return s; } + +std::string toString(const Position& position) +{ + return "{ line = " + std::to_string(position.line) + ", col = " + std::to_string(position.column) + " }"; +} + +std::string toString(const Location& location) +{ + return "Location { " + toString(location.begin) + ", " + toString(location.end) + " }"; +} + } // namespace Luau diff --git a/Analysis/src/TxnLog.cpp b/Analysis/src/TxnLog.cpp index 1a73b049f..18596a638 100644 --- a/Analysis/src/TxnLog.cpp +++ b/Analysis/src/TxnLog.cpp @@ -2,6 +2,7 @@ #include "Luau/TxnLog.h" #include "Luau/ToString.h" +#include "Luau/TypeArena.h" #include "Luau/TypePack.h" #include diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 84c0ca3b0..8c44f90a5 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -90,6 +90,9 @@ struct TypeChecker2 std::vector> stack; + UnifierSharedState sharedState{&ice}; + Normalizer normalizer{&module->internalTypes, singletonTypes, NotNull{&sharedState}}; + TypeChecker2(NotNull singletonTypes, DcrLogger* logger, const SourceModule* sourceModule, Module* module) : singletonTypes(singletonTypes) , logger(logger) @@ -298,8 +301,6 @@ struct TypeChecker2 TypeArena* arena = &module->internalTypes; TypePackId actualRetType = reconstructPack(ret->list, *arena); - UnifierSharedState sharedState{&ice}; - Normalizer normalizer{arena, singletonTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, Mode::Strict, stack.back(), ret->location, Covariant}; u.tryUnify(actualRetType, expectedRetType); @@ -921,7 +922,12 @@ struct TypeChecker2 void visit(AstExprIndexName* indexName) { TypeId leftType = lookupType(indexName->expr); - getIndexTypeFromType(module->getModuleScope(), leftType, indexName->index.value, indexName->location, /* addErrors */ true); + + const NormalizedType* norm = normalizer.normalize(leftType); + if (!norm) + reportError(NormalizationTooComplex{}, indexName->indexLocation); + + checkIndexTypeFromType(leftType, *norm, indexName->index.value, indexName->location); } void visit(AstExprIndexExpr* indexExpr) @@ -1109,11 +1115,18 @@ struct TypeChecker2 if (std::optional leftMm = findMetatableEntry(singletonTypes, module->errors, leftType, it->second, expr->left->location)) mm = leftMm; else if (std::optional rightMm = findMetatableEntry(singletonTypes, module->errors, rightType, it->second, expr->right->location)) + { mm = rightMm; + std::swap(leftType, rightType); + } if (mm) { - if (const FunctionTypeVar* ftv = get(follow(*mm))) + TypeId instantiatedMm = module->astOverloadResolvedTypes[expr]; + if (!instantiatedMm) + reportError(CodeTooComplex{}, expr->location); + + else if (const FunctionTypeVar* ftv = get(follow(instantiatedMm))) { TypePackId expectedArgs; // For >= and > we invoke __lt and __le respectively with @@ -1545,9 +1558,7 @@ struct TypeChecker2 template bool isSubtype(TID subTy, TID superTy, NotNull scope) { - UnifierSharedState sharedState{&ice}; TypeArena arena; - Normalizer normalizer{&arena, singletonTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, Mode::Strict, scope, Location{}, Covariant}; u.useScopes = true; @@ -1559,8 +1570,6 @@ struct TypeChecker2 template ErrorVec tryUnify(NotNull scope, const Location& location, TID subTy, TID superTy) { - UnifierSharedState sharedState{&ice}; - Normalizer normalizer{&module->internalTypes, singletonTypes, NotNull{&sharedState}}; Unifier u{NotNull{&normalizer}, Mode::Strict, scope, location, Covariant}; u.useScopes = true; u.tryUnify(subTy, superTy); @@ -1587,9 +1596,90 @@ struct TypeChecker2 reportError(std::move(e)); } - std::optional getIndexTypeFromType(const ScopePtr& scope, TypeId type, const std::string& prop, const Location& location, bool addErrors) + void checkIndexTypeFromType(TypeId denormalizedTy, const NormalizedType& norm, const std::string& prop, const Location& location) { - return Luau::getIndexTypeFromType(scope, module->errors, &module->internalTypes, singletonTypes, type, prop, location, addErrors, ice); + bool foundOneProp = false; + std::vector typesMissingTheProp; + + auto fetch = [&](TypeId ty) { + if (!normalizer.isInhabited(ty)) + return; + + bool found = hasIndexTypeFromType(ty, prop, location); + foundOneProp |= found; + if (!found) + typesMissingTheProp.push_back(ty); + }; + + fetch(norm.tops); + fetch(norm.booleans); + for (TypeId ty : norm.classes) + fetch(ty); + fetch(norm.errors); + fetch(norm.nils); + fetch(norm.numbers); + if (!norm.strings.isNever()) + fetch(singletonTypes->stringType); + fetch(norm.threads); + for (TypeId ty : norm.tables) + fetch(ty); + if (norm.functions.isTop) + fetch(singletonTypes->functionType); + else if (!norm.functions.isNever()) + { + if (norm.functions.parts->size() == 1) + fetch(norm.functions.parts->front()); + else + { + std::vector parts; + parts.insert(parts.end(), norm.functions.parts->begin(), norm.functions.parts->end()); + fetch(module->internalTypes.addType(IntersectionTypeVar{std::move(parts)})); + } + } + for (const auto& [tyvar, intersect] : norm.tyvars) + { + if (get(intersect->tops)) + { + TypeId ty = normalizer.typeFromNormal(*intersect); + fetch(module->internalTypes.addType(IntersectionTypeVar{{tyvar, ty}})); + } + else + fetch(tyvar); + } + + if (!typesMissingTheProp.empty()) + { + if (foundOneProp) + reportError(TypeError{location, MissingUnionProperty{denormalizedTy, typesMissingTheProp, prop}}); + else + reportError(TypeError{location, UnknownProperty{denormalizedTy, prop}}); + } + } + + bool hasIndexTypeFromType(TypeId ty, const std::string& prop, const Location& location) + { + if (get(ty) || get(ty) || get(ty)) + return true; + + if (isString(ty)) + { + std::optional mtIndex = Luau::findMetatableEntry(singletonTypes, module->errors, singletonTypes->stringType, "__index", location); + LUAU_ASSERT(mtIndex); + ty = *mtIndex; + } + + if (getTableType(ty)) + return bool(findTablePropertyRespectingMeta(singletonTypes, module->errors, ty, prop, location)); + else if (const ClassTypeVar* cls = get(ty)) + return bool(lookupClassProp(cls, prop)); + else if (const UnionTypeVar* utv = get(ty)) + ice.ice("getIndexTypeFromTypeHelper cannot take a UnionTypeVar"); + else if (const IntersectionTypeVar* itv = get(ty)) + return std::any_of(begin(itv), end(itv), [&](TypeId part) { + return hasIndexTypeFromType(part, prop, location); + }); + else + return false; } }; diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 9f64a6010..aa738ad96 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -52,7 +52,7 @@ LUAU_FASTFLAGVARIABLE(LuauIntersectionTestForEquality, false) LUAU_FASTFLAGVARIABLE(LuauImplicitElseRefinement, false) LUAU_FASTFLAGVARIABLE(LuauAllowIndexClassParameters, false) LUAU_FASTFLAGVARIABLE(LuauDeclareClassPrototype, false) -LUAU_FASTFLAG(LuauUninhabitedSubAnything) +LUAU_FASTFLAG(LuauUninhabitedSubAnything2) LUAU_FASTFLAGVARIABLE(LuauCallableClasses, false) namespace Luau @@ -2691,7 +2691,7 @@ static std::optional areEqComparable(NotNull arena, NotNullisInhabited(n); else return isInhabited_DEPRECATED(*n); diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index 876c45a77..7478ac22c 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -88,103 +88,6 @@ std::optional findTablePropertyRespectingMeta( return std::nullopt; } -std::optional getIndexTypeFromType(const ScopePtr& scope, ErrorVec& errors, TypeArena* arena, NotNull singletonTypes, - TypeId type, const std::string& prop, const Location& location, bool addErrors, InternalErrorReporter& handle) -{ - type = follow(type); - - if (get(type) || get(type) || get(type)) - return type; - - if (auto f = get(type)) - *asMutable(type) = TableTypeVar{TableState::Free, f->level}; - - if (isString(type)) - { - std::optional mtIndex = Luau::findMetatableEntry(singletonTypes, errors, singletonTypes->stringType, "__index", location); - LUAU_ASSERT(mtIndex); - type = *mtIndex; - } - - if (getTableType(type)) - { - return findTablePropertyRespectingMeta(singletonTypes, errors, type, prop, location); - } - else if (const ClassTypeVar* cls = get(type)) - { - if (const Property* p = lookupClassProp(cls, prop)) - return p->type; - } - else if (const UnionTypeVar* utv = get(type)) - { - std::vector goodOptions; - std::vector badOptions; - - for (TypeId t : utv) - { - if (get(follow(t))) - return t; - - if (std::optional ty = - getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle)) - goodOptions.push_back(*ty); - else - badOptions.push_back(t); - } - - if (!badOptions.empty()) - { - if (addErrors) - { - if (goodOptions.empty()) - errors.push_back(TypeError{location, UnknownProperty{type, prop}}); - else - errors.push_back(TypeError{location, MissingUnionProperty{type, badOptions, prop}}); - } - return std::nullopt; - } - - goodOptions = reduceUnion(goodOptions); - - if (goodOptions.empty()) - return singletonTypes->neverType; - - if (goodOptions.size() == 1) - return goodOptions[0]; - - return arena->addType(UnionTypeVar{std::move(goodOptions)}); - } - else if (const IntersectionTypeVar* itv = get(type)) - { - std::vector parts; - - for (TypeId t : itv->parts) - { - if (std::optional ty = - getIndexTypeFromType(scope, errors, arena, singletonTypes, t, prop, location, /* addErrors= */ false, handle)) - parts.push_back(*ty); - } - - // If no parts of the intersection had the property we looked up for, it never existed at all. - if (parts.empty()) - { - if (addErrors) - errors.push_back(TypeError{location, UnknownProperty{type, prop}}); - return std::nullopt; - } - - if (parts.size() == 1) - return parts[0]; - - return arena->addType(IntersectionTypeVar{std::move(parts)}); - } - - if (addErrors) - errors.push_back(TypeError{location, UnknownProperty{type, prop}}); - - return std::nullopt; -} - std::pair> getParameterExtents(const TxnLog* log, TypePackId tp, bool includeHiddenVariadics) { size_t minCount = 0; diff --git a/Analysis/src/TypeVar.cpp b/Analysis/src/TypeVar.cpp index 6771d89b0..159e77125 100644 --- a/Analysis/src/TypeVar.cpp +++ b/Analysis/src/TypeVar.cpp @@ -26,7 +26,6 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false) -LUAU_FASTFLAGVARIABLE(LuauNoMoreGlobalSingletonTypes, false) LUAU_FASTFLAGVARIABLE(LuauNewLibraryTypeNames, false) LUAU_FASTFLAG(LuauInstantiateInSubtyping) @@ -890,12 +889,6 @@ TypePackId SingletonTypes::errorRecoveryTypePack(TypePackId guess) return guess; } -SingletonTypes& DEPRECATED_getSingletonTypes() -{ - static SingletonTypes singletonTypes; - return singletonTypes; -} - void persist(TypeId ty) { std::deque queue{ty}; diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 5ff405e62..428820054 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -5,12 +5,13 @@ #include "Luau/Instantiation.h" #include "Luau/RecursionCounter.h" #include "Luau/Scope.h" +#include "Luau/StringUtils.h" +#include "Luau/TimeTrace.h" +#include "Luau/ToString.h" #include "Luau/TypePack.h" #include "Luau/TypeUtils.h" -#include "Luau/TimeTrace.h" #include "Luau/TypeVar.h" #include "Luau/VisitTypeVar.h" -#include "Luau/ToString.h" #include @@ -23,7 +24,7 @@ LUAU_FASTFLAGVARIABLE(LuauScalarShapeSubtyping, false) LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false) LUAU_FASTFLAGVARIABLE(LuauOverloadedFunctionSubtypingPerf, false); LUAU_FASTFLAGVARIABLE(LuauScalarShapeUnifyToMtOwner2, false) -LUAU_FASTFLAGVARIABLE(LuauUninhabitedSubAnything, false) +LUAU_FASTFLAGVARIABLE(LuauUninhabitedSubAnything2, false) LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution) LUAU_FASTFLAG(LuauTxnLogTypePackIterator) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) @@ -588,7 +589,7 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool else if (log.get(subTy)) tryUnifyNegationWithType(subTy, superTy); - else if (FFlag::LuauUninhabitedSubAnything && !normalizer->isInhabited(subTy)) + else if (FFlag::LuauUninhabitedSubAnything2 && !normalizer->isInhabited(subTy)) {} else @@ -1980,7 +1981,7 @@ void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed) TypeId osubTy = subTy; TypeId osuperTy = superTy; - if (FFlag::LuauUninhabitedSubAnything && !normalizer->isInhabited(subTy)) + if (FFlag::LuauUninhabitedSubAnything2 && !normalizer->isInhabited(subTy)) return; if (reversed) diff --git a/Ast/include/Luau/Ast.h b/Ast/include/Luau/Ast.h index 070511632..aa87d9e86 100644 --- a/Ast/include/Luau/Ast.h +++ b/Ast/include/Luau/Ast.h @@ -5,6 +5,7 @@ #include #include +#include #include diff --git a/Ast/include/Luau/Location.h b/Ast/include/Luau/Location.h index e39bbf8c5..dbe36becb 100644 --- a/Ast/include/Luau/Location.h +++ b/Ast/include/Luau/Location.h @@ -1,8 +1,6 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #pragma once -#include - namespace Luau { @@ -10,130 +8,36 @@ struct Position { unsigned int line, column; - Position(unsigned int line, unsigned int column) - : line(line) - , column(column) - { - } - - bool operator==(const Position& rhs) const - { - return this->column == rhs.column && this->line == rhs.line; - } - bool operator!=(const Position& rhs) const - { - return !(*this == rhs); - } - - bool operator<(const Position& rhs) const - { - if (line == rhs.line) - return column < rhs.column; - else - return line < rhs.line; - } - - bool operator>(const Position& rhs) const - { - if (line == rhs.line) - return column > rhs.column; - else - return line > rhs.line; - } - - bool operator<=(const Position& rhs) const - { - return *this == rhs || *this < rhs; - } + Position(unsigned int line, unsigned int column); - bool operator>=(const Position& rhs) const - { - return *this == rhs || *this > rhs; - } + bool operator==(const Position& rhs) const; + bool operator!=(const Position& rhs) const; + bool operator<(const Position& rhs) const; + bool operator>(const Position& rhs) const; + bool operator<=(const Position& rhs) const; + bool operator>=(const Position& rhs) const; - void shift(const Position& start, const Position& oldEnd, const Position& newEnd) - { - if (*this >= start) - { - if (this->line > oldEnd.line) - this->line += (newEnd.line - oldEnd.line); - else - { - this->line = newEnd.line; - this->column += (newEnd.column - oldEnd.column); - } - } - } + void shift(const Position& start, const Position& oldEnd, const Position& newEnd); }; struct Location { Position begin, end; - Location() - : begin(0, 0) - , end(0, 0) - { - } + Location(); + Location(const Position& begin, const Position& end); + Location(const Position& begin, unsigned int length); + Location(const Location& begin, const Location& end); - Location(const Position& begin, const Position& end) - : begin(begin) - , end(end) - { - } + bool operator==(const Location& rhs) const; + bool operator!=(const Location& rhs) const; - Location(const Position& begin, unsigned int length) - : begin(begin) - , end(begin.line, begin.column + length) - { - } - - Location(const Location& begin, const Location& end) - : begin(begin.begin) - , end(end.end) - { - } - - bool operator==(const Location& rhs) const - { - return this->begin == rhs.begin && this->end == rhs.end; - } - bool operator!=(const Location& rhs) const - { - return !(*this == rhs); - } - - bool encloses(const Location& l) const - { - return begin <= l.begin && end >= l.end; - } - bool overlaps(const Location& l) const - { - return (begin <= l.begin && end >= l.begin) || (begin <= l.end && end >= l.end) || (begin >= l.begin && end <= l.end); - } - bool contains(const Position& p) const - { - return begin <= p && p < end; - } - bool containsClosed(const Position& p) const - { - return begin <= p && p <= end; - } - void extend(const Location& other) - { - if (other.begin < begin) - begin = other.begin; - if (other.end > end) - end = other.end; - } - void shift(const Position& start, const Position& oldEnd, const Position& newEnd) - { - begin.shift(start, oldEnd, newEnd); - end.shift(start, oldEnd, newEnd); - } + bool encloses(const Location& l) const; + bool overlaps(const Location& l) const; + bool contains(const Position& p) const; + bool containsClosed(const Position& p) const; + void extend(const Location& other); + void shift(const Position& start, const Position& oldEnd, const Position& newEnd); }; -std::string toString(const Position& position); -std::string toString(const Location& location); - } // namespace Luau diff --git a/Ast/src/Location.cpp b/Ast/src/Location.cpp index d7a899ed9..67c2dd4b6 100644 --- a/Ast/src/Location.cpp +++ b/Ast/src/Location.cpp @@ -4,14 +4,128 @@ namespace Luau { -std::string toString(const Position& position) +Position::Position(unsigned int line, unsigned int column) + : line(line) + , column(column) { - return "{ line = " + std::to_string(position.line) + ", col = " + std::to_string(position.column) + " }"; } -std::string toString(const Location& location) +bool Position::operator==(const Position& rhs) const { - return "Location { " + toString(location.begin) + ", " + toString(location.end) + " }"; + return this->column == rhs.column && this->line == rhs.line; +} + +bool Position::operator!=(const Position& rhs) const +{ + return !(*this == rhs); +} + +bool Position::operator<(const Position& rhs) const +{ + if (line == rhs.line) + return column < rhs.column; + else + return line < rhs.line; +} + +bool Position::operator>(const Position& rhs) const +{ + if (line == rhs.line) + return column > rhs.column; + else + return line > rhs.line; +} + +bool Position::operator<=(const Position& rhs) const +{ + return *this == rhs || *this < rhs; +} + +bool Position::operator>=(const Position& rhs) const +{ + return *this == rhs || *this > rhs; +} + +void Position::shift(const Position& start, const Position& oldEnd, const Position& newEnd) +{ + if (*this >= start) + { + if (this->line > oldEnd.line) + this->line += (newEnd.line - oldEnd.line); + else + { + this->line = newEnd.line; + this->column += (newEnd.column - oldEnd.column); + } + } +} + +Location::Location() + : begin(0, 0) + , end(0, 0) +{ +} + +Location::Location(const Position& begin, const Position& end) + : begin(begin) + , end(end) +{ +} + +Location::Location(const Position& begin, unsigned int length) + : begin(begin) + , end(begin.line, begin.column + length) +{ +} + +Location::Location(const Location& begin, const Location& end) + : begin(begin.begin) + , end(end.end) +{ +} + +bool Location::operator==(const Location& rhs) const +{ + return this->begin == rhs.begin && this->end == rhs.end; +} + +bool Location::operator!=(const Location& rhs) const +{ + return !(*this == rhs); +} + +bool Location::encloses(const Location& l) const +{ + return begin <= l.begin && end >= l.end; +} + +bool Location::overlaps(const Location& l) const +{ + return (begin <= l.begin && end >= l.begin) || (begin <= l.end && end >= l.end) || (begin >= l.begin && end <= l.end); +} + +bool Location::contains(const Position& p) const +{ + return begin <= p && p < end; +} + +bool Location::containsClosed(const Position& p) const +{ + return begin <= p && p <= end; +} + +void Location::extend(const Location& other) +{ + if (other.begin < begin) + begin = other.begin; + if (other.end > end) + end = other.end; +} + +void Location::shift(const Position& start, const Position& oldEnd, const Position& newEnd) +{ + begin.shift(start, oldEnd, newEnd); + end.shift(start, oldEnd, newEnd); } } // namespace Luau diff --git a/CLI/Ast.cpp b/CLI/Ast.cpp index fd99d2259..99c583936 100644 --- a/CLI/Ast.cpp +++ b/CLI/Ast.cpp @@ -6,6 +6,7 @@ #include "Luau/AstJsonEncoder.h" #include "Luau/Parser.h" #include "Luau/ParseOptions.h" +#include "Luau/ToString.h" #include "FileUtils.h" diff --git a/CodeGen/src/CodeGen.cpp b/CodeGen/src/CodeGen.cpp index 39ca913f1..1c05b2986 100644 --- a/CodeGen/src/CodeGen.cpp +++ b/CodeGen/src/CodeGen.cpp @@ -59,7 +59,7 @@ static void assembleHelpers(AssemblyBuilderX64& build, ModuleHelpers& helpers) } static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& helpers, Proto* proto, LuauOpcode op, const Instruction* pc, int i, - Label* labelarr, Label& fallback) + Label* labelarr, Label& next, Label& fallback) { int skip = 0; @@ -89,31 +89,31 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& emitInstGetGlobal(build, pc, i, fallback); break; case LOP_SETGLOBAL: - emitInstSetGlobal(build, pc, i, labelarr, fallback); + emitInstSetGlobal(build, pc, i, next, fallback); break; case LOP_CALL: - emitInstCall(build, helpers, pc, i, labelarr); + emitInstCall(build, helpers, pc, i); break; case LOP_RETURN: - emitInstReturn(build, helpers, pc, i, labelarr); + emitInstReturn(build, helpers, pc, i); break; case LOP_GETTABLE: - emitInstGetTable(build, pc, i, fallback); + emitInstGetTable(build, pc, fallback); break; case LOP_SETTABLE: - emitInstSetTable(build, pc, i, labelarr, fallback); + emitInstSetTable(build, pc, next, fallback); break; case LOP_GETTABLEKS: emitInstGetTableKS(build, pc, i, fallback); break; case LOP_SETTABLEKS: - emitInstSetTableKS(build, pc, i, labelarr, fallback); + emitInstSetTableKS(build, pc, i, next, fallback); break; case LOP_GETTABLEN: - emitInstGetTableN(build, pc, i, fallback); + emitInstGetTableN(build, pc, fallback); break; case LOP_SETTABLEN: - emitInstSetTableN(build, pc, i, labelarr, fallback); + emitInstSetTableN(build, pc, next, fallback); break; case LOP_JUMP: emitInstJump(build, pc, i, labelarr); @@ -161,94 +161,96 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& emitInstJumpxEqS(build, pc, i, labelarr); break; case LOP_ADD: - emitInstBinary(build, pc, i, TM_ADD, fallback); + emitInstBinary(build, pc, TM_ADD, fallback); break; case LOP_SUB: - emitInstBinary(build, pc, i, TM_SUB, fallback); + emitInstBinary(build, pc, TM_SUB, fallback); break; case LOP_MUL: - emitInstBinary(build, pc, i, TM_MUL, fallback); + emitInstBinary(build, pc, TM_MUL, fallback); break; case LOP_DIV: - emitInstBinary(build, pc, i, TM_DIV, fallback); + emitInstBinary(build, pc, TM_DIV, fallback); break; case LOP_MOD: - emitInstBinary(build, pc, i, TM_MOD, fallback); + emitInstBinary(build, pc, TM_MOD, fallback); break; case LOP_POW: - emitInstBinary(build, pc, i, TM_POW, fallback); + emitInstBinary(build, pc, TM_POW, fallback); break; case LOP_ADDK: - emitInstBinaryK(build, pc, i, TM_ADD, fallback); + emitInstBinaryK(build, pc, TM_ADD, fallback); break; case LOP_SUBK: - emitInstBinaryK(build, pc, i, TM_SUB, fallback); + emitInstBinaryK(build, pc, TM_SUB, fallback); break; case LOP_MULK: - emitInstBinaryK(build, pc, i, TM_MUL, fallback); + emitInstBinaryK(build, pc, TM_MUL, fallback); break; case LOP_DIVK: - emitInstBinaryK(build, pc, i, TM_DIV, fallback); + emitInstBinaryK(build, pc, TM_DIV, fallback); break; case LOP_MODK: - emitInstBinaryK(build, pc, i, TM_MOD, fallback); + emitInstBinaryK(build, pc, TM_MOD, fallback); break; case LOP_POWK: - emitInstPowK(build, pc, proto->k, i, fallback); + emitInstPowK(build, pc, proto->k, fallback); break; case LOP_NOT: emitInstNot(build, pc); break; case LOP_MINUS: - emitInstMinus(build, pc, i, fallback); + emitInstMinus(build, pc, fallback); break; case LOP_LENGTH: - emitInstLength(build, pc, i, fallback); + emitInstLength(build, pc, fallback); break; case LOP_NEWTABLE: - emitInstNewTable(build, pc, i, labelarr); + emitInstNewTable(build, pc, i, next); break; case LOP_DUPTABLE: - emitInstDupTable(build, pc, i, labelarr); + emitInstDupTable(build, pc, i, next); break; case LOP_SETLIST: - emitInstSetList(build, pc, i, labelarr); + emitInstSetList(build, pc, next); break; case LOP_GETUPVAL: - emitInstGetUpval(build, pc, i); + emitInstGetUpval(build, pc); break; case LOP_SETUPVAL: - emitInstSetUpval(build, pc, i, labelarr); + emitInstSetUpval(build, pc, next); break; case LOP_CLOSEUPVALS: - emitInstCloseUpvals(build, pc, i, labelarr); + emitInstCloseUpvals(build, pc, next); break; case LOP_FASTCALL: - skip = emitInstFastCall(build, pc, i, labelarr); + // We want to lower next instruction at skip+2, but this instruction is only 1 long, so we need to add 1 + skip = emitInstFastCall(build, pc, i, next) + 1; break; case LOP_FASTCALL1: - skip = emitInstFastCall1(build, pc, i, labelarr); + // We want to lower next instruction at skip+2, but this instruction is only 1 long, so we need to add 1 + skip = emitInstFastCall1(build, pc, i, next) + 1; break; case LOP_FASTCALL2: - skip = emitInstFastCall2(build, pc, i, labelarr); + skip = emitInstFastCall2(build, pc, i, next); break; case LOP_FASTCALL2K: - skip = emitInstFastCall2K(build, pc, i, labelarr); + skip = emitInstFastCall2K(build, pc, i, next); break; case LOP_FORNPREP: - emitInstForNPrep(build, pc, i, labelarr); + emitInstForNPrep(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]); break; case LOP_FORNLOOP: - emitInstForNLoop(build, pc, i, labelarr); + emitInstForNLoop(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]); break; case LOP_FORGLOOP: - emitinstForGLoop(build, pc, i, labelarr, fallback); + emitinstForGLoop(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)], next, fallback); break; case LOP_FORGPREP_NEXT: - emitInstForGPrepNext(build, pc, i, labelarr, fallback); + emitInstForGPrepNext(build, pc, labelarr[i + 1 + LUAU_INSN_D(*pc)], fallback); break; case LOP_FORGPREP_INEXT: - emitInstForGPrepInext(build, pc, i, labelarr, fallback); + emitInstForGPrepInext(build, pc, labelarr[i + 1 + LUAU_INSN_D(*pc)], fallback); break; case LOP_AND: emitInstAnd(build, pc); @@ -266,7 +268,7 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& emitInstGetImport(build, pc, fallback); break; case LOP_CONCAT: - emitInstConcat(build, pc, i, labelarr); + emitInstConcat(build, pc, i, next); break; default: emitFallback(build, data, op, i); @@ -281,7 +283,8 @@ static void emitInstFallback(AssemblyBuilderX64& build, NativeState& data, LuauO switch (op) { case LOP_GETIMPORT: - emitInstGetImportFallback(build, pc, i); + emitSetSavedPc(build, i + 1); + emitInstGetImportFallback(build, LUAU_INSN_A(*pc), pc[1]); break; case LOP_GETTABLE: emitInstGetTableFallback(build, pc, i); @@ -356,11 +359,11 @@ static void emitInstFallback(AssemblyBuilderX64& build, NativeState& data, LuauO emitInstLengthFallback(build, pc, i); break; case LOP_FORGLOOP: - emitinstForGLoopFallback(build, pc, i, labelarr); + emitinstForGLoopFallback(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]); break; case LOP_FORGPREP_NEXT: case LOP_FORGPREP_INEXT: - emitInstForGPrepXnextFallback(build, pc, i, labelarr); + emitInstForGPrepXnextFallback(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]); break; case LOP_GETGLOBAL: // TODO: luaV_gettable + cachedslot update instead of full fallback @@ -430,7 +433,9 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat if (options.annotator) options.annotator(options.annotatorContext, build.text, proto->bytecodeid, i); - int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), instFallbacks[i]); + Label& next = nexti < proto->sizecode ? instLabels[nexti] : start; // Last instruction can't use 'next' label + + int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), next, instFallbacks[i]); if (skip != 0) instOutlines.push_back({nexti, skip}); @@ -454,15 +459,20 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat const Instruction* pc = &proto->code[i]; LuauOpcode op = LuauOpcode(LUAU_INSN_OP(*pc)); + int nexti = i + getOpLength(op); + LUAU_ASSERT(nexti <= proto->sizecode); + build.setLabel(instLabels[i]); if (options.annotator && !options.skipOutlinedCode) options.annotator(options.annotatorContext, build.text, proto->bytecodeid, i); - int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), instFallbacks[i]); + Label& next = nexti < proto->sizecode ? instLabels[nexti] : start; // Last instruction can't use 'next' label + + int skip = emitInst(build, data, helpers, proto, op, pc, i, instLabels.data(), next, instFallbacks[i]); LUAU_ASSERT(skip == 0); - i += getOpLength(op); + i = nexti; } if (i < proto->sizecode) diff --git a/CodeGen/src/EmitCommonX64.cpp b/CodeGen/src/EmitCommonX64.cpp index cbaa84948..fe258ff83 100644 --- a/CodeGen/src/EmitCommonX64.cpp +++ b/CodeGen/src/EmitCommonX64.cpp @@ -61,10 +61,8 @@ void jumpOnNumberCmp(AssemblyBuilderX64& build, RegisterX64 tmp, OperandX64 lhs, } } -void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label, int pcpos) +void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label) { - emitSetSavedPc(build, pcpos + 1); - build.mov(rArg1, rState); build.lea(rArg2, luauRegAddress(ra)); build.lea(rArg3, luauRegAddress(rb)); @@ -85,10 +83,8 @@ void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX6 label); } -RegisterX64 getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 table, int pcpos) +void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, RegisterX64 table, int pcpos) { - RegisterX64 node = rdx; - LUAU_ASSERT(tmp != node); LUAU_ASSERT(table != node); @@ -102,16 +98,12 @@ RegisterX64 getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, // LuaNode* n = &h->node[slot]; build.shl(dwordReg(tmp), kLuaNodeSizeLog2); build.add(node, tmp); - - return node; } -void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, int ri, Label& label) +void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, Label& label) { LUAU_ASSERT(numi.size == SizeX64::dword); - build.vmovsd(numd, luauRegValue(ri)); - // Convert to integer, NaN is converted into 0x80000000 build.vcvttsd2si(numi, numd); @@ -124,10 +116,8 @@ void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, Regi build.jcc(ConditionX64::NotZero, label); } -void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, int pcpos, TMS tm) +void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, TMS tm) { - emitSetSavedPc(build, pcpos + 1); - if (build.abi == ABIX64::Windows) build.mov(sArg5, tm); else @@ -142,10 +132,8 @@ void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, in emitUpdateBase(build); } -void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb, int pcpos) +void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb) { - emitSetSavedPc(build, pcpos + 1); - build.mov(rArg1, rState); build.lea(rArg2, luauRegAddress(ra)); build.lea(rArg3, luauRegAddress(rb)); @@ -154,10 +142,8 @@ void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb, int pcpos) emitUpdateBase(build); } -void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init, int pcpos) +void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init) { - emitSetSavedPc(build, pcpos + 1); - build.mov(rArg1, rState); build.lea(rArg2, luauRegAddress(limit)); build.lea(rArg3, luauRegAddress(step)); @@ -165,10 +151,8 @@ void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init, i build.call(qword[rNativeContext + offsetof(NativeContext, luaV_prepareFORN)]); } -void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos) +void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra) { - emitSetSavedPc(build, pcpos + 1); - build.mov(rArg1, rState); build.lea(rArg2, luauRegAddress(rb)); build.lea(rArg3, c); @@ -178,10 +162,8 @@ void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int p emitUpdateBase(build); } -void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos) +void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra) { - emitSetSavedPc(build, pcpos + 1); - build.mov(rArg1, rState); build.lea(rArg2, luauRegAddress(rb)); build.lea(rArg3, c); diff --git a/CodeGen/src/EmitCommonX64.h b/CodeGen/src/EmitCommonX64.h index 615448551..238a0ed42 100644 --- a/CodeGen/src/EmitCommonX64.h +++ b/CodeGen/src/EmitCommonX64.h @@ -99,7 +99,7 @@ inline OperandX64 luauRegTag(int ri) return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, tt)]; } -inline OperandX64 luauRegValueBoolean(int ri) +inline OperandX64 luauRegValueInt(int ri) { return dword[rBase + ri * sizeof(TValue) + offsetof(TValue, value)]; } @@ -174,7 +174,7 @@ inline void jumpIfFalsy(AssemblyBuilderX64& build, int ri, Label& target, Label& jumpIfTagIs(build, ri, LUA_TNIL, target); // false if nil jumpIfTagIsNot(build, ri, LUA_TBOOLEAN, fallthrough); // true if not nil or boolean - build.cmp(luauRegValueBoolean(ri), 0); + build.cmp(luauRegValueInt(ri), 0); build.jcc(ConditionX64::Equal, target); // true if boolean value is 'true' } @@ -184,7 +184,7 @@ inline void jumpIfTruthy(AssemblyBuilderX64& build, int ri, Label& target, Label jumpIfTagIs(build, ri, LUA_TNIL, fallthrough); // false if nil jumpIfTagIsNot(build, ri, LUA_TBOOLEAN, target); // true if not nil or boolean - build.cmp(luauRegValueBoolean(ri), 0); + build.cmp(luauRegValueInt(ri), 0); build.jcc(ConditionX64::NotEqual, target); // true if boolean value is 'true' } @@ -236,16 +236,16 @@ inline void jumpIfNodeKeyNotInExpectedSlot(AssemblyBuilderX64& build, RegisterX6 } void jumpOnNumberCmp(AssemblyBuilderX64& build, RegisterX64 tmp, OperandX64 lhs, OperandX64 rhs, ConditionX64 cond, Label& label); -void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label, int pcpos); +void jumpOnAnyCmpFallback(AssemblyBuilderX64& build, int ra, int rb, ConditionX64 cond, Label& label); -RegisterX64 getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 table, int pcpos); -void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, int ri, Label& label); +void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 node, RegisterX64 table, int pcpos); +void convertNumberToIndexOrJump(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 numd, RegisterX64 numi, Label& label); -void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, int pcpos, TMS tm); -void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb, int pcpos); -void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init, int pcpos); -void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos); -void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra, int pcpos); +void callArithHelper(AssemblyBuilderX64& build, int ra, int rb, OperandX64 c, TMS tm); +void callLengthHelper(AssemblyBuilderX64& build, int ra, int rb); +void callPrepareForN(AssemblyBuilderX64& build, int limit, int step, int init); +void callGetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra); +void callSetTable(AssemblyBuilderX64& build, int rb, OperandX64 c, int ra); void callBarrierTable(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 table, int ra, Label& skip); void callBarrierObject(AssemblyBuilderX64& build, RegisterX64 tmp, RegisterX64 object, int ra, Label& skip); void callBarrierTableFast(AssemblyBuilderX64& build, RegisterX64 table, Label& skip); diff --git a/CodeGen/src/EmitInstructionX64.cpp b/CodeGen/src/EmitInstructionX64.cpp index 160f0f6cc..abbdb65ca 100644 --- a/CodeGen/src/EmitInstructionX64.cpp +++ b/CodeGen/src/EmitInstructionX64.cpp @@ -69,7 +69,7 @@ void emitInstMove(AssemblyBuilderX64& build, const Instruction* pc) build.vmovups(luauReg(ra), xmm0); } -void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos) { int ra = LUAU_INSN_A(*pc); int nparams = LUAU_INSN_B(*pc) - 1; @@ -222,7 +222,7 @@ void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instr } } -void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos) { emitInterrupt(build, pcpos); @@ -435,7 +435,8 @@ void emitInstJumpIfEqFallback(AssemblyBuilderX64& build, const Instruction* pc, { Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], not_ ? ConditionX64::NotEqual : ConditionX64::Equal, target, pcpos); + emitSetSavedPc(build, pcpos + 1); + jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], not_ ? ConditionX64::NotEqual : ConditionX64::Equal, target); } void emitInstJumpIfCond(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, ConditionX64 cond, Label& fallback) @@ -456,7 +457,8 @@ void emitInstJumpIfCondFallback(AssemblyBuilderX64& build, const Instruction* pc { Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], cond, target, pcpos); + emitSetSavedPc(build, pcpos + 1); + jumpOnAnyCmpFallback(build, LUAU_INSN_A(*pc), pc[1], cond, target); } void emitInstJumpX(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) @@ -488,7 +490,7 @@ void emitInstJumpxEqB(AssemblyBuilderX64& build, const Instruction* pc, int pcpo jumpIfTagIsNot(build, ra, LUA_TBOOLEAN, not_ ? target : exit); - build.test(luauRegValueBoolean(ra), 1); + build.test(luauRegValueInt(ra), 1); build.jcc((aux & 0x1) ^ not_ ? ConditionX64::NotZero : ConditionX64::Zero, target); } @@ -534,7 +536,7 @@ void emitInstJumpxEqS(AssemblyBuilderX64& build, const Instruction* pc, int pcpo build.jcc(not_ ? ConditionX64::NotEqual : ConditionX64::Equal, target); } -static void emitInstBinaryNumeric(AssemblyBuilderX64& build, int ra, int rb, int rc, OperandX64 opc, int pcpos, TMS tm, Label& fallback) +static void emitInstBinaryNumeric(AssemblyBuilderX64& build, int ra, int rb, int rc, OperandX64 opc, TMS tm, Label& fallback) { jumpIfTagIsNot(build, rb, LUA_TNUMBER, fallback); @@ -580,27 +582,29 @@ static void emitInstBinaryNumeric(AssemblyBuilderX64& build, int ra, int rb, int build.mov(luauRegTag(ra), LUA_TNUMBER); } -void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback) +void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback) { - emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), LUAU_INSN_C(*pc), luauRegValue(LUAU_INSN_C(*pc)), pcpos, tm, fallback); + emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), LUAU_INSN_C(*pc), luauRegValue(LUAU_INSN_C(*pc)), tm, fallback); } void emitInstBinaryFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm) { - callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), pcpos, tm); + emitSetSavedPc(build, pcpos + 1); + callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), tm); } -void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback) +void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback) { - emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), -1, luauConstantValue(LUAU_INSN_C(*pc)), pcpos, tm, fallback); + emitInstBinaryNumeric(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), -1, luauConstantValue(LUAU_INSN_C(*pc)), tm, fallback); } void emitInstBinaryKFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm) { - callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauConstantAddress(LUAU_INSN_C(*pc)), pcpos, tm); + emitSetSavedPc(build, pcpos + 1); + callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauConstantAddress(LUAU_INSN_C(*pc)), tm); } -void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, int pcpos, Label& fallback) +void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -647,17 +651,17 @@ void emitInstNot(AssemblyBuilderX64& build, const Instruction* pc) jumpIfFalsy(build, rb, saveone, savezero); build.setLabel(savezero); - build.mov(luauRegValueBoolean(ra), 0); + build.mov(luauRegValueInt(ra), 0); build.jmp(exit); build.setLabel(saveone); - build.mov(luauRegValueBoolean(ra), 1); + build.mov(luauRegValueInt(ra), 1); build.setLabel(exit); build.mov(luauRegTag(ra), LUA_TBOOLEAN); } -void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) +void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -675,10 +679,11 @@ void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, void emitInstMinusFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) { - callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_B(*pc)), pcpos, TM_UNM); + emitSetSavedPc(build, pcpos + 1); + callArithHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_B(*pc)), TM_UNM); } -void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) +void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -699,35 +704,32 @@ void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, void emitInstLengthFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) { - callLengthHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), pcpos); + emitSetSavedPc(build, pcpos + 1); + callLengthHelper(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc)); } -void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next) { int ra = LUAU_INSN_A(*pc); int b = LUAU_INSN_B(*pc); uint32_t aux = pc[1]; - Label& exit = labelarr[pcpos + 2]; - emitSetSavedPc(build, pcpos + 1); build.mov(rArg1, rState); build.mov(dwordReg(rArg2), aux); - build.mov(dwordReg(rArg3), 1 << (b - 1)); + build.mov(dwordReg(rArg3), b == 0 ? 0 : 1 << (b - 1)); build.call(qword[rNativeContext + offsetof(NativeContext, luaH_new)]); build.mov(luauRegValue(ra), rax); build.mov(luauRegTag(ra), LUA_TTABLE); - callCheckGc(build, pcpos, /* savepc = */ false, exit); + callCheckGc(build, pcpos, /* savepc = */ false, next); } -void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next) { int ra = LUAU_INSN_A(*pc); - Label& exit = labelarr[pcpos + 1]; - emitSetSavedPc(build, pcpos + 1); build.mov(rArg1, rState); @@ -736,18 +738,16 @@ void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo build.mov(luauRegValue(ra), rax); build.mov(luauRegTag(ra), LUA_TTABLE); - callCheckGc(build, pcpos, /* savepc= */ false, exit); + callCheckGc(build, pcpos, /* savepc= */ false, next); } -void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, Label& next) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); int c = LUAU_INSN_C(*pc) - 1; uint32_t index = pc[1]; - Label& exit = labelarr[pcpos + 2]; - OperandX64 last = index + c - 1; // Using non-volatile 'rbx' for dynamic 'c' value (for LUA_MULTRET) to skip later recomputation @@ -842,10 +842,10 @@ void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, int pcpos build.setLabel(endLoop); } - callBarrierTableFast(build, table, exit); + callBarrierTableFast(build, table, next); } -void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) +void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc) { int ra = LUAU_INSN_A(*pc); int up = LUAU_INSN_B(*pc); @@ -869,7 +869,7 @@ void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpo build.vmovups(luauReg(ra), xmm0); } -void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, Label& next) { int ra = LUAU_INSN_A(*pc); int up = LUAU_INSN_B(*pc); @@ -884,32 +884,30 @@ void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpo build.vmovups(xmm0, luauReg(ra)); build.vmovups(xmmword[tmp], xmm0); - callBarrierObject(build, tmp, upval, ra, labelarr[pcpos + 1]); + callBarrierObject(build, tmp, upval, ra, next); } -void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, Label& next) { int ra = LUAU_INSN_A(*pc); - Label& skip = labelarr[pcpos + 1]; - // L->openupval != 0 build.mov(rax, qword[rState + offsetof(lua_State, openupval)]); build.test(rax, rax); - build.jcc(ConditionX64::Zero, skip); + build.jcc(ConditionX64::Zero, next); // ra <= L->openuval->v build.lea(rcx, addr[rBase + ra * sizeof(TValue)]); build.cmp(rcx, qword[rax + offsetof(UpVal, v)]); - build.jcc(ConditionX64::Above, skip); + build.jcc(ConditionX64::Above, next); build.mov(rArg2, rcx); build.mov(rArg1, rState); build.call(qword[rNativeContext + offsetof(NativeContext, luaF_close)]); } -static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, bool customParams, int customParamCount, OperandX64 customArgs, - int pcpos, int instLen, Label* labelarr) +static int emitInstFastCallN( + AssemblyBuilderX64& build, const Instruction* pc, bool customParams, int customParamCount, OperandX64 customArgs, int pcpos, Label& fallback) { int bfid = LUAU_INSN_A(*pc); int skip = LUAU_INSN_C(*pc); @@ -923,11 +921,9 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b int arg = customParams ? LUAU_INSN_B(*pc) : ra + 1; OperandX64 args = customParams ? customArgs : luauRegAddress(ra + 2); - Label& exit = labelarr[pcpos + instLen]; - - jumpIfUnsafeEnv(build, rax, exit); + jumpIfUnsafeEnv(build, rax, fallback); - BuiltinImplResult br = emitBuiltin(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, exit); + BuiltinImplResult br = emitBuiltin(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, fallback); if (br.type == BuiltinImplType::UsesFallback) { @@ -945,7 +941,7 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b build.mov(qword[rState + offsetof(lua_State, top)], rax); } - return skip + 2 - instLen; // Return fallback instruction sequence length + return skip; // Return fallback instruction sequence length } // TODO: we can skip saving pc for some well-behaved builtins which we didn't inline @@ -996,8 +992,8 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b build.call(rax); - build.test(eax, eax); // test here will set SF=1 for a negative number and it always sets OF to 0 - build.jcc(ConditionX64::Less, exit); // jl jumps if SF != OF + build.test(eax, eax); // test here will set SF=1 for a negative number and it always sets OF to 0 + build.jcc(ConditionX64::Less, fallback); // jl jumps if SF != OF if (nresults == LUA_MULTRET) { @@ -1014,35 +1010,33 @@ static int emitInstFastCallN(AssemblyBuilderX64& build, const Instruction* pc, b build.mov(qword[rState + offsetof(lua_State, top)], rax); } - return skip + 2 - instLen; // Return fallback instruction sequence length + return skip; // Return fallback instruction sequence length } -int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) { - return emitInstFastCallN(build, pc, /* customParams */ true, /* customParamCount */ 1, /* customArgs */ 0, pcpos, /* instLen */ 1, labelarr); + return emitInstFastCallN(build, pc, /* customParams */ true, /* customParamCount */ 1, /* customArgs */ 0, pcpos, fallback); } -int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) { - return emitInstFastCallN( - build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauRegAddress(pc[1]), pcpos, /* instLen */ 2, labelarr); + return emitInstFastCallN(build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauRegAddress(pc[1]), pcpos, fallback); } -int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) { return emitInstFastCallN( - build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauConstantAddress(pc[1]), pcpos, /* instLen */ 2, labelarr); + build, pc, /* customParams */ true, /* customParamCount */ 2, /* customArgs */ luauConstantAddress(pc[1]), pcpos, fallback); } -int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) { - return emitInstFastCallN(build, pc, /* customParams */ false, /* customParamCount */ 0, /* customArgs */ 0, pcpos, /* instLen */ 1, labelarr); + return emitInstFastCallN(build, pc, /* customParams */ false, /* customParamCount */ 0, /* customArgs */ 0, pcpos, fallback); } -void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopExit) { int ra = LUAU_INSN_A(*pc); - Label& loopExit = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; Label tryConvert, exit; @@ -1080,18 +1074,18 @@ void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpo // TOOD: place at the end of the function build.setLabel(tryConvert); - callPrepareForN(build, ra + 0, ra + 1, ra + 2, pcpos); + emitSetSavedPc(build, pcpos + 1); + callPrepareForN(build, ra + 0, ra + 1, ra + 2); build.jmp(retry); build.setLabel(exit); } -void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat) { emitInterrupt(build, pcpos); int ra = LUAU_INSN_A(*pc); - Label& loopRepeat = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; RegisterX64 limit = xmm0; RegisterX64 step = xmm1; @@ -1121,14 +1115,11 @@ void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpo build.setLabel(exit); } -void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat, Label& loopExit, Label& fallback) { int ra = LUAU_INSN_A(*pc); int aux = pc[1]; - Label& loopRepeat = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - Label& exit = labelarr[pcpos + 2]; - emitInterrupt(build, pcpos); // fast-path: builtin table iteration @@ -1160,13 +1151,13 @@ void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpo // while (unsigned(index) < unsigned(sizearray)) Label arrayLoop = build.setLabel(); build.cmp(dwordReg(index), dword[table + offsetof(Table, sizearray)]); - build.jcc(ConditionX64::NotBelow, isIpairsIter ? exit : skipArray); + build.jcc(ConditionX64::NotBelow, isIpairsIter ? loopExit : skipArray); // If element is nil, we increment the index; if it's not, we still need 'index + 1' inside build.inc(index); build.cmp(dword[elemPtr + offsetof(TValue, tt)], LUA_TNIL); - build.jcc(ConditionX64::Equal, isIpairsIter ? exit : skipArrayNil); + build.jcc(ConditionX64::Equal, isIpairsIter ? loopExit : skipArrayNil); // setpvalue(ra + 2, reinterpret_cast(uintptr_t(index + 1))); build.mov(luauRegValue(ra + 2), index); @@ -1202,13 +1193,11 @@ void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpo } } -void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat) { int ra = LUAU_INSN_A(*pc); int aux = pc[1]; - Label& loopRepeat = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - emitSetSavedPc(build, pcpos + 1); build.mov(rArg1, rState); @@ -1220,12 +1209,10 @@ void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, build.jcc(ConditionX64::NotZero, loopRepeat); } -void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback) { int ra = LUAU_INSN_A(*pc); - Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - // fast-path: pairs/next jumpIfUnsafeEnv(build, rax, fallback); jumpIfTagIsNot(build, ra + 1, LUA_TTABLE, fallback); @@ -1240,12 +1227,10 @@ void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, int build.jmp(target); } -void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback) { int ra = LUAU_INSN_A(*pc); - Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - // fast-path: ipairs/inext jumpIfUnsafeEnv(build, rax, fallback); jumpIfTagIsNot(build, ra + 1, LUA_TTABLE, fallback); @@ -1264,12 +1249,10 @@ void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, int build.jmp(target); } -void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& target) { int ra = LUAU_INSN_A(*pc); - Label& target = labelarr[pcpos + 1 + LUAU_INSN_D(*pc)]; - build.mov(rArg1, rState); build.lea(rArg2, luauRegAddress(ra)); build.mov(dwordReg(rArg3), pcpos + 1); @@ -1353,7 +1336,7 @@ void emitInstOrK(AssemblyBuilderX64& build, const Instruction* pc) emitInstOrX(build, LUAU_INSN_A(*pc), LUAU_INSN_B(*pc), luauConstant(LUAU_INSN_C(*pc))); } -void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) +void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -1376,12 +1359,14 @@ void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcp void emitInstGetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) { + emitSetSavedPc(build, pcpos + 1); + TValue n; setnvalue(&n, LUAU_INSN_C(*pc) + 1); - callGetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc), pcpos); + callGetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc)); } -void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -1404,17 +1389,19 @@ void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcp build.vmovups(xmm0, luauReg(ra)); build.vmovups(xmmword[rax + c * sizeof(TValue)], xmm0); - callBarrierTable(build, rax, table, ra, labelarr[pcpos + 1]); + callBarrierTable(build, rax, table, ra, next); } void emitInstSetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) { + emitSetSavedPc(build, pcpos + 1); + TValue n; setnvalue(&n, LUAU_INSN_C(*pc) + 1); - callSetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc), pcpos); + callSetTable(build, LUAU_INSN_B(*pc), build.bytes(&n, sizeof(n)), LUAU_INSN_A(*pc)); } -void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) +void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -1427,29 +1414,33 @@ void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo RegisterX64 table = rcx; build.mov(table, luauRegValue(rb)); - convertNumberToIndexOrJump(build, xmm1, xmm0, eax, rc, fallback); + RegisterX64 intIndex = eax; + RegisterX64 fpIndex = xmm0; + build.vmovsd(fpIndex, luauRegValue(rc)); + convertNumberToIndexOrJump(build, xmm1, fpIndex, intIndex, fallback); // index - 1 - build.dec(eax); + build.dec(intIndex); // unsigned(index - 1) < unsigned(h->sizearray) - build.cmp(dword[table + offsetof(Table, sizearray)], eax); + build.cmp(dword[table + offsetof(Table, sizearray)], intIndex); build.jcc(ConditionX64::BelowEqual, fallback); jumpIfMetatablePresent(build, table, fallback); // setobj2s(L, ra, &h->array[unsigned(index - 1)]); build.mov(rdx, qword[table + offsetof(Table, array)]); - build.shl(eax, kTValueSizeLog2); + build.shl(intIndex, kTValueSizeLog2); setLuauReg(build, xmm0, ra, xmmword[rdx + rax]); } void emitInstGetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) { - callGetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc), pcpos); + emitSetSavedPc(build, pcpos + 1); + callGetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc)); } -void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -1462,13 +1453,16 @@ void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo RegisterX64 table = rcx; build.mov(table, luauRegValue(rb)); - convertNumberToIndexOrJump(build, xmm1, xmm0, eax, rc, fallback); + RegisterX64 intIndex = eax; + RegisterX64 fpIndex = xmm0; + build.vmovsd(fpIndex, luauRegValue(rc)); + convertNumberToIndexOrJump(build, xmm1, fpIndex, intIndex, fallback); // index - 1 - build.dec(eax); + build.dec(intIndex); // unsigned(index - 1) < unsigned(h->sizearray) - build.cmp(dword[table + offsetof(Table, sizearray)], eax); + build.cmp(dword[table + offsetof(Table, sizearray)], intIndex); build.jcc(ConditionX64::BelowEqual, fallback); jumpIfMetatablePresent(build, table, fallback); @@ -1476,16 +1470,17 @@ void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpo // setobj2t(L, &h->array[unsigned(index - 1)], ra); build.mov(rdx, qword[table + offsetof(Table, array)]); - build.shl(eax, kTValueSizeLog2); + build.shl(intIndex, kTValueSizeLog2); build.vmovups(xmm0, luauReg(ra)); build.vmovups(xmmword[rdx + rax], xmm0); - callBarrierTable(build, rdx, table, ra, labelarr[pcpos + 1]); + callBarrierTable(build, rdx, table, ra, next); } void emitInstSetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) { - callSetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc), pcpos); + emitSetSavedPc(build, pcpos + 1); + callSetTable(build, LUAU_INSN_B(*pc), luauRegAddress(LUAU_INSN_C(*pc)), LUAU_INSN_A(*pc)); } void emitInstGetImport(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback) @@ -1504,13 +1499,8 @@ void emitInstGetImport(AssemblyBuilderX64& build, const Instruction* pc, Label& build.vmovups(luauReg(ra), xmm0); } -void emitInstGetImportFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos) +void emitInstGetImportFallback(AssemblyBuilderX64& build, int ra, uint32_t aux) { - int ra = LUAU_INSN_A(*pc); - uint32_t aux = pc[1]; - - emitSetSavedPc(build, pcpos + 1); - build.mov(rax, sClosure); // luaV_getimport(L, cl->env, k, aux, /* propagatenil= */ false) @@ -1548,14 +1538,15 @@ void emitInstGetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pc RegisterX64 table = rcx; build.mov(table, luauRegValue(rb)); - RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos); + RegisterX64 node = rdx; + getTableNodeAtCachedSlot(build, rax, node, table, pcpos); jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback); setLuauReg(build, xmm0, ra, luauNodeValue(node)); } -void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -1567,14 +1558,15 @@ void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pc build.mov(table, luauRegValue(rb)); // fast-path: set value at the expected slot - RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos); + RegisterX64 node = rdx; + getTableNodeAtCachedSlot(build, rax, node, table, pcpos); jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback); jumpIfTableIsReadOnly(build, table, fallback); setNodeValue(build, xmm0, luauNodeValue(node), ra); - callBarrierTable(build, rax, table, ra, labelarr[pcpos + 2]); + callBarrierTable(build, rax, table, ra, next); } void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback) @@ -1585,14 +1577,15 @@ void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcp RegisterX64 table = rcx; build.mov(rax, sClosure); build.mov(table, qword[rax + offsetof(Closure, env)]); - RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos); + RegisterX64 node = rdx; + getTableNodeAtCachedSlot(build, rax, node, table, pcpos); jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback); setLuauReg(build, xmm0, ra, luauNodeValue(node)); } -void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback) +void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback) { int ra = LUAU_INSN_A(*pc); uint32_t aux = pc[1]; @@ -1600,17 +1593,18 @@ void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcp RegisterX64 table = rcx; build.mov(rax, sClosure); build.mov(table, qword[rax + offsetof(Closure, env)]); - RegisterX64 node = getTableNodeAtCachedSlot(build, rax, table, pcpos); + RegisterX64 node = rdx; + getTableNodeAtCachedSlot(build, rax, node, table, pcpos); jumpIfNodeKeyNotInExpectedSlot(build, rax, node, luauConstantValue(aux), fallback); jumpIfTableIsReadOnly(build, table, fallback); setNodeValue(build, xmm0, luauNodeValue(node), ra); - callBarrierTable(build, rax, table, ra, labelarr[pcpos + 2]); + callBarrierTable(build, rax, table, ra, next); } -void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr) +void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next) { int ra = LUAU_INSN_A(*pc); int rb = LUAU_INSN_B(*pc); @@ -1630,7 +1624,7 @@ void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, build.vmovups(xmm0, luauReg(rb)); build.vmovups(luauReg(ra), xmm0); - callCheckGc(build, pcpos, /* savepc= */ false, labelarr[pcpos + 1]); + callCheckGc(build, pcpos, /* savepc= */ false, next); } } // namespace CodeGen diff --git a/CodeGen/src/EmitInstructionX64.h b/CodeGen/src/EmitInstructionX64.h index ae310acab..1ecb06d4f 100644 --- a/CodeGen/src/EmitInstructionX64.h +++ b/CodeGen/src/EmitInstructionX64.h @@ -24,8 +24,8 @@ void emitInstLoadN(AssemblyBuilderX64& build, const Instruction* pc); void emitInstLoadK(AssemblyBuilderX64& build, const Instruction* pc); void emitInstLoadKX(AssemblyBuilderX64& build, const Instruction* pc); void emitInstMove(AssemblyBuilderX64& build, const Instruction* pc); -void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos, Label* labelarr); +void emitInstCall(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos); +void emitInstReturn(AssemblyBuilderX64& build, ModuleHelpers& helpers, const Instruction* pc, int pcpos); void emitInstJump(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); void emitInstJumpBack(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); void emitInstJumpIf(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, bool not_); @@ -38,52 +38,52 @@ void emitInstJumpxEqNil(AssemblyBuilderX64& build, const Instruction* pc, int pc void emitInstJumpxEqB(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); void emitInstJumpxEqN(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, int pcpos, Label* labelarr); void emitInstJumpxEqS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback); +void emitInstBinary(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback); void emitInstBinaryFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm); -void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm, Label& fallback); +void emitInstBinaryK(AssemblyBuilderX64& build, const Instruction* pc, TMS tm, Label& fallback); void emitInstBinaryKFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, TMS tm); -void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, int pcpos, Label& fallback); +void emitInstPowK(AssemblyBuilderX64& build, const Instruction* pc, const TValue* k, Label& fallback); void emitInstNot(AssemblyBuilderX64& build, const Instruction* pc); -void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +void emitInstMinus(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback); void emitInstMinusFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); -void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +void emitInstLength(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback); void emitInstLengthFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); -void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); -void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); -void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); -void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); -void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); -void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); +void emitInstNewTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next); +void emitInstDupTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next); +void emitInstSetList(AssemblyBuilderX64& build, const Instruction* pc, Label& next); +void emitInstGetUpval(AssemblyBuilderX64& build, const Instruction* pc); +void emitInstSetUpval(AssemblyBuilderX64& build, const Instruction* pc, Label& next); +void emitInstCloseUpvals(AssemblyBuilderX64& build, const Instruction* pc, Label& next); +int emitInstFastCall1(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +int emitInstFastCall2(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +int emitInstFastCall2K(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +int emitInstFastCall(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +void emitInstForNPrep(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopExit); +void emitInstForNLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat); +void emitinstForGLoop(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat, Label& loopExit, Label& fallback); +void emitinstForGLoopFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& loopRepeat); +void emitInstForGPrepNext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback); +void emitInstForGPrepInext(AssemblyBuilderX64& build, const Instruction* pc, Label& target, Label& fallback); +void emitInstForGPrepXnextFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& target); void emitInstAnd(AssemblyBuilderX64& build, const Instruction* pc); void emitInstAndK(AssemblyBuilderX64& build, const Instruction* pc); void emitInstOr(AssemblyBuilderX64& build, const Instruction* pc); void emitInstOrK(AssemblyBuilderX64& build, const Instruction* pc); -void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +void emitInstGetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback); void emitInstGetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); -void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); +void emitInstSetTableN(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback); void emitInstSetTableNFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); -void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); +void emitInstGetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback); void emitInstGetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); -void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); +void emitInstSetTable(AssemblyBuilderX64& build, const Instruction* pc, Label& next, Label& fallback); void emitInstSetTableFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); void emitInstGetImport(AssemblyBuilderX64& build, const Instruction* pc, Label& fallback); -void emitInstGetImportFallback(AssemblyBuilderX64& build, const Instruction* pc, int pcpos); +void emitInstGetImportFallback(AssemblyBuilderX64& build, int ra, uint32_t aux); void emitInstGetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); -void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); +void emitInstSetTableKS(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback); void emitInstGetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& fallback); -void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr, Label& fallback); -void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label* labelarr); +void emitInstSetGlobal(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next, Label& fallback); +void emitInstConcat(AssemblyBuilderX64& build, const Instruction* pc, int pcpos, Label& next); } // namespace CodeGen } // namespace Luau diff --git a/tests/AstJsonEncoder.test.cpp b/tests/AstJsonEncoder.test.cpp index a14d5f595..1532b7a8f 100644 --- a/tests/AstJsonEncoder.test.cpp +++ b/tests/AstJsonEncoder.test.cpp @@ -6,6 +6,7 @@ #include "doctest.h" +#include #include using namespace Luau; @@ -58,6 +59,9 @@ TEST_CASE("encode_constants") AstExprConstantBool b{Location(), true}; AstExprConstantNumber n{Location(), 8.2}; AstExprConstantNumber bigNum{Location(), 0.1677721600000003}; + AstExprConstantNumber positiveInfinity{Location(), INFINITY}; + AstExprConstantNumber negativeInfinity{Location(), -INFINITY}; + AstExprConstantNumber nan{Location(), NAN}; AstArray charString; charString.data = const_cast("a\x1d\0\\\"b"); @@ -69,6 +73,9 @@ TEST_CASE("encode_constants") CHECK_EQ(R"({"type":"AstExprConstantBool","location":"0,0 - 0,0","value":true})", toJson(&b)); CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":8.1999999999999993})", toJson(&n)); CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":0.16777216000000031})", toJson(&bigNum)); + CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":Infinity})", toJson(&positiveInfinity)); + CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":-Infinity})", toJson(&negativeInfinity)); + CHECK_EQ(R"({"type":"AstExprConstantNumber","location":"0,0 - 0,0","value":NaN})", toJson(&nan)); CHECK_EQ("{\"type\":\"AstExprConstantString\",\"location\":\"0,0 - 0,0\",\"value\":\"a\\u001d\\u0000\\\\\\\"b\"}", toJson(&needsEscaping)); } diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index 45baec2ce..123708cab 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -2948,6 +2948,71 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singletons") CHECK_EQ(ac.context, AutocompleteContext::String); } +TEST_CASE_FIXTURE(ACFixture, "string_singleton_as_table_key") +{ + ScopedFastFlag sff{"LuauCompleteTableKeysBetter", true}; + + check(R"( + type Direction = "up" | "down" + + local a: {[Direction]: boolean} = {[@1] = true} + local b: {[Direction]: boolean} = {["@2"] = true} + local c: {[Direction]: boolean} = {u@3 = true} + local d: {[Direction]: boolean} = {[u@4] = true} + + local e: {[Direction]: boolean} = {[@5]} + local f: {[Direction]: boolean} = {["@6"]} + local g: {[Direction]: boolean} = {u@7} + local h: {[Direction]: boolean} = {[u@8]} + )"); + + auto ac = autocomplete('1'); + + CHECK(ac.entryMap.count("\"up\"")); + CHECK(ac.entryMap.count("\"down\"")); + + ac = autocomplete('2'); + + CHECK(ac.entryMap.count("up")); + CHECK(ac.entryMap.count("down")); + + ac = autocomplete('3'); + + CHECK(ac.entryMap.count("up")); + CHECK(ac.entryMap.count("down")); + + ac = autocomplete('4'); + + CHECK(!ac.entryMap.count("up")); + CHECK(!ac.entryMap.count("down")); + + CHECK(ac.entryMap.count("\"up\"")); + CHECK(ac.entryMap.count("\"down\"")); + + ac = autocomplete('5'); + + CHECK(ac.entryMap.count("\"up\"")); + CHECK(ac.entryMap.count("\"down\"")); + + ac = autocomplete('6'); + + CHECK(ac.entryMap.count("up")); + CHECK(ac.entryMap.count("down")); + + ac = autocomplete('7'); + + CHECK(ac.entryMap.count("up")); + CHECK(ac.entryMap.count("down")); + + ac = autocomplete('8'); + + CHECK(!ac.entryMap.count("up")); + CHECK(!ac.entryMap.count("down")); + + CHECK(ac.entryMap.count("\"up\"")); + CHECK(ac.entryMap.count("\"down\"")); +} + TEST_CASE_FIXTURE(ACFixture, "autocomplete_string_singleton_equality") { check(R"( diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index b28155e30..eb77ce521 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -2,19 +2,21 @@ #include "Fixture.h" #include "Luau/AstQuery.h" +#include "Luau/BuiltinDefinitions.h" +#include "Luau/Constraint.h" #include "Luau/ModuleResolver.h" #include "Luau/NotNull.h" #include "Luau/Parser.h" #include "Luau/TypeVar.h" #include "Luau/TypeAttach.h" #include "Luau/Transpiler.h" -#include "Luau/BuiltinDefinitions.h" #include "doctest.h" #include #include #include +#include static const char* mainModuleName = "MainModule"; @@ -27,6 +29,41 @@ extern std::optional randomSeed; // tests/main.cpp namespace Luau { +std::optional TestFileResolver::resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) +{ + if (auto name = pathExprToModuleName(currentModuleName, pathExpr)) + return {{*name, false}}; + + return std::nullopt; +} + +const ModulePtr TestFileResolver::getModule(const ModuleName& moduleName) const +{ + LUAU_ASSERT(false); + return nullptr; +} + +bool TestFileResolver::moduleExists(const ModuleName& moduleName) const +{ + auto it = source.find(moduleName); + return (it != source.end()); +} + +std::optional TestFileResolver::readSource(const ModuleName& name) +{ + auto it = source.find(name); + if (it == source.end()) + return std::nullopt; + + SourceCode::Type sourceType = SourceCode::Module; + + auto it2 = sourceTypes.find(name); + if (it2 != sourceTypes.end()) + sourceType = it2->second; + + return SourceCode{it->second, sourceType}; +} + std::optional TestFileResolver::resolveModule(const ModuleInfo* context, AstExpr* expr) { if (AstExprGlobal* g = expr->as()) @@ -90,6 +127,15 @@ std::optional TestFileResolver::getEnvironmentForModule(const Modul return std::nullopt; } +const Config& TestConfigResolver::getConfig(const ModuleName& name) const +{ + auto it = configFiles.find(name); + if (it != configFiles.end()) + return it->second; + + return defaultConfig; +} + Fixture::Fixture(bool freeze, bool prepareAutocomplete) : sff_DebugLuauFreezeArena("DebugLuauFreezeArena", freeze) , frontend(&fileResolver, &configResolver, diff --git a/tests/Fixture.h b/tests/Fixture.h index 24c9566fe..5d838b163 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -10,59 +10,31 @@ #include "Luau/ModuleResolver.h" #include "Luau/Scope.h" #include "Luau/ToString.h" -#include "Luau/TypeInfer.h" #include "Luau/TypeVar.h" #include "IostreamOptional.h" #include "ScopedFlags.h" -#include #include #include - #include namespace Luau { +struct TypeChecker; + struct TestFileResolver : FileResolver , ModuleResolver { - std::optional resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override - { - if (auto name = pathExprToModuleName(currentModuleName, pathExpr)) - return {{*name, false}}; - - return std::nullopt; - } + std::optional resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override; - const ModulePtr getModule(const ModuleName& moduleName) const override - { - LUAU_ASSERT(false); - return nullptr; - } + const ModulePtr getModule(const ModuleName& moduleName) const override; - bool moduleExists(const ModuleName& moduleName) const override - { - auto it = source.find(moduleName); - return (it != source.end()); - } + bool moduleExists(const ModuleName& moduleName) const override; - std::optional readSource(const ModuleName& name) override - { - auto it = source.find(name); - if (it == source.end()) - return std::nullopt; - - SourceCode::Type sourceType = SourceCode::Module; - - auto it2 = sourceTypes.find(name); - if (it2 != sourceTypes.end()) - sourceType = it2->second; - - return SourceCode{it->second, sourceType}; - } + std::optional readSource(const ModuleName& name) override; std::optional resolveModule(const ModuleInfo* context, AstExpr* expr) override; @@ -80,14 +52,7 @@ struct TestConfigResolver : ConfigResolver Config defaultConfig; std::unordered_map configFiles; - const Config& getConfig(const ModuleName& name) const override - { - auto it = configFiles.find(name); - if (it != configFiles.end()) - return it->second; - - return defaultConfig; - } + const Config& getConfig(const ModuleName& name) const override; }; struct Fixture diff --git a/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index 4e72dd4e7..6f92b6551 100644 --- a/tests/Frontend.test.cpp +++ b/tests/Frontend.test.cpp @@ -519,10 +519,6 @@ TEST_CASE_FIXTURE(FrontendFixture, "recheck_if_dependent_script_is_dirty") TEST_CASE_FIXTURE(FrontendFixture, "mark_non_immediate_reverse_deps_as_dirty") { - ScopedFastFlag sff[] = { - {"LuauFixMarkDirtyReverseDeps", true}, - }; - fileResolver.source["game/Gui/Modules/A"] = "return {hello=5, world=true}"; fileResolver.source["game/Gui/Modules/B"] = R"( return require(game:GetService('Gui').Modules.A) diff --git a/tests/Normalize.test.cpp b/tests/Normalize.test.cpp index a8f3c7ba0..e6bf00a12 100644 --- a/tests/Normalize.test.cpp +++ b/tests/Normalize.test.cpp @@ -393,7 +393,6 @@ TEST_SUITE_END(); struct NormalizeFixture : Fixture { - ScopedFastFlag sff0{"LuauNegatedStringSingletons", true}; ScopedFastFlag sff1{"LuauNegatedFunctionTypes", true}; TypeArena arena; diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index 29954dc46..05f49422b 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -269,9 +269,9 @@ TEST_CASE_FIXTURE(Fixture, "quit_stringifying_type_when_length_is_exceeded") { o.maxTypeLength = 30; CHECK_EQ(toString(requireType("f0"), o), "() -> ()"); - CHECK_EQ(toString(requireType("f1"), o), "(a) -> () -> ()"); - CHECK_EQ(toString(requireType("f2"), o), "(b) -> (a) -> () -> ()"); - CHECK_EQ(toString(requireType("f3"), o), "(c) -> (b) -> (a) -> (... *TRUNCATED*"); + CHECK_EQ(toString(requireType("f1"), o), "(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*"); + CHECK_EQ(toString(requireType("f2"), o), "(b) -> ((a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*"); + CHECK_EQ(toString(requireType("f3"), o), "(c) -> ((b) -> ((a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*"); } else { @@ -299,9 +299,9 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive") { o.maxTypeLength = 30; CHECK_EQ(toString(requireType("f0"), o), "() -> ()"); - CHECK_EQ(toString(requireType("f1"), o), "(a) -> () -> ()"); - CHECK_EQ(toString(requireType("f2"), o), "(b) -> (a) -> () -> ()"); - CHECK_EQ(toString(requireType("f3"), o), "(c) -> (b) -> (a) -> (... *TRUNCATED*"); + CHECK_EQ(toString(requireType("f1"), o), "(a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*"); + CHECK_EQ(toString(requireType("f2"), o), "(b) -> ((a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*"); + CHECK_EQ(toString(requireType("f3"), o), "(c) -> ((b) -> ((a) -> (() -> ()) | (a & ~(false?))... *TRUNCATED*"); } else { diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index 53c54f4f3..38e246c8d 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -8,8 +8,8 @@ using namespace Luau; LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) -LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes) LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError) +LUAU_FASTFLAG(LuauNewLibraryTypeNames) TEST_SUITE_BEGIN("TypeAliases"); @@ -525,21 +525,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "general_require_multi_assign") TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_import_mutation") { - ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true}; - CheckResult result = check("type t10 = typeof(table)"); LUAU_REQUIRE_NO_ERRORS(result); TypeId ty = getGlobalBinding(frontend, "table"); - if (FFlag::LuauNoMoreGlobalSingletonTypes) - { - CHECK_EQ(toString(ty), "typeof(table)"); - } + if (FFlag::LuauNewLibraryTypeNames) + CHECK(toString(ty) == "typeof(table)"); else - { - CHECK_EQ(toString(ty), "table"); - } + CHECK(toString(ty) == "table"); const TableTypeVar* ttv = get(ty); REQUIRE(ttv); diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index 7d0621a79..188be63c7 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -533,7 +533,7 @@ TEST_CASE_FIXTURE(Fixture, "intersection_of_tables_with_never_properties") ScopedFastFlag sffs[]{ {"LuauSubtypeNormalizer", true}, {"LuauTypeNormalization2", true}, - {"LuauUninhabitedSubAnything", true}, + {"LuauUninhabitedSubAnything2", true}, }; CheckResult result = check(R"( diff --git a/tests/TypeInfer.negations.test.cpp b/tests/TypeInfer.negations.test.cpp index e8256f974..0e7fb03de 100644 --- a/tests/TypeInfer.negations.test.cpp +++ b/tests/TypeInfer.negations.test.cpp @@ -13,8 +13,7 @@ namespace struct NegationFixture : Fixture { TypeArena arena; - ScopedFastFlag sff[2]{ - {"LuauNegatedStringSingletons", true}, + ScopedFastFlag sff[1]{ {"LuauSubtypeNormalizer", true}, }; diff --git a/tests/TypeInfer.operators.test.cpp b/tests/TypeInfer.operators.test.cpp index 21806082f..93d7361bf 100644 --- a/tests/TypeInfer.operators.test.cpp +++ b/tests/TypeInfer.operators.test.cpp @@ -25,8 +25,16 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types") local x:string|number = s )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(toString(*requireType("s")), "number | string"); - CHECK_EQ(toString(*requireType("x")), "number | string"); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ(toString(*requireType("s")), "(string & ~(false?)) | number"); + CHECK_EQ(toString(*requireType("x")), "number | string"); + } + else + { + CHECK_EQ(toString(*requireType("s")), "number | string"); + CHECK_EQ(toString(*requireType("x")), "number | string"); + } } TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras") @@ -37,8 +45,16 @@ TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_extras") local y = x or "s" )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(toString(*requireType("s")), "number | string"); - CHECK_EQ(toString(*requireType("y")), "number | string"); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ(toString(*requireType("s")), "(string & ~(false?)) | number"); + CHECK_EQ(toString(*requireType("y")), "((number | string) & ~(false?)) | string"); + } + else + { + CHECK_EQ(toString(*requireType("s")), "number | string"); + CHECK_EQ(toString(*requireType("y")), "number | string"); + } } TEST_CASE_FIXTURE(Fixture, "or_joins_types_with_no_superfluous_union") @@ -62,7 +78,14 @@ TEST_CASE_FIXTURE(Fixture, "and_does_not_always_add_boolean") local x:boolean|number = s )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(toString(*requireType("s")), "number"); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ(toString(*requireType("s")), "((false?) & string) | number"); + } + else + { + CHECK_EQ(toString(*requireType("s")), "number"); + } } TEST_CASE_FIXTURE(Fixture, "and_adds_boolean_no_superfluous_union") @@ -81,7 +104,14 @@ TEST_CASE_FIXTURE(Fixture, "and_or_ternary") local s = (1/2) > 0.5 and "a" or 10 )"); LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ(toString(*requireType("s")), "number | string"); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ(toString(*requireType("s")), "((((false?) & boolean) | string) & ~(false?)) | number"); + } + else + { + CHECK_EQ(toString(*requireType("s")), "number | string"); + } } TEST_CASE_FIXTURE(BuiltinsFixture, "primitive_arith_no_metatable") @@ -405,11 +435,9 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "compound_assign_mismatch_metatable") local v2: V2 = setmetatable({ x = 3, y = 4 }, VMT) v1 %= v2 )"); - LUAU_REQUIRE_ERROR_COUNT(1, result); - TypeMismatch* tm = get(result.errors[0]); - CHECK_EQ(*tm->wantedType, *requireType("v2")); - CHECK_EQ(*tm->givenType, *typeChecker.numberType); + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK("Type 'number' could not be converted into 'V2'" == toString(result.errors[0])); } TEST_CASE_FIXTURE(Fixture, "CallOrOfFunctions") @@ -781,7 +809,14 @@ local b: number = 1 or a TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); CHECK_EQ(typeChecker.numberType, tm->wantedType); - CHECK_EQ("number?", toString(tm->givenType)); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ("((number & ~(false?)) | number)?", toString(tm->givenType)); + } + else + { + CHECK_EQ("number?", toString(tm->givenType)); + } } TEST_CASE_FIXTURE(Fixture, "operator_eq_verifies_types_do_intersect") @@ -842,7 +877,14 @@ TEST_CASE_FIXTURE(Fixture, "refine_and_or") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ("number", toString(requireType("u"))); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ("((((false?) & ({| x: number? |}?)) | a) & ~(false?)) | number", toString(requireType("u"))); + } + else + { + CHECK_EQ("number", toString(requireType("u"))); + } } TEST_CASE_FIXTURE(Fixture, "infer_any_in_all_modes_when_lhs_is_unknown") @@ -1035,10 +1077,10 @@ local w = c and 1 if (FFlag::DebugLuauDeferredConstraintResolution) { - CHECK("number?" == toString(requireType("x"))); - CHECK("number" == toString(requireType("y"))); - CHECK("false | number" == toString(requireType("z"))); - CHECK("number" == toString(requireType("w"))); // Normalizer considers free & falsy == never + CHECK("((false?) & (number?)) | number" == toString(requireType("x"))); + CHECK("((false?) & string) | number" == toString(requireType("y"))); + CHECK("((false?) & boolean) | number" == toString(requireType("z"))); + CHECK("((false?) & a) | number" == toString(requireType("w"))); } else { @@ -1073,12 +1115,12 @@ local f1 = f or 'f' if (FFlag::DebugLuauDeferredConstraintResolution) { - CHECK("number | string" == toString(requireType("a1"))); - CHECK("number" == toString(requireType("b1"))); - CHECK("string | true" == toString(requireType("c1"))); - CHECK("string | true" == toString(requireType("d1"))); - CHECK("string" == toString(requireType("e1"))); - CHECK("string" == toString(requireType("f1"))); + CHECK("((false | number) & ~(false?)) | string" == toString(requireType("a1"))); + CHECK("((number?) & ~(false?)) | number" == toString(requireType("b1"))); + CHECK("(boolean & ~(false?)) | string" == toString(requireType("c1"))); + CHECK("(true & ~(false?)) | string" == toString(requireType("d1"))); + CHECK("(false & ~(false?)) | string" == toString(requireType("e1"))); + CHECK("(nil & ~(false?)) | string" == toString(requireType("f1"))); } else { diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index 259341744..b7408f876 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -9,6 +9,8 @@ using namespace Luau; +LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError) + TEST_SUITE_BEGIN("ProvisionalTests"); // These tests check for behavior that differs from the final behavior we'd @@ -776,4 +778,32 @@ TEST_CASE_FIXTURE(IsSubtypeFixture, "functions_with_mismatching_arity_but_any_is // CHECK(!isSubtype(b, c)); } +TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal") +{ + CheckResult result = check(R"( + local t: {x: number?} = {x = nil} + + if t.x then + local u: {x: number} = t + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + + if (FFlag::LuauTypeMismatchInvarianceInError) + { + CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}' +caused by: + Property 'x' is not compatible. Type 'number?' could not be converted into 'number' in an invariant context)", + toString(result.errors[0])); + } + else + { + CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}' +caused by: + Property 'x' is not compatible. Type 'number?' could not be converted into 'number')", + toString(result.errors[0])); + } +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index e5bc186a0..5a7c8432a 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -8,7 +8,6 @@ #include "doctest.h" LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) -LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError) using namespace Luau; @@ -36,7 +35,7 @@ std::optional> magicFunctionInstanceIsA( return WithPredicate{booleanPack, {IsAPredicate{std::move(*lvalue), expr.location, tfun->type}}}; } -std::vector dcrMagicRefinementInstanceIsA(MagicRefinementContext ctx) +std::vector dcrMagicRefinementInstanceIsA(const MagicRefinementContext& ctx) { if (ctx.callSite->args.size != 1) return {}; @@ -462,35 +461,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_non_binary_expressions_actually_resol LUAU_REQUIRE_NO_ERRORS(result); } -TEST_CASE_FIXTURE(Fixture, "assign_table_with_refined_property_with_a_similar_type_is_illegal") -{ - CheckResult result = check(R"( - local t: {x: number?} = {x = nil} - - if t.x then - local u: {x: number} = t - end - )"); - - LUAU_REQUIRE_ERROR_COUNT(1, result); - - if (FFlag::LuauTypeMismatchInvarianceInError) - { - CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}' -caused by: - Property 'x' is not compatible. Type 'number?' could not be converted into 'number' in an invariant context)", - toString(result.errors[0])); - } - else - { - CHECK_EQ(R"(Type '{| x: number? |}' could not be converted into '{| x: number |}' -caused by: - Property 'x' is not compatible. Type 'number?' could not be converted into 'number')", - toString(result.errors[0])); - } -} - - TEST_CASE_FIXTURE(Fixture, "lvalue_is_equal_to_another_lvalue") { CheckResult result = check(R"( @@ -1009,8 +979,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "assert_a_to_be_truthy_then_assert_a_to_be_nu LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ("number | string", toString(requireTypeAtPosition({3, 18}))); - CHECK_EQ("number", toString(requireTypeAtPosition({5, 18}))); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ("((number | string)?) & ~(false?)", toString(requireTypeAtPosition({3, 18}))); + CHECK_EQ("((number | string)?) & ~(false?) & number", toString(requireTypeAtPosition({5, 18}))); + } + else + { + CHECK_EQ("number | string", toString(requireTypeAtPosition({3, 18}))); + CHECK_EQ("number", toString(requireTypeAtPosition({5, 18}))); + } } TEST_CASE_FIXTURE(BuiltinsFixture, "merge_should_be_fully_agnostic_of_hashmap_ordering") @@ -1031,7 +1009,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "merge_should_be_fully_agnostic_of_hashmap_or if (FFlag::DebugLuauDeferredConstraintResolution) { - CHECK_EQ("(string | {| x: string |}) & string", toString(requireTypeAtPosition({6, 28}))); + CHECK_EQ("(never | string) & (string | {| x: string |}) & string", toString(requireTypeAtPosition({6, 28}))); } else { @@ -1075,8 +1053,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "is_truthy_constraint_ifelse_expression") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ("string", toString(requireTypeAtPosition({2, 29}))); - CHECK_EQ("nil", toString(requireTypeAtPosition({2, 45}))); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ("(string?) & ~(false?)", toString(requireTypeAtPosition({2, 29}))); + CHECK_EQ("(string?) & ~~(false?)", toString(requireTypeAtPosition({2, 45}))); + } + else + { + CHECK_EQ("string", toString(requireTypeAtPosition({2, 29}))); + CHECK_EQ("nil", toString(requireTypeAtPosition({2, 45}))); + } } TEST_CASE_FIXTURE(BuiltinsFixture, "invert_is_truthy_constraint_ifelse_expression") @@ -1089,8 +1075,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "invert_is_truthy_constraint_ifelse_expressio LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ("nil", toString(requireTypeAtPosition({2, 42}))); - CHECK_EQ("string", toString(requireTypeAtPosition({2, 50}))); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ("(string?) & ~~(false?)", toString(requireTypeAtPosition({2, 42}))); + CHECK_EQ("(string?) & ~(false?)", toString(requireTypeAtPosition({2, 50}))); + } + else + { + CHECK_EQ("nil", toString(requireTypeAtPosition({2, 42}))); + CHECK_EQ("string", toString(requireTypeAtPosition({2, 50}))); + } } TEST_CASE_FIXTURE(BuiltinsFixture, "type_comparison_ifelse_expression") @@ -1107,8 +1101,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_comparison_ifelse_expression") LUAU_REQUIRE_NO_ERRORS(result); - CHECK_EQ("number", toString(requireTypeAtPosition({6, 49}))); - CHECK_EQ("any", toString(requireTypeAtPosition({6, 66}))); + if (FFlag::DebugLuauDeferredConstraintResolution) + { + CHECK_EQ("any & number", toString(requireTypeAtPosition({6, 49}))); + CHECK_EQ("any & ~number", toString(requireTypeAtPosition({6, 66}))); + } + else + { + CHECK_EQ("number", toString(requireTypeAtPosition({6, 49}))); + CHECK_EQ("any", toString(requireTypeAtPosition({6, 66}))); + } } TEST_CASE_FIXTURE(BuiltinsFixture, "correctly_lookup_a_shadowed_local_that_which_was_previously_refined") diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index c94ed1f9a..c379559dc 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -17,8 +17,8 @@ using namespace Luau; LUAU_FASTFLAG(LuauLowerBoundsCalculation); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAG(LuauNoMoreGlobalSingletonTypes) LUAU_FASTFLAG(LuauTypeMismatchInvarianceInError) +LUAU_FASTFLAG(LuauNewLibraryTypeNames) TEST_SUITE_BEGIN("TableTests"); @@ -1723,8 +1723,6 @@ TEST_CASE_FIXTURE(Fixture, "hide_table_error_properties") TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_table_names") { - ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true}; - CheckResult result = check(R"( os.h = 2 string.k = 3 @@ -1732,7 +1730,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_table_names") LUAU_REQUIRE_ERROR_COUNT(2, result); - if (FFlag::LuauNoMoreGlobalSingletonTypes) + if (FFlag::LuauNewLibraryTypeNames) { CHECK_EQ("Cannot add property 'h' to table 'typeof(os)'", toString(result.errors[0])); CHECK_EQ("Cannot add property 'k' to table 'typeof(string)'", toString(result.errors[1])); @@ -1746,22 +1744,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_table_names") TEST_CASE_FIXTURE(BuiltinsFixture, "persistent_sealed_table_is_immutable") { - ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true}; - CheckResult result = check(R"( --!nonstrict function os:bad() end )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::LuauNoMoreGlobalSingletonTypes) - { + if (FFlag::LuauNewLibraryTypeNames) CHECK_EQ("Cannot add property 'bad' to table 'typeof(os)'", toString(result.errors[0])); - } else - { CHECK_EQ("Cannot add property 'bad' to table 'os'", toString(result.errors[0])); - } const TableTypeVar* osType = get(requireType("os")); REQUIRE(osType != nullptr); @@ -3238,7 +3230,8 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_a_subtype_of_a_compatible_polymorphic_shap TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type") { ScopedFastFlag sff{"LuauScalarShapeSubtyping", true}; - ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true}; + if (!FFlag::LuauNewLibraryTypeNames) + return; CheckResult result = check(R"( local function f(s) @@ -3252,40 +3245,20 @@ TEST_CASE_FIXTURE(Fixture, "scalar_is_not_a_subtype_of_a_compatible_polymorphic_ LUAU_REQUIRE_ERROR_COUNT(3, result); - if (FFlag::LuauNoMoreGlobalSingletonTypes) - { - CHECK_EQ(R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' + CHECK_EQ(R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' caused by: The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[0])); - CHECK_EQ(R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' + toString(result.errors[0])); + CHECK_EQ(R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' caused by: The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[1])); - CHECK_EQ(R"(Type '"bar" | "baz"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' + toString(result.errors[1])); + CHECK_EQ(R"(Type '"bar" | "baz"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' caused by: Not all union options are compatible. Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' caused by: The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[2])); - } - else - { - CHECK_EQ(R"(Type 'string' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' -caused by: - The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[0])); - CHECK_EQ(R"(Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' -caused by: - The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[1])); - CHECK_EQ(R"(Type '"bar" | "baz"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' -caused by: - Not all union options are compatible. Type '"bar"' could not be converted into 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' -caused by: - The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {- absolutely_no_scalar_has_this_method: (t1) -> (a...) -}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[2])); - } + toString(result.errors[2])); } TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compatible") @@ -3307,7 +3280,8 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_can_turn_into_a_scalar_if_it_is_compati TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_compatible") { ScopedFastFlag sff{"LuauScalarShapeSubtyping", true}; - ScopedFastFlag luauNewLibraryTypeNames{"LuauNewLibraryTypeNames", true}; + if (!FFlag::LuauNewLibraryTypeNames) + return; CheckResult result = check(R"( local function f(s): string @@ -3317,22 +3291,11 @@ TEST_CASE_FIXTURE(Fixture, "a_free_shape_cannot_turn_into_a_scalar_if_it_is_not_ )"); LUAU_REQUIRE_ERROR_COUNT(1, result); - if (FFlag::LuauNoMoreGlobalSingletonTypes) - { - CHECK_EQ(R"(Type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' could not be converted into 'string' + CHECK_EQ(R"(Type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' could not be converted into 'string' caused by: The former's metatable does not satisfy the requirements. Table type 'typeof(string)' not compatible with type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[0])); - CHECK_EQ("(t1) -> string where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}", toString(requireType("f"))); - } - else - { - CHECK_EQ(R"(Type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' could not be converted into 'string' -caused by: - The former's metatable does not satisfy the requirements. Table type 'string' not compatible with type 't1 where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}' because the former is missing field 'absolutely_no_scalar_has_this_method')", - toString(result.errors[0])); - CHECK_EQ("(t1) -> string where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}", toString(requireType("f"))); - } + toString(result.errors[0])); + CHECK_EQ("(t1) -> string where t1 = {+ absolutely_no_scalar_has_this_method: (t1) -> (a, b...) +}", toString(requireType("f"))); } TEST_CASE_FIXTURE(BuiltinsFixture, "a_free_shape_can_turn_into_a_scalar_directly") diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 5cc07a286..b1abdf7c9 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -145,7 +145,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "uninhabited_table_sub_never") ScopedFastFlag sffs[]{ {"LuauSubtypeNormalizer", true}, {"LuauTypeNormalization2", true}, - {"LuauUninhabitedSubAnything", true}, + {"LuauUninhabitedSubAnything2", true}, }; CheckResult result = check(R"( @@ -161,7 +161,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "uninhabited_table_sub_anything") ScopedFastFlag sffs[]{ {"LuauSubtypeNormalizer", true}, {"LuauTypeNormalization2", true}, - {"LuauUninhabitedSubAnything", true}, + {"LuauUninhabitedSubAnything2", true}, }; CheckResult result = check(R"( diff --git a/tools/faillist.txt b/tools/faillist.txt index 6f49db84c..5d6779f48 100644 --- a/tools/faillist.txt +++ b/tools/faillist.txt @@ -27,6 +27,7 @@ AutocompleteTest.do_wrong_compatible_self_calls AutocompleteTest.keyword_methods AutocompleteTest.no_incompatible_self_calls AutocompleteTest.no_wrong_compatible_self_calls_with_generics +AutocompleteTest.string_singleton_as_table_key AutocompleteTest.suggest_external_module_type AutocompleteTest.suggest_table_keys AutocompleteTest.type_correct_argument_type_suggestion @@ -88,7 +89,6 @@ DefinitionTests.class_definition_string_props DefinitionTests.declaring_generic_functions DefinitionTests.definition_file_classes FrontendTest.environments -FrontendTest.imported_table_modification_2 FrontendTest.it_should_be_safe_to_stringify_errors_when_full_type_graph_is_discarded FrontendTest.nocheck_cycle_used_by_checked FrontendTest.reexport_cyclic_type @@ -96,7 +96,6 @@ FrontendTest.trace_requires_in_nonstrict_mode GenericsTests.apply_type_function_nested_generics1 GenericsTests.apply_type_function_nested_generics2 GenericsTests.better_mismatch_error_messages -GenericsTests.calling_self_generic_methods GenericsTests.check_generic_typepack_function GenericsTests.check_mutual_generic_functions GenericsTests.correctly_instantiate_polymorphic_member_functions @@ -113,7 +112,6 @@ GenericsTests.higher_rank_polymorphism_should_not_accept_instantiated_arguments GenericsTests.infer_generic_function_function_argument GenericsTests.infer_generic_function_function_argument_overloaded GenericsTests.infer_generic_lib_function_function_argument -GenericsTests.infer_generic_methods GenericsTests.infer_generic_property GenericsTests.instantiated_function_argument_names GenericsTests.instantiation_sharing_types @@ -147,6 +145,7 @@ ParseErrorRecovery.generic_type_list_recovery ParseErrorRecovery.recovery_of_parenthesized_expressions ParserTests.parse_nesting_based_end_detection_failsafe_earlier ParserTests.parse_nesting_based_end_detection_local_function +ProvisionalTests.assign_table_with_refined_property_with_a_similar_type_is_illegal ProvisionalTests.bail_early_if_unification_is_too_complicated ProvisionalTests.discriminate_from_x_not_equal_to_nil ProvisionalTests.do_not_ice_when_trying_to_pick_first_of_generic_type_pack @@ -163,26 +162,16 @@ ProvisionalTests.typeguard_inference_incomplete ProvisionalTests.weirditer_should_not_loop_forever ProvisionalTests.while_body_are_also_refined RefinementTest.apply_refinements_on_astexprindexexpr_whose_subscript_expr_is_constant_string -RefinementTest.assert_a_to_be_truthy_then_assert_a_to_be_number -RefinementTest.assert_non_binary_expressions_actually_resolve_constraints -RefinementTest.assign_table_with_refined_property_with_a_similar_type_is_illegal RefinementTest.call_an_incompatible_function_after_using_typeguard -RefinementTest.correctly_lookup_property_whose_base_was_previously_refined RefinementTest.correctly_lookup_property_whose_base_was_previously_refined2 RefinementTest.discriminate_on_properties_of_disjoint_tables_where_that_property_is_true_or_false RefinementTest.discriminate_tag RefinementTest.else_with_no_explicit_expression_should_also_refine_the_tagged_union RefinementTest.falsiness_of_TruthyPredicate_narrows_into_nil -RefinementTest.fuzz_filtered_refined_types_are_followed -RefinementTest.index_on_a_refined_property -RefinementTest.invert_is_truthy_constraint_ifelse_expression -RefinementTest.is_truthy_constraint_ifelse_expression RefinementTest.narrow_property_of_a_bounded_variable RefinementTest.nonoptional_type_can_narrow_to_nil_if_sense_is_true -RefinementTest.not_t_or_some_prop_of_t RefinementTest.refine_a_property_not_to_be_nil_through_an_intersection_table RefinementTest.refine_unknowns -RefinementTest.type_comparison_ifelse_expression RefinementTest.type_guard_can_filter_for_intersection_of_tables RefinementTest.type_guard_narrowed_into_nothingness RefinementTest.type_narrow_for_all_the_userdata @@ -199,18 +188,18 @@ TableTests.access_index_metamethod_that_returns_variadic TableTests.accidentally_checked_prop_in_opposite_branch TableTests.builtin_table_names TableTests.call_method +TableTests.call_method_with_explicit_self_argument TableTests.cannot_augment_sealed_table TableTests.casting_sealed_tables_with_props_into_table_with_indexer TableTests.casting_tables_with_props_into_table_with_indexer3 TableTests.casting_tables_with_props_into_table_with_indexer4 TableTests.checked_prop_too_early -TableTests.defining_a_method_for_a_builtin_sealed_table_must_fail -TableTests.defining_a_method_for_a_local_sealed_table_must_fail -TableTests.defining_a_self_method_for_a_builtin_sealed_table_must_fail -TableTests.defining_a_self_method_for_a_local_sealed_table_must_fail +TableTests.defining_a_method_for_a_local_unsealed_table_is_ok +TableTests.defining_a_self_method_for_a_local_unsealed_table_is_ok TableTests.dont_crash_when_setmetatable_does_not_produce_a_metatabletypevar TableTests.dont_hang_when_trying_to_look_up_in_cyclic_metatable_index TableTests.dont_quantify_table_that_belongs_to_outer_scope +TableTests.dont_seal_an_unsealed_table_by_passing_it_to_a_function_that_takes_a_sealed_table TableTests.dont_suggest_exact_match_keys TableTests.error_detailed_metatable_prop TableTests.expected_indexer_from_table_union @@ -235,12 +224,11 @@ TableTests.infer_indexer_from_value_property_in_literal TableTests.inferred_return_type_of_free_table TableTests.inferring_crazy_table_should_also_be_quick TableTests.instantiate_table_cloning_3 +TableTests.instantiate_tables_at_scope_level TableTests.invariant_table_properties_means_instantiating_tables_in_assignment_is_unsound TableTests.invariant_table_properties_means_instantiating_tables_in_call_is_unsound TableTests.leaking_bad_metatable_errors TableTests.less_exponential_blowup_please -TableTests.meta_add -TableTests.meta_add_both_ways TableTests.meta_add_inferred TableTests.metatable_mismatch_should_fail TableTests.missing_metatable_for_sealed_tables_do_not_get_inferred @@ -253,7 +241,6 @@ TableTests.oop_indexer_works TableTests.oop_polymorphic TableTests.open_table_unification_2 TableTests.persistent_sealed_table_is_immutable -TableTests.prop_access_on_key_whose_types_mismatches TableTests.property_lookup_through_tabletypevar_metatable TableTests.quantify_even_that_table_was_never_exported_at_all TableTests.quantify_metatables_of_metatables_of_table @@ -267,6 +254,7 @@ TableTests.shared_selfs TableTests.shared_selfs_from_free_param TableTests.shared_selfs_through_metatables TableTests.table_call_metamethod_basic +TableTests.table_function_check_use_after_free TableTests.table_indexing_error_location TableTests.table_insert_should_cope_with_optional_properties_in_nonstrict TableTests.table_insert_should_cope_with_optional_properties_in_strict @@ -279,13 +267,17 @@ TableTests.tables_get_names_from_their_locals TableTests.tc_member_function TableTests.tc_member_function_2 TableTests.unification_of_unions_in_a_self_referential_type +TableTests.unifying_tables_shouldnt_uaf1 TableTests.unifying_tables_shouldnt_uaf2 +TableTests.used_colon_correctly TableTests.used_colon_instead_of_dot TableTests.used_dot_instead_of_colon +TableTests.used_dot_instead_of_colon_but_correctly ToDot.bound_table ToDot.function ToDot.table ToString.exhaustive_toString_of_cyclic_table +ToString.function_type_with_argument_names_and_self ToString.function_type_with_argument_names_generic ToString.toStringDetailed2 ToString.toStringErrorPack @@ -303,6 +295,7 @@ TryUnifyTests.typepack_unification_should_trim_free_tails TryUnifyTests.variadics_should_use_reversed_properly TypeAliases.cannot_create_cyclic_type_with_unknown_module TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any +TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any_2 TypeAliases.generic_param_remap TypeAliases.mismatched_generic_type_param TypeAliases.mutually_recursive_types_restriction_not_ok_1 @@ -322,6 +315,7 @@ TypeInfer.checking_should_not_ice TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error TypeInfer.dont_report_type_errors_within_an_AstExprError TypeInfer.dont_report_type_errors_within_an_AstStatError +TypeInfer.follow_on_new_types_in_substitution TypeInfer.fuzz_free_table_type_change_during_index_check TypeInfer.globals TypeInfer.globals2 @@ -335,11 +329,13 @@ TypeInfer.tc_interpolated_string_with_invalid_expression TypeInfer.type_infer_recursion_limit_no_ice TypeInfer.type_infer_recursion_limit_normalizer TypeInferAnyError.for_in_loop_iterator_is_any2 +TypeInferAnyError.metatable_of_any_can_be_a_table TypeInferClasses.can_read_prop_of_base_class_using_string TypeInferClasses.class_type_mismatch_with_name_conflict TypeInferClasses.classes_without_overloaded_operators_cannot_be_added TypeInferClasses.detailed_class_unification_error TypeInferClasses.higher_order_function_arguments_are_contravariant +TypeInferClasses.index_instance_property TypeInferClasses.optional_class_field_access_error TypeInferClasses.table_class_unification_reports_sane_errors_for_missing_properties TypeInferClasses.warn_when_prop_almost_matches @@ -349,7 +345,9 @@ TypeInferFunctions.calling_function_with_incorrect_argument_type_yields_errors_s TypeInferFunctions.cannot_hoist_interior_defns_into_signature TypeInferFunctions.dont_give_other_overloads_message_if_only_one_argument_matching_overload_exists TypeInferFunctions.dont_infer_parameter_types_for_functions_from_their_call_site +TypeInferFunctions.dont_mutate_the_underlying_head_of_typepack_when_calling_with_self TypeInferFunctions.duplicate_functions_with_different_signatures_not_allowed_in_nonstrict +TypeInferFunctions.first_argument_can_be_optional TypeInferFunctions.function_cast_error_uses_correct_language TypeInferFunctions.function_decl_non_self_sealed_overwrite_2 TypeInferFunctions.function_decl_non_self_unsealed_overwrite @@ -387,36 +385,45 @@ TypeInferLoops.loop_iter_no_indexer_nonstrict TypeInferLoops.loop_iter_trailing_nil TypeInferLoops.properly_infer_iteratee_is_a_free_table TypeInferLoops.unreachable_code_after_infinite_loop +TypeInferLoops.varlist_declared_by_for_in_loop_should_be_free +TypeInferModules.bound_free_table_export_is_ok TypeInferModules.custom_require_global -TypeInferModules.do_not_modify_imported_types +TypeInferModules.do_not_modify_imported_types_4 +TypeInferModules.do_not_modify_imported_types_5 TypeInferModules.module_type_conflict TypeInferModules.module_type_conflict_instantiated TypeInferModules.require_a_variadic_function TypeInferModules.type_error_of_unknown_qualified_type -TypeInferOOP.CheckMethodsOfSealed TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_another_overload_works TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_it_wont_help_2 TypeInferOOP.dont_suggest_using_colon_rather_than_dot_if_not_defined_with_colon TypeInferOOP.inferring_hundreds_of_self_calls_should_not_suffocate_memory +TypeInferOOP.method_depends_on_table TypeInferOOP.methods_are_topologically_sorted +TypeInferOOP.nonstrict_self_mismatch_tail TypeInferOOP.object_constructor_can_refer_to_method_of_self +TypeInferOOP.table_oop +TypeInferOperators.CallAndOrOfFunctions +TypeInferOperators.CallOrOfFunctions TypeInferOperators.cannot_compare_tables_that_do_not_have_the_same_metatable TypeInferOperators.cannot_indirectly_compare_types_that_do_not_have_a_metatable TypeInferOperators.cannot_indirectly_compare_types_that_do_not_offer_overloaded_ordering_operators TypeInferOperators.cli_38355_recursive_union +TypeInferOperators.compound_assign_metatable TypeInferOperators.compound_assign_mismatch_metatable TypeInferOperators.compound_assign_mismatch_op TypeInferOperators.compound_assign_mismatch_result TypeInferOperators.disallow_string_and_types_without_metatables_from_arithmetic_binary_ops TypeInferOperators.in_nonstrict_mode_strip_nil_from_intersections_when_considering_relational_operators TypeInferOperators.infer_any_in_all_modes_when_lhs_is_unknown -TypeInferOperators.mm_comparisons_must_return_a_boolean -TypeInferOperators.mm_ops_must_return_a_value +TypeInferOperators.operator_eq_completely_incompatible +TypeInferOperators.or_joins_types_with_no_superfluous_union TypeInferOperators.produce_the_correct_error_message_when_comparing_a_table_with_a_metatable_with_one_that_does_not -TypeInferOperators.refine_and_or TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection TypeInferOperators.typecheck_overloaded_multiply_that_is_an_intersection_on_rhs TypeInferOperators.UnknownGlobalCompoundAssign +TypeInferOperators.unrelated_classes_cannot_be_compared +TypeInferOperators.unrelated_primitives_cannot_be_compared TypeInferPrimitives.CheckMethodsOfNumber TypeInferPrimitives.string_index TypeInferUnknownNever.assign_to_global_which_is_never @@ -432,6 +439,7 @@ TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable2 TypeInferUnknownNever.unary_minus_of_never TypePackTests.detect_cyclic_typepacks2 TypePackTests.pack_tail_unification_check +TypePackTests.self_and_varargs_should_work TypePackTests.type_alias_backwards_compatible TypePackTests.type_alias_default_export TypePackTests.type_alias_default_mixed_self