From d19a5f06991ac9e723055f5b2bbef7724fd203cf Mon Sep 17 00:00:00 2001 From: ayoungbloodrbx Date: Fri, 22 Nov 2024 13:00:51 -0800 Subject: [PATCH] Sync to upstream/release/653 (#1541) ## What's Changed? * Optimized the vector dot product by up to 24% * Allow for x/y/z/X/Y/Z vector field access by registering a `vector` metatable with an `__index` method (Fixes #1521) * Fixed a bug preventing consistent recovery from parse errors in table types. * Optimized `k*n` and `k+n` when types are known * Allow fragment autocomplete to handle cases like the automatic insertion of parens, keywords, strings, etc., while maintaining a correct relative positioning ### New Solver * Allow for `nil` assignment to tables and classes with indexers --------- Co-authored-by: Aaron Weiss Co-authored-by: Andy Friesen Co-authored-by: Aviral Goel Co-authored-by: Hunter Goldstein Co-authored-by: Varun Saini Co-authored-by: Vighnesh Vijay Co-authored-by: Vyacheslav Egorov --- Analysis/include/Luau/ConstraintGenerator.h | 16 +- Analysis/include/Luau/ConstraintSolver.h | 24 ++- Analysis/include/Luau/DataFlowGraph.h | 3 +- Analysis/include/Luau/Instantiation2.h | 2 +- Analysis/include/Luau/Module.h | 3 - Analysis/include/Luau/Type.h | 2 +- Analysis/include/Luau/TypePack.h | 2 +- Analysis/include/Luau/Unifiable.h | 15 +- Analysis/include/Luau/VisitType.h | 5 +- Analysis/src/Clone.cpp | 8 +- Analysis/src/ConstraintGenerator.cpp | 113 +++++++------ Analysis/src/ConstraintSolver.cpp | 62 ++++--- Analysis/src/DataFlowGraph.cpp | 41 +++-- Analysis/src/Differ.cpp | 2 +- Analysis/src/FragmentAutocomplete.cpp | 28 +++- Analysis/src/Frontend.cpp | 59 ++----- Analysis/src/Generalization.cpp | 6 +- Analysis/src/Linter.cpp | 5 +- Analysis/src/NonStrictTypeChecker.cpp | 2 +- Analysis/src/OverloadResolution.cpp | 8 +- Analysis/src/Substitution.cpp | 27 ++- Analysis/src/ToDot.cpp | 2 +- Analysis/src/ToString.cpp | 23 ++- Analysis/src/Type.cpp | 2 +- Analysis/src/TypeAttach.cpp | 4 +- Analysis/src/TypeChecker2.cpp | 5 +- Analysis/src/TypeFunction.cpp | 20 +-- Analysis/src/TypeInfer.cpp | 16 +- Analysis/src/TypeUtils.cpp | 5 +- Analysis/src/Unifiable.cpp | 11 +- Analysis/src/Unifier.cpp | 10 +- Analysis/src/Unifier2.cpp | 2 +- Ast/src/Ast.cpp | 4 - Ast/src/Parser.cpp | 27 ++- CodeGen/include/Luau/IrUtils.h | 5 + CodeGen/src/AssemblyBuilderA64.cpp | 3 + CodeGen/src/AssemblyBuilderX64.cpp | 3 + CodeGen/src/BytecodeSummary.cpp | 7 +- CodeGen/src/CodeGenAssembly.cpp | 7 +- CodeGen/src/CodeGenContext.cpp | 6 +- CodeGen/src/CodeGenLower.h | 20 --- CodeGen/src/IrDump.cpp | 3 + CodeGen/src/IrLoweringA64.cpp | 4 + CodeGen/src/IrLoweringX64.cpp | 4 + CodeGen/src/IrUtils.cpp | 3 + CodeGen/src/OptimizeConstProp.cpp | 4 + Compiler/src/Compiler.cpp | 7 +- Config/include/Luau/Config.h | 2 +- Config/src/Config.cpp | 14 +- VM/src/lveclib.cpp | 58 +++++++ tests/AssemblyBuilderA64.test.cpp | 4 + tests/AssemblyBuilderX64.test.cpp | 4 + tests/Conformance.test.cpp | 9 +- tests/Fixture.cpp | 12 +- tests/Fixture.h | 2 +- tests/FragmentAutocomplete.test.cpp | 174 ++++++++++++++++++-- tests/Linter.test.cpp | 3 +- tests/Parser.test.cpp | 19 ++- tests/ToDot.test.cpp | 2 +- tests/ToString.test.cpp | 3 +- tests/TypeFunction.test.cpp | 1 - tests/TypeFunction.user.test.cpp | 62 ------- tests/TypeInfer.aliases.test.cpp | 17 +- tests/TypeInfer.refinements.test.cpp | 5 +- tests/TypeInfer.tables.test.cpp | 124 +++++++++++++- tests/TypePack.test.cpp | 4 +- tests/conformance/vector_library.lua | 31 ++++ 67 files changed, 783 insertions(+), 407 deletions(-) diff --git a/Analysis/include/Luau/ConstraintGenerator.h b/Analysis/include/Luau/ConstraintGenerator.h index b3b35fc21..3047b9053 100644 --- a/Analysis/include/Luau/ConstraintGenerator.h +++ b/Analysis/include/Luau/ConstraintGenerator.h @@ -295,11 +295,25 @@ struct ConstraintGenerator Inference check(const ScopePtr& scope, AstExprFunction* func, std::optional expectedType, bool generalize); Inference check(const ScopePtr& scope, AstExprUnary* unary); Inference check(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType); + Inference checkAstExprBinary( + const ScopePtr& scope, + const Location& location, + AstExprBinary::Op op, + AstExpr* left, + AstExpr* right, + std::optional expectedType + ); Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional expectedType); Inference check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert); Inference check(const ScopePtr& scope, AstExprInterpString* interpString); Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional expectedType); - std::tuple checkBinary(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType); + std::tuple checkBinary( + const ScopePtr& scope, + AstExprBinary::Op op, + AstExpr* left, + AstExpr* right, + std::optional expectedType + ); void visitLValue(const ScopePtr& scope, AstExpr* expr, TypeId rhsType); void visitLValue(const ScopePtr& scope, AstExprLocal* local, TypeId rhsType); diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index 37042c757..ceb9cab41 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -59,6 +59,25 @@ struct HashInstantiationSignature size_t operator()(const InstantiationSignature& signature) const; }; + +struct TablePropLookupResult +{ + // What types are we blocked on for determining this type? + std::vector blockedTypes; + // The type of the property (if we were able to determine it). + std::optional propType; + // Whether or not this is _definitely_ derived as the result of an indexer. + // We use this to determine whether or not code like: + // + // t.lol = nil; + // + // ... is legal. If `t: { [string]: ~nil }` then this is legal as + // there's no guarantee on whether "lol" specifically exists. + // However, if `t: { lol: ~nil }`, then we cannot allow assignment as + // that would remove "lol" from the table entirely. + bool isIndex = false; +}; + struct ConstraintSolver { NotNull arena; @@ -211,7 +230,7 @@ struct ConstraintSolver // for a, ... in next_function, t, ... do bool tryDispatchIterableFunction(TypeId nextTy, TypeId tableTy, const IterableConstraint& c, NotNull constraint); - std::pair, std::optional> lookupTableProp( + TablePropLookupResult lookupTableProp( NotNull constraint, TypeId subjectType, const std::string& propName, @@ -219,7 +238,8 @@ struct ConstraintSolver bool inConditional = false, bool suppressSimplification = false ); - std::pair, std::optional> lookupTableProp( + + TablePropLookupResult lookupTableProp( NotNull constraint, TypeId subjectType, const std::string& propName, diff --git a/Analysis/include/Luau/DataFlowGraph.h b/Analysis/include/Luau/DataFlowGraph.h index b3fc8f05f..83dfa4b79 100644 --- a/Analysis/include/Luau/DataFlowGraph.h +++ b/Analysis/include/Luau/DataFlowGraph.h @@ -84,7 +84,6 @@ struct DfgScope DfgScope* parent; ScopeType scopeType; - Location location; using Bindings = DenseHashMap; using Props = DenseHashMap>; @@ -156,7 +155,7 @@ struct DataFlowGraphBuilder DenseHashMap captures{Symbol{}}; void resolveCaptures(); - DfgScope* makeChildScope(Location loc, DfgScope::ScopeType scopeType = DfgScope::Linear); + DfgScope* makeChildScope(DfgScope::ScopeType scopeType = DfgScope::Linear); void join(DfgScope* p, DfgScope* a, DfgScope* b); void joinBindings(DfgScope* p, const DfgScope& a, const DfgScope& b); diff --git a/Analysis/include/Luau/Instantiation2.h b/Analysis/include/Luau/Instantiation2.h index c9215fada..ee949388b 100644 --- a/Analysis/include/Luau/Instantiation2.h +++ b/Analysis/include/Luau/Instantiation2.h @@ -53,7 +53,7 @@ struct Replacer : Substitution }; // A substitution which replaces generic functions by monomorphic functions -struct Instantiation2 : Substitution +struct Instantiation2 final : Substitution { // Mapping from generic types to free types to be used in instantiation. DenseHashMap genericSubstitutions{nullptr}; diff --git a/Analysis/include/Luau/Module.h b/Analysis/include/Luau/Module.h index 61877d51b..49b4ae028 100644 --- a/Analysis/include/Luau/Module.h +++ b/Analysis/include/Luau/Module.h @@ -135,9 +135,6 @@ struct Module TypePackId returnType = nullptr; std::unordered_map exportedTypeBindings; - // We also need to keep DFG data alive between runs - std::shared_ptr dataFlowGraph = nullptr; - std::vector> dfgScopes; bool hasModuleScope() const; ScopePtr getModuleScope() const; diff --git a/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index 0005605ee..85957bed3 100644 --- a/Analysis/include/Luau/Type.h +++ b/Analysis/include/Luau/Type.h @@ -762,7 +762,7 @@ struct NegationType TypeId ty; }; -using ErrorType = Unifiable::Error; +using ErrorType = Unifiable::Error; using TypeVariant = Unifiable::Variant< TypeId, diff --git a/Analysis/include/Luau/TypePack.h b/Analysis/include/Luau/TypePack.h index 1065b9475..8509da034 100644 --- a/Analysis/include/Luau/TypePack.h +++ b/Analysis/include/Luau/TypePack.h @@ -52,7 +52,7 @@ struct GenericTypePack }; using BoundTypePack = Unifiable::Bound; -using ErrorTypePack = Unifiable::Error; +using ErrorTypePack = Unifiable::Error; using TypePackVariant = Unifiable::Variant; diff --git a/Analysis/include/Luau/Unifiable.h b/Analysis/include/Luau/Unifiable.h index 79b3b7dea..132eda967 100644 --- a/Analysis/include/Luau/Unifiable.h +++ b/Analysis/include/Luau/Unifiable.h @@ -3,6 +3,7 @@ #include "Luau/Variant.h" +#include #include namespace Luau @@ -94,19 +95,29 @@ struct Bound Id boundTo; }; +template struct Error { // This constructor has to be public, since it's used in Type and TypePack, // but shouldn't be called directly. Please use errorRecoveryType() instead. - Error(); + explicit Error(); + + explicit Error(Id synthetic) + : synthetic{synthetic} + { + } int index; + // This is used to create an error that can be rendered out using this field + // as appropriate metadata for communicating it to the user. + std::optional synthetic; + private: static int nextIndex; }; template -using Variant = Luau::Variant, Error, Value...>; +using Variant = Luau::Variant, Error, Value...>; } // namespace Luau::Unifiable diff --git a/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index 7202c1005..0e5475a71 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/Analysis/include/Luau/VisitType.h @@ -10,7 +10,6 @@ #include "Type.h" LUAU_FASTINT(LuauVisitRecursionLimit) -LUAU_FASTFLAG(LuauBoundLazyTypes2) LUAU_FASTFLAG(LuauSolverV2) namespace Luau @@ -190,7 +189,7 @@ struct GenericTypeVisitor { return visit(tp); } - virtual bool visit(TypePackId tp, const Unifiable::Error& etp) + virtual bool visit(TypePackId tp, const ErrorTypePack& etp) { return visit(tp); } @@ -461,7 +460,7 @@ struct GenericTypeVisitor else if (auto gtv = get(tp)) visit(tp, *gtv); - else if (auto etv = get(tp)) + else if (auto etv = get(tp)) visit(tp, *etv); else if (auto pack = get(tp)) diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index 745a03074..98397fa39 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -257,8 +257,7 @@ class TypeCloner LUAU_ASSERT(!"Item holds neither TypeId nor TypePackId when enqueuing its children?"); } - // ErrorType and ErrorTypePack is an alias to this type. - void cloneChildren(Unifiable::Error* t) + void cloneChildren(ErrorType* t) { // noop. } @@ -428,6 +427,11 @@ class TypeCloner t->boundTo = shallowClone(t->boundTo); } + void cloneChildren(ErrorTypePack* t) + { + // noop. + } + void cloneChildren(VariadicTypePack* t) { t->ty = shallowClone(t->ty); diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 07458279e..ed3d8a6dd 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -69,13 +69,11 @@ struct TypeGuard std::string type; }; -static std::optional matchTypeGuard(const AstExprBinary* binary) +static std::optional matchTypeGuard(const AstExprBinary::Op op, AstExpr* left, AstExpr* right) { - if (binary->op != AstExprBinary::CompareEq && binary->op != AstExprBinary::CompareNe) + if (op != AstExprBinary::CompareEq && op != AstExprBinary::CompareNe) return std::nullopt; - AstExpr* left = binary->left; - AstExpr* right = binary->right; if (right->is()) std::swap(left, right); @@ -1459,8 +1457,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatAssign* ass ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatCompoundAssign* assign) { - AstExprBinary binop = AstExprBinary{assign->location, assign->op, assign->var, assign->value}; - TypeId resultTy = check(scope, &binop).ty; + TypeId resultTy = checkAstExprBinary(scope, assign->location, assign->op, assign->var, assign->value, std::nullopt).ty; module->astCompoundAssignResultTypes[assign] = resultTy; TypeId lhsType = check(scope, assign->var).ty; @@ -2437,63 +2434,75 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary) Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType) { - auto [leftType, rightType, refinement] = checkBinary(scope, binary, expectedType); + return checkAstExprBinary(scope, binary->location, binary->op, binary->left, binary->right, expectedType); +} + +Inference ConstraintGenerator::checkAstExprBinary( + const ScopePtr& scope, + const Location& location, + AstExprBinary::Op op, + AstExpr* left, + AstExpr* right, + std::optional expectedType +) +{ + auto [leftType, rightType, refinement] = checkBinary(scope, op, left, right, expectedType); - switch (binary->op) + switch (op) { case AstExprBinary::Op::Add: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().addFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().addFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Sub: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().subFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().subFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Mul: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().mulFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().mulFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Div: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().divFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().divFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::FloorDiv: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().idivFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().idivFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Pow: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().powFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().powFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Mod: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().modFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().modFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Concat: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().concatFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().concatFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::And: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().andFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().andFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Or: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().orFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().orFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareLt: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().ltFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareGe: @@ -2503,13 +2512,13 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar {rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)` {}, scope, - binary->location + location ); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareLe: { - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().leFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareGt: @@ -2519,15 +2528,15 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar {rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)` {}, scope, - binary->location + location ); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareEq: case AstExprBinary::Op::CompareNe: { - DefId leftDef = dfg->getDef(binary->left); - DefId rightDef = dfg->getDef(binary->right); + DefId leftDef = dfg->getDef(left); + DefId rightDef = dfg->getDef(right); bool leftSubscripted = containsSubscriptedDefinition(leftDef); bool rightSubscripted = containsSubscriptedDefinition(rightDef); @@ -2536,11 +2545,11 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar // we cannot add nil in this case because then we will blindly accept comparisons that we should not. } else if (leftSubscripted) - leftType = makeUnion(scope, binary->location, leftType, builtinTypes->nilType); + leftType = makeUnion(scope, location, leftType, builtinTypes->nilType); else if (rightSubscripted) - rightType = makeUnion(scope, binary->location, rightType, builtinTypes->nilType); + rightType = makeUnion(scope, location, rightType, builtinTypes->nilType); - TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().eqFunc, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFunctionInstance(builtinTypeFunctions().eqFunc, {leftType, rightType}, {}, scope, location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Op__Count: @@ -2586,44 +2595,46 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprInterpString* std::tuple ConstraintGenerator::checkBinary( const ScopePtr& scope, - AstExprBinary* binary, + AstExprBinary::Op op, + AstExpr* left, + AstExpr* right, std::optional expectedType ) { - if (binary->op == AstExprBinary::And) + if (op == AstExprBinary::And) { std::optional relaxedExpectedLhs; if (expectedType) relaxedExpectedLhs = arena->addType(UnionType{{builtinTypes->falsyType, *expectedType}}); - auto [leftType, leftRefinement] = check(scope, binary->left, relaxedExpectedLhs); + auto [leftType, leftRefinement] = check(scope, left, relaxedExpectedLhs); - ScopePtr rightScope = childScope(binary->right, scope); - applyRefinements(rightScope, binary->right->location, leftRefinement); - auto [rightType, rightRefinement] = check(rightScope, binary->right, expectedType); + ScopePtr rightScope = childScope(right, scope); + applyRefinements(rightScope, right->location, leftRefinement); + auto [rightType, rightRefinement] = check(rightScope, right, expectedType); return {leftType, rightType, refinementArena.conjunction(leftRefinement, rightRefinement)}; } - else if (binary->op == AstExprBinary::Or) + else if (op == AstExprBinary::Or) { std::optional relaxedExpectedLhs; if (expectedType) relaxedExpectedLhs = arena->addType(UnionType{{builtinTypes->falsyType, *expectedType}}); - auto [leftType, leftRefinement] = check(scope, binary->left, relaxedExpectedLhs); + auto [leftType, leftRefinement] = check(scope, left, relaxedExpectedLhs); - ScopePtr rightScope = childScope(binary->right, scope); - applyRefinements(rightScope, binary->right->location, refinementArena.negation(leftRefinement)); - auto [rightType, rightRefinement] = check(rightScope, binary->right, expectedType); + ScopePtr rightScope = childScope(right, scope); + applyRefinements(rightScope, right->location, refinementArena.negation(leftRefinement)); + auto [rightType, rightRefinement] = check(rightScope, right, expectedType); return {leftType, rightType, refinementArena.disjunction(leftRefinement, rightRefinement)}; } - else if (auto typeguard = matchTypeGuard(binary)) + else if (auto typeguard = matchTypeGuard(op, left, right)) { - TypeId leftType = check(scope, binary->left).ty; - TypeId rightType = check(scope, binary->right).ty; + TypeId leftType = check(scope, left).ty; + TypeId rightType = check(scope, right).ty; const RefinementKey* key = dfg->getRefinementKey(typeguard->target); if (!key) @@ -2665,24 +2676,24 @@ std::tuple ConstraintGenerator::checkBinary( } RefinementId proposition = refinementArena.proposition(key, discriminantTy); - if (binary->op == AstExprBinary::CompareEq) + if (op == AstExprBinary::CompareEq) return {leftType, rightType, proposition}; - else if (binary->op == AstExprBinary::CompareNe) + else if (op == AstExprBinary::CompareNe) return {leftType, rightType, refinementArena.negation(proposition)}; else ice->ice("matchTypeGuard should only return a Some under `==` or `~=`!"); } - else if (binary->op == AstExprBinary::CompareEq || binary->op == AstExprBinary::CompareNe) + else if (op == AstExprBinary::CompareEq || op == AstExprBinary::CompareNe) { // We are checking a binary expression of the form a op b // Just because a op b is epxected to return a bool, doesn't mean a, b are expected to be bools too - TypeId leftType = check(scope, binary->left, {}, true).ty; - TypeId rightType = check(scope, binary->right, {}, true).ty; + TypeId leftType = check(scope, left, {}, true).ty; + TypeId rightType = check(scope, right, {}, true).ty; - RefinementId leftRefinement = refinementArena.proposition(dfg->getRefinementKey(binary->left), rightType); - RefinementId rightRefinement = refinementArena.proposition(dfg->getRefinementKey(binary->right), leftType); + RefinementId leftRefinement = refinementArena.proposition(dfg->getRefinementKey(left), rightType); + RefinementId rightRefinement = refinementArena.proposition(dfg->getRefinementKey(right), leftType); - if (binary->op == AstExprBinary::CompareNe) + if (op == AstExprBinary::CompareNe) { leftRefinement = refinementArena.negation(leftRefinement); rightRefinement = refinementArena.negation(rightRefinement); @@ -2692,8 +2703,8 @@ std::tuple ConstraintGenerator::checkBinary( } else { - TypeId leftType = check(scope, binary->left).ty; - TypeId rightType = check(scope, binary->right).ty; + TypeId leftType = check(scope, left).ty; + TypeId rightType = check(scope, right).ty; return {leftType, rightType, nullptr}; } } diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 65a1a5113..d18c61cb2 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -34,6 +34,7 @@ LUAU_FASTINTVARIABLE(LuauSolverRecursionLimit, 500) LUAU_FASTFLAGVARIABLE(LuauRemoveNotAnyHack) LUAU_FASTFLAGVARIABLE(DebugLuauEqSatSimplification) LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations) +LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer) namespace Luau { @@ -1501,7 +1502,8 @@ bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNulladdType(UnionType{{propTy, builtinTypes->nilType}}) : propTy + ); unify(constraint, rhsType, propTy); return true; } @@ -1888,7 +1894,12 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNullindexer->indexType); unify(constraint, rhsType, lhsTable->indexer->indexResultType); - bind(constraint, c.propType, lhsTable->indexer->indexResultType); + bind( + constraint, + c.propType, + FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{lhsTable->indexer->indexResultType, builtinTypes->nilType}}) + : lhsTable->indexer->indexResultType + ); return true; } @@ -1937,7 +1948,12 @@ bool ConstraintSolver::tryDispatch(const AssignIndexConstraint& c, NotNullindexer->indexType); unify(constraint, rhsType, lhsClass->indexer->indexResultType); - bind(constraint, c.propType, lhsClass->indexer->indexResultType); + bind( + constraint, + c.propType, + FFlag::LuauAllowNilAssignmentToIndexer ? arena->addType(UnionType{{lhsClass->indexer->indexResultType, builtinTypes->nilType}}) + : lhsClass->indexer->indexResultType + ); return true; } @@ -2360,7 +2376,7 @@ NotNull ConstraintSolver::unpackAndAssign( return c; } -std::pair, std::optional> ConstraintSolver::lookupTableProp( +TablePropLookupResult ConstraintSolver::lookupTableProp( NotNull constraint, TypeId subjectType, const std::string& propName, @@ -2373,7 +2389,7 @@ std::pair, std::optional> ConstraintSolver::lookupTa return lookupTableProp(constraint, subjectType, propName, context, inConditional, suppressSimplification, seen); } -std::pair, std::optional> ConstraintSolver::lookupTableProp( +TablePropLookupResult ConstraintSolver::lookupTableProp( NotNull constraint, TypeId subjectType, const std::string& propName, @@ -2413,7 +2429,7 @@ std::pair, std::optional> ConstraintSolver::lookupTa } if (ttv->indexer && maybeString(ttv->indexer->indexType)) - return {{}, ttv->indexer->indexResultType}; + return {{}, ttv->indexer->indexResultType, /* isIndex = */ true}; if (ttv->state == TableState::Free) { @@ -2455,9 +2471,9 @@ std::pair, std::optional> ConstraintSolver::lookupTa } else if (auto mt = get(subjectType); mt && context == ValueContext::RValue) { - auto [blocked, result] = lookupTableProp(constraint, mt->table, propName, context, inConditional, suppressSimplification, seen); - if (!blocked.empty() || result) - return {blocked, result}; + auto result = lookupTableProp(constraint, mt->table, propName, context, inConditional, suppressSimplification, seen); + if (!result.blockedTypes.empty() || result.propType) + return result; TypeId mtt = follow(mt->metatable); @@ -2467,7 +2483,7 @@ std::pair, std::optional> ConstraintSolver::lookupTa { auto indexProp = metatable->props.find("__index"); if (indexProp == metatable->props.end()) - return {{}, result}; + return {{}, result.propType}; // TODO: __index can be an overloaded function. @@ -2497,7 +2513,7 @@ std::pair, std::optional> ConstraintSolver::lookupTa return {{}, context == ValueContext::RValue ? p->readTy : p->writeTy}; if (ct->indexer) { - return {{}, ct->indexer->indexResultType}; + return {{}, ct->indexer->indexResultType, /* isIndex = */ true}; } } else if (auto pt = get(subjectType); pt && pt->metatable) @@ -2548,10 +2564,10 @@ std::pair, std::optional> ConstraintSolver::lookupTa for (TypeId ty : utv) { - auto [innerBlocked, innerResult] = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen); - blocked.insert(blocked.end(), innerBlocked.begin(), innerBlocked.end()); - if (innerResult) - options.insert(*innerResult); + auto result = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen); + blocked.insert(blocked.end(), result.blockedTypes.begin(), result.blockedTypes.end()); + if (result.propType) + options.insert(*result.propType); } if (!blocked.empty()) @@ -2585,10 +2601,10 @@ std::pair, std::optional> ConstraintSolver::lookupTa for (TypeId ty : itv) { - auto [innerBlocked, innerResult] = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen); - blocked.insert(blocked.end(), innerBlocked.begin(), innerBlocked.end()); - if (innerResult) - options.insert(*innerResult); + auto result = lookupTableProp(constraint, ty, propName, context, inConditional, suppressSimplification, seen); + blocked.insert(blocked.end(), result.blockedTypes.begin(), result.blockedTypes.end()); + if (result.propType) + options.insert(*result.propType); } if (!blocked.empty()) @@ -2920,7 +2936,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l } TypePackId modulePack = module->returnType; - if (get(modulePack)) + if (get(modulePack)) return errorRecoveryType(); std::optional moduleType = first(modulePack); diff --git a/Analysis/src/DataFlowGraph.cpp b/Analysis/src/DataFlowGraph.cpp index 0becd49d2..9925f29ca 100644 --- a/Analysis/src/DataFlowGraph.cpp +++ b/Analysis/src/DataFlowGraph.cpp @@ -184,7 +184,7 @@ DataFlowGraph DataFlowGraphBuilder::build(AstStatBlock* block, NotNulllocation); + DfgScope* moduleScope = builder.makeChildScope(); PushScope ps{builder.scopeStack, moduleScope}; builder.visitBlockWithoutChildScope(block); builder.resolveCaptures(); @@ -208,7 +208,7 @@ std::pair, std::vector> DataFlowGraphBuilder builder; builder.handle = handle; - DfgScope* moduleScope = builder.makeChildScope(block->location); + DfgScope* moduleScope = builder.makeChildScope(); PushScope ps{builder.scopeStack, moduleScope}; builder.visitBlockWithoutChildScope(block); builder.resolveCaptures(); @@ -247,9 +247,9 @@ DfgScope* DataFlowGraphBuilder::currentScope() return scopeStack.back(); } -DfgScope* DataFlowGraphBuilder::makeChildScope(Location loc, DfgScope::ScopeType scopeType) +DfgScope* DataFlowGraphBuilder::makeChildScope(DfgScope::ScopeType scopeType) { - return scopes.emplace_back(new DfgScope{currentScope(), scopeType, loc}).get(); + return scopes.emplace_back(new DfgScope{currentScope(), scopeType}).get(); } void DataFlowGraphBuilder::join(DfgScope* p, DfgScope* a, DfgScope* b) @@ -397,7 +397,7 @@ DefId DataFlowGraphBuilder::lookup(DefId def, const std::string& key) ControlFlow DataFlowGraphBuilder::visit(AstStatBlock* b) { - DfgScope* child = makeChildScope(b->location); + DfgScope* child = makeChildScope(); ControlFlow cf; { @@ -474,8 +474,8 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i) { visitExpr(i->condition); - DfgScope* thenScope = makeChildScope(i->thenbody->location); - DfgScope* elseScope = makeChildScope(i->elsebody ? i->elsebody->location : i->location); + DfgScope* thenScope = makeChildScope(); + DfgScope* elseScope = makeChildScope(); ControlFlow thencf; { @@ -509,7 +509,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatIf* i) ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w) { // TODO(controlflow): entry point has a back edge from exit point - DfgScope* whileScope = makeChildScope(w->location, DfgScope::Loop); + DfgScope* whileScope = makeChildScope(DfgScope::Loop); { PushScope ps{scopeStack, whileScope}; @@ -525,7 +525,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatWhile* w) ControlFlow DataFlowGraphBuilder::visit(AstStatRepeat* r) { // TODO(controlflow): entry point has a back edge from exit point - DfgScope* repeatScope = makeChildScope(r->location, DfgScope::Loop); + DfgScope* repeatScope = makeChildScope(DfgScope::Loop); { PushScope ps{scopeStack, repeatScope}; @@ -601,7 +601,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocal* l) ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f) { - DfgScope* forScope = makeChildScope(f->location, DfgScope::Loop); + DfgScope* forScope = makeChildScope(DfgScope::Loop); visitExpr(f->from); visitExpr(f->to); @@ -630,7 +630,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatFor* f) ControlFlow DataFlowGraphBuilder::visit(AstStatForIn* f) { - DfgScope* forScope = makeChildScope(f->location, DfgScope::Loop); + DfgScope* forScope = makeChildScope(DfgScope::Loop); { PushScope ps{scopeStack, forScope}; @@ -726,7 +726,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatLocalFunction* l) ControlFlow DataFlowGraphBuilder::visit(AstStatTypeAlias* t) { - DfgScope* unreachable = makeChildScope(t->location); + DfgScope* unreachable = makeChildScope(); PushScope ps{scopeStack, unreachable}; visitGenerics(t->generics); @@ -738,7 +738,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatTypeAlias* t) ControlFlow DataFlowGraphBuilder::visit(AstStatTypeFunction* f) { - DfgScope* unreachable = makeChildScope(f->location); + DfgScope* unreachable = makeChildScope(); PushScope ps{scopeStack, unreachable}; visitExpr(f->body); @@ -765,7 +765,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d) currentScope()->bindings[d->name] = def; captures[d->name].allVersions.push_back(def); - DfgScope* unreachable = makeChildScope(d->location); + DfgScope* unreachable = makeChildScope(); PushScope ps{scopeStack, unreachable}; visitGenerics(d->generics); @@ -781,7 +781,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d) // This declaration does not "introduce" any bindings in value namespace, // so there's no symbolic value to begin with. We'll traverse the properties // because their type annotations may depend on something in the value namespace. - DfgScope* unreachable = makeChildScope(d->location); + DfgScope* unreachable = makeChildScope(); PushScope ps{scopeStack, unreachable}; for (AstDeclaredClassProp prop : d->props) @@ -792,7 +792,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareClass* d) ControlFlow DataFlowGraphBuilder::visit(AstStatError* error) { - DfgScope* unreachable = makeChildScope(error->location); + DfgScope* unreachable = makeChildScope(); PushScope ps{scopeStack, unreachable}; for (AstStat* s : error->statements) @@ -904,10 +904,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprCall* c) LUAU_ASSERT(result); - Location location = currentScope()->location; - // This scope starts at the end of the call site and continues to the end of the original scope. - location.begin = c->location.end; - DfgScope* child = makeChildScope(location); + DfgScope* child = makeChildScope(); scopeStack.push_back(child); auto [def, key] = *result; @@ -952,7 +949,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprIndexExpr* i) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f) { - DfgScope* signatureScope = makeChildScope(f->location, DfgScope::Function); + DfgScope* signatureScope = makeChildScope(DfgScope::Function); PushScope ps{scopeStack, signatureScope}; if (AstLocal* self = f->self) @@ -1056,7 +1053,7 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprInterpString* i) DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprError* error) { - DfgScope* unreachable = makeChildScope(error->location); + DfgScope* unreachable = makeChildScope(); PushScope ps{scopeStack, unreachable}; for (AstExpr* e : error->expressions) diff --git a/Analysis/src/Differ.cpp b/Analysis/src/Differ.cpp index b2cebc0ba..7debae9c7 100644 --- a/Analysis/src/Differ.cpp +++ b/Analysis/src/Differ.cpp @@ -719,7 +719,7 @@ static DifferResult diffUsingEnv(DifferEnvironment& env, TypeId left, TypeId rig env.popVisiting(); return diffRes; } - if (auto le = get(left)) + if (auto le = get(left)) { // TODO: return debug-friendly result state env.popVisiting(); diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index 7515ee986..33989a2be 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -27,7 +27,6 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit); LUAU_FASTINT(LuauTypeInferIterationLimit); LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(LuauAllowFragmentParsing); -LUAU_FASTFLAG(LuauStoreDFGOnModule2); LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete) namespace @@ -89,6 +88,25 @@ FragmentAutocompleteAncestryResult findAncestryForFragmentParse(AstStatBlock* ro { localStack.push_back(locFun->name); localMap[locFun->name->name] = locFun->name; + if (locFun->location.contains(cursorPos)) + { + for (AstLocal* loc : locFun->func->args) + { + localStack.push_back(loc); + localMap[loc->name] = loc; + } + } + } + else if (auto globFun = stat->as()) + { + if (globFun->location.contains(cursorPos)) + { + for (AstLocal* loc : globFun->func->args) + { + localStack.push_back(loc); + localMap[loc->name] = loc; + } + } } } } @@ -234,9 +252,9 @@ FragmentParseResult parseFragment( // If we added to the end of the sourceModule, use the end of the nearest location if (appended && multiline) startPos = nearestStatement->location.end; - // Statement spans one line && cursorPos is on a different line - else if (!multiline && cursorPos.line != nearestStatement->location.end.line) - startPos = nearestStatement->location.end; + // Statement spans one line && cursorPos is either on the same line or after + else if (!multiline && cursorPos.line >= nearestStatement->location.end.line) + startPos = nearestStatement->location.begin; else if (multiline && nearestStatement->location.end.line < cursorPos.line) startPos = nearestStatement->location.end; else @@ -300,6 +318,7 @@ struct MixedModeIncrementalTCDefFinder : public AstVisitor referencedLocalDefs.push_back({local->local, local}); return true; } + // ast defs is just a mapping from expr -> def in general // will get built up by the dfg builder @@ -495,7 +514,6 @@ FragmentAutocompleteResult fragmentAutocomplete( ) { LUAU_ASSERT(FFlag::LuauAllowFragmentParsing); - LUAU_ASSERT(FFlag::LuauStoreDFGOnModule2); LUAU_ASSERT(FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete); const SourceModule* sourceModule = frontend.getSourceModule(moduleName); diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index d219f1820..396678463 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -45,11 +45,9 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile) LUAU_FASTFLAGVARIABLE(DebugLuauForbidInternalTypes) LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode) LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode) -LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionNoEvaluation) LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauRunCustomModuleChecks, false) LUAU_FASTFLAG(StudioReportLuauAny2) -LUAU_FASTFLAGVARIABLE(LuauStoreDFGOnModule2) LUAU_FASTFLAGVARIABLE(LuauStoreSolverTypeOnModule) namespace Luau @@ -1307,19 +1305,7 @@ ModulePtr check( } } - DataFlowGraph oldDfg = DataFlowGraphBuilder::build(sourceModule.root, iceHandler); - DataFlowGraph* dfgForConstraintGeneration = nullptr; - if (FFlag::LuauStoreDFGOnModule2) - { - auto [dfg, scopes] = DataFlowGraphBuilder::buildShared(sourceModule.root, iceHandler); - result->dataFlowGraph = std::move(dfg); - result->dfgScopes = std::move(scopes); - dfgForConstraintGeneration = result->dataFlowGraph.get(); - } - else - { - dfgForConstraintGeneration = &oldDfg; - } + DataFlowGraph dfg = DataFlowGraphBuilder::build(sourceModule.root, iceHandler); UnifierSharedState unifierState{iceHandler}; unifierState.counters.recursionLimit = FInt::LuauTypeInferRecursionLimit; @@ -1329,8 +1315,7 @@ ModulePtr check( SimplifierPtr simplifier = newSimplifier(NotNull{&result->internalTypes}, builtinTypes); TypeFunctionRuntime typeFunctionRuntime{iceHandler, NotNull{&limits}}; - if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation) - typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty(); + typeFunctionRuntime.allowEvaluation = sourceModule.parseErrors.empty(); ConstraintGenerator cg{ result, @@ -1343,7 +1328,7 @@ ModulePtr check( parentScope, std::move(prepareModuleScope), logger.get(), - NotNull{dfgForConstraintGeneration}, + NotNull{&dfg}, requireCycles }; @@ -1360,7 +1345,7 @@ ModulePtr check( moduleResolver, requireCycles, logger.get(), - NotNull{dfgForConstraintGeneration}, + NotNull{&dfg}, limits }; @@ -1414,32 +1399,16 @@ ModulePtr check( switch (mode) { case Mode::Nonstrict: - if (FFlag::LuauStoreDFGOnModule2) - { - Luau::checkNonStrict( - builtinTypes, - NotNull{&typeFunctionRuntime}, - iceHandler, - NotNull{&unifierState}, - NotNull{dfgForConstraintGeneration}, - NotNull{&limits}, - sourceModule, - result.get() - ); - } - else - { - Luau::checkNonStrict( - builtinTypes, - NotNull{&typeFunctionRuntime}, - iceHandler, - NotNull{&unifierState}, - NotNull{&oldDfg}, - NotNull{&limits}, - sourceModule, - result.get() - ); - } + Luau::checkNonStrict( + builtinTypes, + NotNull{&typeFunctionRuntime}, + iceHandler, + NotNull{&unifierState}, + NotNull{&dfg}, + NotNull{&limits}, + sourceModule, + result.get() + ); break; case Mode::Definition: // fallthrough intentional diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index c495fb6bf..3eb14fda3 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -9,6 +9,8 @@ #include "Luau/TypePack.h" #include "Luau/VisitType.h" +LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete) + namespace Luau { @@ -445,7 +447,7 @@ struct FreeTypeSearcher : TypeVisitor traverse(*prop.readTy); else { - LUAU_ASSERT(prop.isShared()); + LUAU_ASSERT(prop.isShared() || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete); Polarity p = polarity; polarity = Both; @@ -894,7 +896,7 @@ struct TypeCacher : TypeOnceVisitor return true; } - bool visit(TypePackId tp, const Unifiable::Error& etp) override + bool visit(TypePackId tp, const ErrorTypePack& etp) override { return true; } diff --git a/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index 073b05dc2..a2bcb2470 100644 --- a/Analysis/src/Linter.cpp +++ b/Analysis/src/Linter.cpp @@ -17,7 +17,6 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAttribute) -LUAU_FASTFLAG(LuauNativeAttribute) LUAU_FASTFLAGVARIABLE(LintRedundantNativeAttribute) namespace Luau @@ -3239,7 +3238,6 @@ static void lintComments(LintContext& context, const std::vector& ho static bool hasNativeCommentDirective(const std::vector& hotcomments) { - LUAU_ASSERT(FFlag::LuauNativeAttribute); LUAU_ASSERT(FFlag::LintRedundantNativeAttribute); for (const HotComment& hc : hotcomments) @@ -3265,7 +3263,6 @@ struct LintRedundantNativeAttribute : AstVisitor public: LUAU_NOINLINE static void process(LintContext& context) { - LUAU_ASSERT(FFlag::LuauNativeAttribute); LUAU_ASSERT(FFlag::LintRedundantNativeAttribute); LintRedundantNativeAttribute pass; @@ -3389,7 +3386,7 @@ std::vector lint( if (context.warningEnabled(LintWarning::Code_ComparisonPrecedence)) LintComparisonPrecedence::process(context); - if (FFlag::LuauNativeAttribute && FFlag::LintRedundantNativeAttribute && context.warningEnabled(LintWarning::Code_RedundantNativeAttribute)) + if (FFlag::LintRedundantNativeAttribute && context.warningEnabled(LintWarning::Code_RedundantNativeAttribute)) { if (hasNativeCommentDirective(hotcomments)) LintRedundantNativeAttribute::process(context); diff --git a/Analysis/src/NonStrictTypeChecker.cpp b/Analysis/src/NonStrictTypeChecker.cpp index e5f3c4693..b4a5eaf67 100644 --- a/Analysis/src/NonStrictTypeChecker.cpp +++ b/Analysis/src/NonStrictTypeChecker.cpp @@ -218,7 +218,7 @@ struct NonStrictTypeChecker return result; } - else if (get(pack)) + else if (get(pack)) return builtinTypes->errorRecoveryType(); else if (finite(pack) && size(pack) == 0) return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` diff --git a/Analysis/src/OverloadResolution.cpp b/Analysis/src/OverloadResolution.cpp index fbcce2b7c..f5557f2d4 100644 --- a/Analysis/src/OverloadResolution.cpp +++ b/Analysis/src/OverloadResolution.cpp @@ -417,8 +417,8 @@ std::optional selectOverload( TypePackId argsPack ) { - OverloadResolver resolver{builtinTypes, arena, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location}; - auto [status, overload] = resolver.selectOverload(fn, argsPack); + auto resolver = std::make_unique(builtinTypes, arena, normalizer, typeFunctionRuntime, scope, iceReporter, limits, location); + auto [status, overload] = resolver->selectOverload(fn, argsPack); if (status == OverloadResolver::Analysis::Ok) return overload; @@ -456,9 +456,9 @@ SolveResult solveFunctionCall( if (!u2.genericSubstitutions.empty() || !u2.genericPackSubstitutions.empty()) { - Instantiation2 instantiation{arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions)}; + auto instantiation = std::make_unique(arena, std::move(u2.genericSubstitutions), std::move(u2.genericPackSubstitutions)); - std::optional subst = instantiation.substitute(resultPack); + std::optional subst = instantiation->substitute(resultPack); if (!subst) return {SolveResult::CodeTooComplex}; diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index 1618b78f8..e8357f487 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -4,13 +4,15 @@ #include "Luau/Common.h" #include "Luau/Clone.h" #include "Luau/TxnLog.h" +#include "Luau/Type.h" #include #include LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) -LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256) +LUAU_FASTFLAG(LuauSyntheticErrors) namespace Luau { @@ -57,8 +59,25 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a } else if constexpr (std::is_same_v) { - LUAU_ASSERT(ty->persistent); - return ty; + if (FFlag::LuauSyntheticErrors) + { + LUAU_ASSERT(ty->persistent || a.synthetic); + + if (ty->persistent) + return ty; + + // While this code intentionally works (and clones) even if `a.synthetic` is `std::nullopt`, + // we still assert above because we consider it a bug to have a non-persistent error type + // without any associated metadata. We should always use the persistent version in such cases. + ErrorType clone = ErrorType{}; + clone.synthetic = a.synthetic; + return dest.addType(clone); + } + else + { + LUAU_ASSERT(ty->persistent); + return ty; + } } else if constexpr (std::is_same_v) { diff --git a/Analysis/src/ToDot.cpp b/Analysis/src/ToDot.cpp index e3f4fd3b0..9b1c20fbf 100644 --- a/Analysis/src/ToDot.cpp +++ b/Analysis/src/ToDot.cpp @@ -420,7 +420,7 @@ void StateDot::visitChildren(TypePackId tp, int index) finishNodeLabel(tp); finishNode(); } - else if (get(tp)) + else if (get(tp)) { formatAppend(result, "ErrorTypePack %d", index); finishNodeLabel(tp); diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 60ed3027f..96314ea1c 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -20,6 +20,7 @@ #include LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAGVARIABLE(LuauSyntheticErrors) /* * Enables increasing levels of verbosity for Luau type names when stringifying. @@ -998,7 +999,15 @@ struct TypeStringifier void operator()(TypeId, const ErrorType& tv) { state.result.error = true; - state.emit("*error-type*"); + + if (FFlag::LuauSyntheticErrors && tv.synthetic) + { + state.emit("*error-type<"); + stringify(*tv.synthetic); + state.emit(">*"); + } + else + state.emit("*error-type*"); } void operator()(TypeId, const LazyType& ltv) @@ -1173,10 +1182,18 @@ struct TypePackStringifier state.unsee(&tp); } - void operator()(TypePackId, const Unifiable::Error& error) + void operator()(TypePackId, const ErrorTypePack& error) { state.result.error = true; - state.emit("*error-type*"); + + if (FFlag::LuauSyntheticErrors && error.synthetic) + { + state.emit("*"); + stringify(*error.synthetic); + state.emit("*"); + } + else + state.emit("*error-type*"); } void operator()(TypePackId, const VariadicTypePack& pack) diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index 1cf9d268f..a5298ee5a 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -1045,7 +1045,7 @@ BuiltinTypes::BuiltinTypes() , unknownTypePack(arena->addTypePack(TypePackVar{VariadicTypePack{unknownType}, /*persistent*/ true})) , neverTypePack(arena->addTypePack(TypePackVar{VariadicTypePack{neverType}, /*persistent*/ true})) , uninhabitableTypePack(arena->addTypePack(TypePackVar{TypePack{{neverType}, neverTypePack}, /*persistent*/ true})) - , errorTypePack(arena->addTypePack(TypePackVar{Unifiable::Error{}, /*persistent*/ true})) + , errorTypePack(arena->addTypePack(TypePackVar{ErrorTypePack{}, /*persistent*/ true})) { freeze(*arena); } diff --git a/Analysis/src/TypeAttach.cpp b/Analysis/src/TypeAttach.cpp index a28ff9871..6f28b11cd 100644 --- a/Analysis/src/TypeAttach.cpp +++ b/Analysis/src/TypeAttach.cpp @@ -329,7 +329,7 @@ class TypeRehydrationVisitor Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation} ); } - AstType* operator()(const Unifiable::Error&) + AstType* operator()(const ErrorType&) { return allocator->alloc(Location(), std::nullopt, AstName("Unifiable"), std::nullopt, Location()); } @@ -458,7 +458,7 @@ class TypePackRehydrationVisitor return allocator->alloc(Location(), AstName("free")); } - AstTypePack* operator()(const Unifiable::Error&) const + AstTypePack* operator()(const ErrorTypePack&) const { return allocator->alloc(Location(), AstName("Unifiable")); } diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 794200116..5397a0e8b 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -31,7 +31,6 @@ #include LUAU_FASTFLAG(DebugLuauMagicTypes) -LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2) LUAU_FASTFLAGVARIABLE(LuauTableKeysAreRValues) @@ -1200,8 +1199,6 @@ void TypeChecker2::visit(AstStatTypeAlias* stat) void TypeChecker2::visit(AstStatTypeFunction* stat) { // TODO: add type checking for user-defined type functions - if (!FFlag::LuauUserDefinedTypeFunctions2) - reportError(TypeError{stat->location, GenericError{"This syntax is not supported"}}); } void TypeChecker2::visit(AstTypeList types) @@ -2353,7 +2350,7 @@ TypeId TypeChecker2::flattenPack(TypePackId pack) return result; } - else if (get(pack)) + else if (get(pack)) return builtinTypes->errorRecoveryType(); else if (finite(pack) && size(pack) == 0) return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index 6b5dd88bf..e7620cc45 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -46,8 +46,6 @@ LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyApplicationCartesianProductLimit, 5'0 LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFamilyUseGuesserDepth, -1); LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) -LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctions2) -LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation) LUAU_FASTFLAG(LuauUserTypeFunFixRegister) LUAU_FASTFLAG(LuauRemoveNotAnyHack) LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionResetState) @@ -634,12 +632,9 @@ TypeFunctionReductionResult userDefinedTypeFunction( } } - if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation) - { - // If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones - if (!ctx->typeFunctionRuntime->allowEvaluation) - return {ctx->builtins->errorRecoveryType(), false, {}, {}}; - } + // If type functions cannot be evaluated because of errors in the code, we do not generate any additional ones + if (!ctx->typeFunctionRuntime->allowEvaluation) + return {ctx->builtins->errorRecoveryType(), false, {}, {}}; for (auto typeParam : typeParams) { @@ -994,12 +989,9 @@ TypeFunctionRuntime::~TypeFunctionRuntime() {} std::optional TypeFunctionRuntime::registerFunction(AstStatTypeFunction* function) { - if (FFlag::LuauUserDefinedTypeFunctionNoEvaluation) - { - // If evaluation is disabled, we do not generate additional error messages - if (!allowEvaluation) - return std::nullopt; - } + // If evaluation is disabled, we do not generate additional error messages + if (!allowEvaluation) + return std::nullopt; prepareState(); diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 34b101554..7ed3290bb 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -964,7 +964,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatAssign& assig else if (auto tail = valueIter.tail()) { TypePackId tailPack = follow(*tail); - if (get(tailPack)) + if (get(tailPack)) right = errorRecoveryType(scope); else if (auto vtp = get(tailPack)) right = vtp->ty; @@ -1244,7 +1244,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin) iterTy = freshType(scope); unify(callRetPack, addTypePack({{iterTy}, freshTypePack(scope)}), scope, forin.location); } - else if (get(callRetPack) || !first(callRetPack)) + else if (get(callRetPack) || !first(callRetPack)) { for (TypeId var : varTypes) unify(errorRecoveryType(scope), var, scope, forin.location); @@ -1972,7 +1972,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp *asMutable(varargPack) = TypePack{{head}, tail}; return WithPredicate{head}; } - if (get(varargPack)) + if (get(varargPack)) return WithPredicate{errorRecoveryType(scope)}; else if (auto vtp = get(varargPack)) return WithPredicate{vtp->ty}; @@ -2002,7 +2002,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp unify(pack, retPack, scope, expr.location); return {head, std::move(result.predicates)}; } - if (get(retPack)) + if (get(retPack)) return {errorRecoveryType(scope), std::move(result.predicates)}; else if (auto vtp = get(retPack)) return {vtp->ty, std::move(result.predicates)}; @@ -4093,7 +4093,7 @@ void TypeChecker::checkArgumentList( if (argIter.tail()) { TypePackId tail = *argIter.tail(); - if (state.log.getMutable(tail)) + if (state.log.getMutable(tail)) { // Unify remaining parameters so we don't leave any free-types hanging around. while (paramIter != endIter) @@ -4178,7 +4178,7 @@ void TypeChecker::checkArgumentList( } TypePackId tail = state.log.follow(*paramIter.tail()); - if (state.log.getMutable(tail)) + if (state.log.getMutable(tail)) { // Function is variadic. Ok. return; @@ -4314,7 +4314,7 @@ WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope WithPredicate argListResult = checkExprList(scope, expr.location, expr.args, false, {}, expectedTypes); TypePackId argPack = argListResult.type; - if (get(argPack)) + if (get(argPack)) return WithPredicate{errorRecoveryTypePack(scope)}; TypePack* args = nullptr; @@ -4904,7 +4904,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module TypePackId modulePack = module->returnType; - if (get(modulePack)) + if (get(modulePack)) return errorRecoveryType(scope); std::optional moduleType = first(modulePack); diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index 1ed1b9e0a..ed7d5ebf2 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -10,6 +10,7 @@ #include LUAU_FASTFLAG(LuauSolverV2); +LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete); namespace Luau { @@ -331,7 +332,7 @@ TypePack extendTypePack( return result; } - else if (const Unifiable::Error* etp = getMutable(pack)) + else if (auto etp = getMutable(pack)) { while (result.head.size() < length) result.head.push_back(builtinTypes->errorRecoveryType()); @@ -426,7 +427,7 @@ TypeId stripNil(NotNull builtinTypes, TypeArena& arena, TypeId ty) ErrorSuppression shouldSuppressErrors(NotNull normalizer, TypeId ty) { - LUAU_ASSERT(FFlag::LuauSolverV2); + LUAU_ASSERT(FFlag::LuauSolverV2 || FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete); std::shared_ptr normType = normalizer->normalize(ty); if (!normType) diff --git a/Analysis/src/Unifiable.cpp b/Analysis/src/Unifiable.cpp index 2ceb97aae..d9f3947fa 100644 --- a/Analysis/src/Unifiable.cpp +++ b/Analysis/src/Unifiable.cpp @@ -1,5 +1,7 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/Unifiable.h" +#include "Luau/TypeFwd.h" +#include "Luau/TypePack.h" namespace Luau { @@ -13,12 +15,17 @@ int freshIndex() return ++nextIndex; } -Error::Error() +template +Error::Error() : index(++nextIndex) { } -int Error::nextIndex = 0; +template +int Error::nextIndex = 0; + +template struct Error; +template struct Error; } // namespace Unifiable } // namespace Luau diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 11cc399e3..5d71d5cb8 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -1616,9 +1616,9 @@ void Unifier::tryUnify_(TypePackId subTp, TypePackId superTp, bool isFunctionCal log.replace(subTp, Unifiable::Bound(superTp)); } } - else if (log.getMutable(superTp)) + else if (log.getMutable(superTp)) tryUnifyWithAny(subTp, superTp); - else if (log.getMutable(subTp)) + else if (log.getMutable(subTp)) tryUnifyWithAny(superTp, subTp); else if (log.getMutable(superTp)) tryUnifyVariadics(subTp, superTp, false); @@ -2741,7 +2741,7 @@ void Unifier::tryUnifyVariadics(TypePackId subTp, TypePackId superTp, bool rever else log.replace(tail, BoundTypePack{superTp}); } - else if (get(tail)) + else if (get(tail)) { // Nothing to do here. } @@ -2845,7 +2845,7 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy) void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp) { - LUAU_ASSERT(get(anyTp)); + LUAU_ASSERT(get(anyTp)); const TypeId anyTy = builtinTypes->errorRecoveryType(); @@ -2997,7 +2997,7 @@ bool Unifier::occursCheck(DenseHashSet& seen, TypePackId needle, Typ RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); - while (!log.getMutable(haystack)) + while (!log.getMutable(haystack)) { if (needle == haystack) return true; diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index 5ea11ad06..e63856d38 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -908,7 +908,7 @@ OccursCheckResult Unifier2::occursCheck(DenseHashSet& seen, TypePack RecursionLimiter _ra(&recursionCount, recursionLimit); - while (!getMutable(haystack)) + while (!getMutable(haystack)) { if (needle == haystack) return OccursCheckResult::Fail; diff --git a/Ast/src/Ast.cpp b/Ast/src/Ast.cpp index d9a948be4..7e0efd435 100644 --- a/Ast/src/Ast.cpp +++ b/Ast/src/Ast.cpp @@ -3,8 +3,6 @@ #include "Luau/Common.h" -LUAU_FASTFLAG(LuauNativeAttribute) - namespace Luau { @@ -239,8 +237,6 @@ void AstExprFunction::visit(AstVisitor* visitor) bool AstExprFunction::hasNativeAttribute() const { - LUAU_ASSERT(FFlag::LuauNativeAttribute); - for (const auto attribute : attributes) { if (attribute->type == AstAttr::Type::Native) diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index a0231fc8a..8d665688d 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -18,13 +18,12 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) // flag so that we don't break production games by reverting syntax changes. // See docs/SyntaxChanges.md for an explanation. LUAU_FASTFLAGVARIABLE(LuauSolverV2) -LUAU_FASTFLAGVARIABLE(LuauNativeAttribute) -LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr) LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunctionsSyntax2) LUAU_FASTFLAGVARIABLE(LuauUserDefinedTypeFunParseExport) LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing) LUAU_FASTFLAGVARIABLE(LuauPortableStringZeroCheck) LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams) +LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes) namespace Luau { @@ -724,10 +723,6 @@ std::pair Parser::validateAttribute(const char* attributeNa if (found) { type = kAttributeEntries[i].type; - - if (!FFlag::LuauNativeAttribute && type == AstAttr::Type::Native) - found = false; - break; } } @@ -1278,6 +1273,19 @@ std::pair Parser::parseFunctionBody( MatchLexeme matchParen = lexer.current(); expectAndConsume('(', "function"); + // NOTE: This was added in conjunction with passing `searchForMissing` to + // `expectMatchAndConsume` inside `parseTableType` so that the behavior of + // parsing code like below (note the missing `}`): + // + // function (t: { a: number ) end + // + // ... will still parse as (roughly): + // + // function (t: { a: number }) end + // + if (FFlag::LuauErrorRecoveryForTableTypes) + matchRecoveryStopOnToken[')']++; + TempVector args(scratchBinding); bool vararg = false; @@ -1294,6 +1302,9 @@ std::pair Parser::parseFunctionBody( expectMatchAndConsume(')', matchParen, true); + if (FFlag::LuauErrorRecoveryForTableTypes) + matchRecoveryStopOnToken[')']--; + std::optional typelist = parseOptionalReturnType(); AstLocal* funLocal = nullptr; @@ -1678,7 +1689,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) Location end = lexer.current().location; - if (!expectMatchAndConsume('}', matchBrace)) + if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ FFlag::LuauErrorRecoveryForTableTypes)) end = lexer.previousLocation(); return allocator.alloc(Location(start, end), copy(props), indexer); @@ -2526,7 +2537,7 @@ AstExpr* Parser::parseSimpleExpr() AstArray attributes{nullptr, 0}; - if (FFlag::LuauAttributeSyntaxFunExpr && lexer.current().type == Lexeme::Attribute) + if (lexer.current().type == Lexeme::Attribute) { attributes = parseAttributes(); diff --git a/CodeGen/include/Luau/IrUtils.h b/CodeGen/include/Luau/IrUtils.h index 08700573f..773b23a66 100644 --- a/CodeGen/include/Luau/IrUtils.h +++ b/CodeGen/include/Luau/IrUtils.h @@ -5,6 +5,8 @@ #include "Luau/Common.h" #include "Luau/IrData.h" +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -177,6 +179,9 @@ inline bool hasResult(IrCmd cmd) case IrCmd::MUL_VEC: case IrCmd::DIV_VEC: case IrCmd::DOT_VEC: + if (cmd == IrCmd::DOT_VEC) + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); + LUAU_FALLTHROUGH; case IrCmd::UNM_VEC: case IrCmd::NOT_ANY: case IrCmd::CMP_ANY: diff --git a/CodeGen/src/AssemblyBuilderA64.cpp b/CodeGen/src/AssemblyBuilderA64.cpp index 23384e57d..9e17d3fd1 100644 --- a/CodeGen/src/AssemblyBuilderA64.cpp +++ b/CodeGen/src/AssemblyBuilderA64.cpp @@ -7,6 +7,8 @@ #include #include +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -588,6 +590,7 @@ void AssemblyBuilderA64::fabs(RegisterA64 dst, RegisterA64 src) void AssemblyBuilderA64::faddp(RegisterA64 dst, RegisterA64 src) { + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); CODEGEN_ASSERT(dst.kind == KindA64::d || dst.kind == KindA64::s); CODEGEN_ASSERT(dst.kind == src.kind); diff --git a/CodeGen/src/AssemblyBuilderX64.cpp b/CodeGen/src/AssemblyBuilderX64.cpp index 1e646bcbc..803732e2c 100644 --- a/CodeGen/src/AssemblyBuilderX64.cpp +++ b/CodeGen/src/AssemblyBuilderX64.cpp @@ -6,6 +6,8 @@ #include #include +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -948,6 +950,7 @@ void AssemblyBuilderX64::vpinsrd(RegisterX64 dst, RegisterX64 src1, OperandX64 s void AssemblyBuilderX64::vdpps(OperandX64 dst, OperandX64 src1, OperandX64 src2, uint8_t mask) { + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); placeAvx("vdpps", dst, src1, src2, mask, 0x40, false, AVX_0F3A, AVX_66); } diff --git a/CodeGen/src/BytecodeSummary.cpp b/CodeGen/src/BytecodeSummary.cpp index d0d715041..d179dcc50 100644 --- a/CodeGen/src/BytecodeSummary.cpp +++ b/CodeGen/src/BytecodeSummary.cpp @@ -8,8 +8,6 @@ #include "lobject.h" #include "lstate.h" -LUAU_FASTFLAG(LuauNativeAttribute) - namespace Luau { namespace CodeGen @@ -58,10 +56,7 @@ std::vector summarizeBytecode(lua_State* L, int idx, un Proto* root = clvalue(func)->l.p; std::vector protos; - if (FFlag::LuauNativeAttribute) - gatherFunctions(protos, root, CodeGen_ColdFunctions, root->flags & LPF_NATIVE_FUNCTION); - else - gatherFunctions_DEPRECATED(protos, root, CodeGen_ColdFunctions); + gatherFunctions(protos, root, CodeGen_ColdFunctions, root->flags & LPF_NATIVE_FUNCTION); std::vector summaries; summaries.reserve(protos.size()); diff --git a/CodeGen/src/CodeGenAssembly.cpp b/CodeGen/src/CodeGenAssembly.cpp index c423a1ce7..bffce5175 100644 --- a/CodeGen/src/CodeGenAssembly.cpp +++ b/CodeGen/src/CodeGenAssembly.cpp @@ -12,8 +12,6 @@ #include "lapi.h" -LUAU_FASTFLAG(LuauNativeAttribute) - namespace Luau { namespace CodeGen @@ -155,10 +153,7 @@ static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, A } std::vector protos; - if (FFlag::LuauNativeAttribute) - gatherFunctions(protos, root, options.compilationOptions.flags, root->flags & LPF_NATIVE_FUNCTION); - else - gatherFunctions_DEPRECATED(protos, root, options.compilationOptions.flags); + gatherFunctions(protos, root, options.compilationOptions.flags, root->flags & LPF_NATIVE_FUNCTION); protos.erase( std::remove_if( diff --git a/CodeGen/src/CodeGenContext.cpp b/CodeGen/src/CodeGenContext.cpp index c3c58f789..262d4a429 100644 --- a/CodeGen/src/CodeGenContext.cpp +++ b/CodeGen/src/CodeGenContext.cpp @@ -14,7 +14,6 @@ LUAU_FASTINTVARIABLE(LuauCodeGenBlockSize, 4 * 1024 * 1024) LUAU_FASTINTVARIABLE(LuauCodeGenMaxTotalSize, 256 * 1024 * 1024) -LUAU_FASTFLAG(LuauNativeAttribute) namespace Luau { @@ -510,10 +509,7 @@ template return CompilationResult{CodeGenCompilationResult::CodeGenNotInitialized}; std::vector protos; - if (FFlag::LuauNativeAttribute) - gatherFunctions(protos, root, options.flags, root->flags & LPF_NATIVE_FUNCTION); - else - gatherFunctions_DEPRECATED(protos, root, options.flags); + gatherFunctions(protos, root, options.flags, root->flags & LPF_NATIVE_FUNCTION); // Skip protos that have been compiled during previous invocations of CodeGen::compile protos.erase( diff --git a/CodeGen/src/CodeGenLower.h b/CodeGen/src/CodeGenLower.h index 9e77844bd..03eaabea6 100644 --- a/CodeGen/src/CodeGenLower.h +++ b/CodeGen/src/CodeGenLower.h @@ -27,31 +27,12 @@ LUAU_FASTFLAG(DebugCodegenSkipNumbering) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTINT(CodegenHeuristicsBlockLimit) LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit) -LUAU_FASTFLAG(LuauNativeAttribute) namespace Luau { namespace CodeGen { -inline void gatherFunctions_DEPRECATED(std::vector& results, Proto* proto, unsigned int flags) -{ - if (results.size() <= size_t(proto->bytecodeid)) - results.resize(proto->bytecodeid + 1); - - // Skip protos that we've already compiled in this run: this happens because at -O2, inlined functions get their protos reused - if (results[proto->bytecodeid]) - return; - - // Only compile cold functions if requested - if ((proto->flags & LPF_NATIVE_COLD) == 0 || (flags & CodeGen_ColdFunctions) != 0) - results[proto->bytecodeid] = proto; - - // Recursively traverse child protos even if we aren't compiling this one - for (int i = 0; i < proto->sizep; i++) - gatherFunctions_DEPRECATED(results, proto->p[i], flags); -} - inline void gatherFunctionsHelper( std::vector& results, Proto* proto, @@ -82,7 +63,6 @@ inline void gatherFunctionsHelper( inline void gatherFunctions(std::vector& results, Proto* root, const unsigned int flags, const bool hasNativeFunctions = false) { - LUAU_ASSERT(FFlag::LuauNativeAttribute); gatherFunctionsHelper(results, root, flags, hasNativeFunctions, true); } diff --git a/CodeGen/src/IrDump.cpp b/CodeGen/src/IrDump.cpp index f4806b315..a59db8e8a 100644 --- a/CodeGen/src/IrDump.cpp +++ b/CodeGen/src/IrDump.cpp @@ -7,6 +7,8 @@ #include +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -164,6 +166,7 @@ const char* getCmdName(IrCmd cmd) case IrCmd::UNM_VEC: return "UNM_VEC"; case IrCmd::DOT_VEC: + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); return "DOT_VEC"; case IrCmd::NOT_ANY: return "NOT_ANY"; diff --git a/CodeGen/src/IrLoweringA64.cpp b/CodeGen/src/IrLoweringA64.cpp index 45ae5eeb3..91fbf0bf7 100644 --- a/CodeGen/src/IrLoweringA64.cpp +++ b/CodeGen/src/IrLoweringA64.cpp @@ -11,6 +11,8 @@ #include "lstate.h" #include "lgc.h" +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -730,6 +732,8 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) } case IrCmd::DOT_VEC: { + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); + inst.regA64 = regs.allocReg(KindA64::d, index); RegisterA64 temp = regs.allocTemp(KindA64::q); diff --git a/CodeGen/src/IrLoweringX64.cpp b/CodeGen/src/IrLoweringX64.cpp index 3e4592bf4..796894ed6 100644 --- a/CodeGen/src/IrLoweringX64.cpp +++ b/CodeGen/src/IrLoweringX64.cpp @@ -15,6 +15,8 @@ #include "lstate.h" #include "lgc.h" +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -677,6 +679,8 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) } case IrCmd::DOT_VEC: { + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); + inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b}); ScopedRegX64 tmp1{regs}; diff --git a/CodeGen/src/IrUtils.cpp b/CodeGen/src/IrUtils.cpp index c1183a47c..5f3848071 100644 --- a/CodeGen/src/IrUtils.cpp +++ b/CodeGen/src/IrUtils.cpp @@ -12,6 +12,8 @@ #include #include +LUAU_FASTFLAG(LuauVectorLibNativeDot); + namespace Luau { namespace CodeGen @@ -76,6 +78,7 @@ IrValueKind getCmdValueKind(IrCmd cmd) case IrCmd::UNM_VEC: return IrValueKind::Tvalue; case IrCmd::DOT_VEC: + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); return IrValueKind::Double; case IrCmd::NOT_ANY: case IrCmd::CMP_ANY: diff --git a/CodeGen/src/OptimizeConstProp.cpp b/CodeGen/src/OptimizeConstProp.cpp index 6d4537657..519630f0d 100644 --- a/CodeGen/src/OptimizeConstProp.cpp +++ b/CodeGen/src/OptimizeConstProp.cpp @@ -18,6 +18,7 @@ LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3) LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64) LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64) LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks) +LUAU_FASTFLAG(LuauVectorLibNativeDot); namespace Luau { @@ -1344,6 +1345,9 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& case IrCmd::MUL_VEC: case IrCmd::DIV_VEC: case IrCmd::DOT_VEC: + if (inst.cmd == IrCmd::DOT_VEC) + LUAU_ASSERT(FFlag::LuauVectorLibNativeDot); + if (IrInst* a = function.asInstOp(inst.a); a && a->cmd == IrCmd::TAG_VECTOR) replace(function, inst.a, a->a); diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 6b908c279..847001775 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -26,7 +26,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25) LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300) LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) -LUAU_FASTFLAG(LuauNativeAttribute) LUAU_FASTFLAGVARIABLE(LuauCompileOptimizeRevArith) namespace Luau @@ -286,7 +285,7 @@ struct Compiler if (func->functionDepth == 0 && !hasLoops) protoflags |= LPF_NATIVE_COLD; - if (FFlag::LuauNativeAttribute && func->hasNativeAttribute()) + if (func->hasNativeAttribute()) protoflags |= LPF_NATIVE_FUNCTION; bytecode.endFunction(uint8_t(stackSize), uint8_t(upvals.size()), protoflags); @@ -3927,7 +3926,7 @@ struct Compiler // this makes sure all functions that are used when compiling this one have been already added to the vector functions.push_back(node); - if (FFlag::LuauNativeAttribute && !hasNativeFunction && node->hasNativeAttribute()) + if (!hasNativeFunction && node->hasNativeAttribute()) hasNativeFunction = true; return false; @@ -4272,7 +4271,7 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c // If a function has native attribute and the whole module is not native, we set LPF_NATIVE_FUNCTION flag // This ensures that LPF_NATIVE_MODULE and LPF_NATIVE_FUNCTION are exclusive. - if (FFlag::LuauNativeAttribute && (protoflags & LPF_NATIVE_FUNCTION) && !(mainFlags & LPF_NATIVE_MODULE)) + if ((protoflags & LPF_NATIVE_FUNCTION) && !(mainFlags & LPF_NATIVE_MODULE)) mainFlags |= LPF_NATIVE_FUNCTION; } diff --git a/Config/include/Luau/Config.h b/Config/include/Luau/Config.h index 64b76f070..3f29a24fb 100644 --- a/Config/include/Luau/Config.h +++ b/Config/include/Luau/Config.h @@ -46,7 +46,7 @@ struct Config DenseHashMap aliases{""}; - void setAlias(std::string alias, const std::string& value, const std::string configLocation); + void setAlias(std::string alias, std::string value, const std::string& configLocation); private: // Prevents making unnecessary copies of the same config location string. diff --git a/Config/src/Config.cpp b/Config/src/Config.cpp index 345e039c1..15e58e297 100644 --- a/Config/src/Config.cpp +++ b/Config/src/Config.cpp @@ -28,15 +28,7 @@ Config::Config(const Config& other) { for (const auto& [alias, aliasInfo] : other.aliases) { - std::string configLocation = std::string(aliasInfo.configLocation); - - if (!configLocationCache.contains(configLocation)) - configLocationCache[configLocation] = std::make_unique(configLocation); - - AliasInfo newAliasInfo; - newAliasInfo.value = aliasInfo.value; - newAliasInfo.configLocation = *configLocationCache[configLocation]; - aliases[alias] = std::move(newAliasInfo); + setAlias(alias, aliasInfo.value, std::string(aliasInfo.configLocation)); } } @@ -50,10 +42,10 @@ Config& Config::operator=(const Config& other) return *this; } -void Config::setAlias(std::string alias, const std::string& value, const std::string configLocation) +void Config::setAlias(std::string alias, std::string value, const std::string& configLocation) { AliasInfo& info = aliases[alias]; - info.value = value; + info.value = std::move(value); if (!configLocationCache.contains(configLocation)) configLocationCache[configLocation] = std::make_unique(configLocation); diff --git a/VM/src/lveclib.cpp b/VM/src/lveclib.cpp index 174e1ed46..2a4e58c60 100644 --- a/VM/src/lveclib.cpp +++ b/VM/src/lveclib.cpp @@ -6,6 +6,8 @@ #include +LUAU_FASTFLAGVARIABLE(LuauVectorMetatable) + static int vector_create(lua_State* L) { double x = luaL_checknumber(L, 1); @@ -254,6 +256,35 @@ static int vector_max(lua_State* L) return 1; } +static int vector_index(lua_State* L) +{ + LUAU_ASSERT(FFlag::LuauVectorMetatable); + + const float* v = luaL_checkvector(L, 1); + size_t namelen = 0; + const char* name = luaL_checklstring(L, 2, &namelen); + + // field access implementation mirrors the fast-path we have in the VM + if (namelen == 1) + { + int ic = (name[0] | ' ') - 'x'; + +#if LUA_VECTOR_SIZE == 4 + // 'w' is before 'x' in ascii, so ic is -1 when indexing with 'w' + if (ic == -1) + ic = 3; +#endif + + if (unsigned(ic) < LUA_VECTOR_SIZE) + { + lua_pushnumber(L, v[ic]); + return 1; + } + } + + luaL_error(L, "attempt to index vector with '%s'", name); +} + static const luaL_Reg vectorlib[] = { {"create", vector_create}, {"magnitude", vector_magnitude}, @@ -271,6 +302,30 @@ static const luaL_Reg vectorlib[] = { {NULL, NULL}, }; +static void createmetatable(lua_State* L) +{ + LUAU_ASSERT(FFlag::LuauVectorMetatable); + + lua_createtable(L, 0, 1); // create metatable for vectors + + // push dummy vector +#if LUA_VECTOR_SIZE == 4 + lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f); +#else + lua_pushvector(L, 0.0f, 0.0f, 0.0f); +#endif + + lua_pushvalue(L, -2); + lua_setmetatable(L, -2); // set vector metatable + lua_pop(L, 1); // pop dummy vector + + lua_pushcfunction(L, vector_index, nullptr); + lua_setfield(L, -2, "__index"); + + lua_setreadonly(L, -1, true); + lua_pop(L, 1); // pop the metatable +} + int luaopen_vector(lua_State* L) { luaL_register(L, LUA_VECLIBNAME, vectorlib); @@ -287,5 +342,8 @@ int luaopen_vector(lua_State* L) lua_setfield(L, -2, "one"); #endif + if (FFlag::LuauVectorMetatable) + createmetatable(L); + return 1; } diff --git a/tests/AssemblyBuilderA64.test.cpp b/tests/AssemblyBuilderA64.test.cpp index ee319a5fa..b730cb1ef 100644 --- a/tests/AssemblyBuilderA64.test.cpp +++ b/tests/AssemblyBuilderA64.test.cpp @@ -10,6 +10,8 @@ using namespace Luau::CodeGen; using namespace Luau::CodeGen::A64; +LUAU_FASTFLAG(LuauVectorLibNativeDot); + static std::string bytecodeAsArray(const std::vector& bytecode) { std::string result = "{"; @@ -387,6 +389,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPBasic") TEST_CASE_FIXTURE(AssemblyBuilderA64Fixture, "FPMath") { + ScopedFastFlag sff{FFlag::LuauVectorLibNativeDot, true}; + SINGLE_COMPARE(fabs(d1, d2), 0x1E60C041); SINGLE_COMPARE(fadd(d1, d2, d3), 0x1E632841); SINGLE_COMPARE(fadd(s29, s29, s28), 0x1E3C2BBD); diff --git a/tests/AssemblyBuilderX64.test.cpp b/tests/AssemblyBuilderX64.test.cpp index 016616e0b..504e40e4c 100644 --- a/tests/AssemblyBuilderX64.test.cpp +++ b/tests/AssemblyBuilderX64.test.cpp @@ -10,6 +10,8 @@ using namespace Luau::CodeGen; using namespace Luau::CodeGen::X64; +LUAU_FASTFLAG(LuauVectorLibNativeDot); + static std::string bytecodeAsArray(const std::vector& bytecode) { std::string result = "{"; @@ -568,6 +570,8 @@ TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXConversionInstructionForms") TEST_CASE_FIXTURE(AssemblyBuilderX64Fixture, "AVXTernaryInstructionForms") { + ScopedFastFlag sff{FFlag::LuauVectorLibNativeDot, true}; + SINGLE_COMPARE(vroundsd(xmm7, xmm12, xmm3, RoundingModeX64::RoundToNegativeInfinity), 0xc4, 0xe3, 0x19, 0x0b, 0xfb, 0x09); SINGLE_COMPARE( vroundsd(xmm8, xmm13, xmmword[r13 + rdx], RoundingModeX64::RoundToPositiveInfinity), 0xc4, 0x43, 0x11, 0x0b, 0x44, 0x15, 0x00, 0x0a diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 17e5a3612..c0e81371d 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -34,11 +34,13 @@ void luaC_validate(lua_State* L); LUAU_FASTFLAG(LuauMathMap) LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) -LUAU_FASTFLAG(LuauNativeAttribute) LUAU_DYNAMIC_FASTFLAG(LuauStackLimit) LUAU_FASTFLAG(LuauVectorDefinitions) LUAU_DYNAMIC_FASTFLAG(LuauDebugInfoInvArgLeftovers) LUAU_FASTFLAG(LuauVectorLibNativeCodegen) +LUAU_FASTFLAG(LuauVectorLibNativeDot) +LUAU_FASTFLAG(LuauVectorBuiltins) +LUAU_FASTFLAG(LuauVectorMetatable) static lua_CompileOptions defaultOptions() { @@ -889,7 +891,10 @@ TEST_CASE("Vector") TEST_CASE("VectorLibrary") { + ScopedFastFlag luauVectorBuiltins{FFlag::LuauVectorBuiltins, true}; ScopedFastFlag luauVectorLibNativeCodegen{FFlag::LuauVectorLibNativeCodegen, true}; + ScopedFastFlag luauVectorLibNativeDot{FFlag::LuauVectorLibNativeDot, true}; + ScopedFastFlag luauVectorMetatable{FFlag::LuauVectorMetatable, true}; lua_CompileOptions copts = defaultOptions(); @@ -2951,8 +2956,6 @@ TEST_CASE("NativeAttribute") if (!codegen || !luau_codegen_supported()) return; - ScopedFastFlag sffs[] = {{FFlag::LuauNativeAttribute, true}}; - std::string source = R"R( @native local function sum(x, y) diff --git a/tests/Fixture.cpp b/tests/Fixture.cpp index 57ff8ebd8..5a2f9319f 100644 --- a/tests/Fixture.cpp +++ b/tests/Fixture.cpp @@ -562,12 +562,14 @@ void Fixture::validateErrors(const std::vector& errors) } } -LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source) +LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source, bool forAutocomplete) { - unfreeze(frontend.globals.globalTypes); - LoadDefinitionFileResult result = - frontend.loadDefinitionFile(frontend.globals, frontend.globals.globalScope, source, "@test", /* captureComments */ false); - freeze(frontend.globals.globalTypes); + GlobalTypes& globals = forAutocomplete ? frontend.globalsForAutocomplete : frontend.globals; + unfreeze(globals.globalTypes); + LoadDefinitionFileResult result = frontend.loadDefinitionFile( + globals, globals.globalScope, source, "@test", /* captureComments */ false, /* typecheckForAutocomplete */ forAutocomplete + ); + freeze(globals.globalTypes); if (result.module) dumpErrors(result.module); diff --git a/tests/Fixture.h b/tests/Fixture.h index d0a2ca9f9..e273a6420 100644 --- a/tests/Fixture.h +++ b/tests/Fixture.h @@ -139,7 +139,7 @@ struct Fixture void registerTestTypes(); - LoadDefinitionFileResult loadDefinition(const std::string& source); + LoadDefinitionFileResult loadDefinition(const std::string& source, bool forAutocomplete = false); }; struct BuiltinsFixture : Fixture diff --git a/tests/FragmentAutocomplete.test.cpp b/tests/FragmentAutocomplete.test.cpp index d90481b20..f0e2ae91a 100644 --- a/tests/FragmentAutocomplete.test.cpp +++ b/tests/FragmentAutocomplete.test.cpp @@ -15,12 +15,12 @@ #include #include #include +#include using namespace Luau; LUAU_FASTFLAG(LuauAllowFragmentParsing); -LUAU_FASTFLAG(LuauStoreDFGOnModule2); LUAU_FASTFLAG(LuauAutocompleteRefactorsForIncrementalAutocomplete) LUAU_FASTFLAG(LuauSymbolEquality); LUAU_FASTFLAG(LuauStoreSolverTypeOnModule); @@ -46,12 +46,11 @@ static FrontendOptions getOptions() template struct FragmentAutocompleteFixtureImpl : BaseType { - ScopedFastFlag sffs[5] = { + ScopedFastFlag sffs[4] = { {FFlag::LuauAllowFragmentParsing, true}, - {FFlag::LuauStoreDFGOnModule2, true}, {FFlag::LuauAutocompleteRefactorsForIncrementalAutocomplete, true}, {FFlag::LuauStoreSolverTypeOnModule, true}, - {FFlag::LuauSymbolEquality, true} + {FFlag::LuauSymbolEquality, true}, }; FragmentAutocompleteFixtureImpl() @@ -140,6 +139,25 @@ struct FragmentAutocompleteFixture : FragmentAutocompleteFixtureImpl struct FragmentAutocompleteBuiltinsFixture : FragmentAutocompleteFixtureImpl { + FragmentAutocompleteBuiltinsFixture() + : FragmentAutocompleteFixtureImpl() + { + const std::string fakeVecDecl = R"( +declare class FakeVec + function dot(self, x: FakeVec) : FakeVec + zero : FakeVec +end +)"; + // The old solver always performs a strict mode check and populates the module resolver and globals + // for autocomplete. + // The new solver just populates the globals and the moduleResolver. + // Because these tests run in both the old solver and the new solver, and the test suite + // now picks the module resolver as appropriate in order to better mimic the studio code path, + // we have to load the definition file into both the 'globals'/'resolver' and the equivalent + // 'for autocomplete'. + loadDefinition(fakeVecDecl); + loadDefinition(fakeVecDecl, /* For Autocomplete Module */ true); + } }; TEST_SUITE_BEGIN("FragmentAutocompleteTraversalTests"); @@ -316,11 +334,11 @@ local z = x + y Position{3, 15} ); - CHECK_EQ("\nlocal z = x + y", fragment.fragmentToParse); + CHECK_EQ("local y = 5\nlocal z = x + y", fragment.fragmentToParse); CHECK_EQ(5, fragment.ancestry.size()); REQUIRE(fragment.root); - CHECK_EQ(1, fragment.root->body.size); - auto stat = fragment.root->body.data[0]->as(); + CHECK_EQ(2, fragment.root->body.size); + auto stat = fragment.root->body.data[1]->as(); REQUIRE(stat); CHECK_EQ(1, stat->vars.size); CHECK_EQ(1, stat->values.size); @@ -422,7 +440,7 @@ abc("bar") Position{1, 10} ); - CHECK_EQ("\nabc(\"foo\")", callFragment.fragmentToParse); + CHECK_EQ("function abc(foo: string) end\nabc(\"foo\")", callFragment.fragmentToParse); CHECK(callFragment.nearestStatement->is()); CHECK_GE(callFragment.ancestry.size(), 2); @@ -447,7 +465,7 @@ abc("bar") Position{1, 9} ); - CHECK_EQ("\nabc(\"foo\"", stringFragment.fragmentToParse); + CHECK_EQ("function abc(foo: string) end\nabc(\"foo\"", stringFragment.fragmentToParse); CHECK(stringFragment.nearestStatement->is()); CHECK_GE(stringFragment.ancestry.size(), 1); @@ -482,7 +500,7 @@ abc("bar") Position{3, 1} ); - CHECK_EQ("\nabc(\n\"foo\"\n)", fragment.fragmentToParse); + CHECK_EQ("function abc(foo: string) end\nabc(\n\"foo\"\n)", fragment.fragmentToParse); CHECK(fragment.nearestStatement->is()); CHECK_GE(fragment.ancestry.size(), 2); @@ -1223,4 +1241,140 @@ TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "method_call_inside_function_body ); } +TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tbl_function_parameter") +{ + const std::string source = R"( +--!strict +type Foo = {x : number, y : number} +local function func(abc : Foo) + abc. +end +)"; + + autocompleteFragmentInBothSolvers( + source, + source, + Position{4, 7}, + [](FragmentAutocompleteResult& result) + { + CHECK_EQ(2, result.acResults.entryMap.size()); + CHECK(result.acResults.entryMap.count("x")); + CHECK(result.acResults.entryMap.count("y")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteFixture, "tbl_local_function_parameter") +{ + const std::string source = R"( +--!strict +type Foo = {x : number, y : number} +local function func(abc : Foo) + abc. +end +)"; + + autocompleteFragmentInBothSolvers( + source, + source, + Position{4, 7}, + [](FragmentAutocompleteResult& result) + { + CHECK_EQ(2, result.acResults.entryMap.size()); + CHECK(result.acResults.entryMap.count("x")); + CHECK(result.acResults.entryMap.count("y")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "vec3_function_parameter") +{ + const std::string source = R"( +--!strict +local function func(abc : FakeVec) + abc. +end +)"; + + autocompleteFragmentInBothSolvers( + source, + source, + Position{3, 7}, + [](FragmentAutocompleteResult& result) + { + CHECK_EQ(2, result.acResults.entryMap.size()); + CHECK(result.acResults.entryMap.count("zero")); + CHECK(result.acResults.entryMap.count("dot")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "vec3_local_function_parameter") +{ + const std::string source = R"( +--!strict +local function func(abc : FakeVec) + abc. +end +)"; + + autocompleteFragmentInBothSolvers( + source, + source, + Position{3, 7}, + [](FragmentAutocompleteResult& result) + { + CHECK_EQ(2, result.acResults.entryMap.size()); + CHECK(result.acResults.entryMap.count("zero")); + CHECK(result.acResults.entryMap.count("dot")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "function_parameter_not_recommending_out_of_scope_argument") +{ + const std::string source = R"( +--!strict +local function foo(abd: FakeVec) +end +local function bar(abc : FakeVec) + a +end +)"; + + autocompleteFragmentInBothSolvers( + source, + source, + Position{5, 5}, + [](FragmentAutocompleteResult& result) + { + CHECK(result.acResults.entryMap.count("abc")); + CHECK(!result.acResults.entryMap.count("abd")); + } + ); +} + +TEST_CASE_FIXTURE(FragmentAutocompleteBuiltinsFixture, "bad_range") +{ + const std::string source = R"( +l +)"; + const std::string updated = R"( +local t = 1 +t +)"; + + autocompleteFragmentInBothSolvers( + source, + updated, + Position{2, 1}, + [](FragmentAutocompleteResult& result) + { + auto opt = linearSearchForBinding(result.freshScope, "t"); + REQUIRE(opt); + CHECK_EQ("number", toString(*opt)); + } + ); +} + TEST_SUITE_END(); diff --git a/tests/Linter.test.cpp b/tests/Linter.test.cpp index 8647777aa..9162ccf38 100644 --- a/tests/Linter.test.cpp +++ b/tests/Linter.test.cpp @@ -8,7 +8,6 @@ #include "doctest.h" LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTFLAG(LuauNativeAttribute); LUAU_FASTFLAG(LintRedundantNativeAttribute); using namespace Luau; @@ -1999,7 +1998,7 @@ local _ = a <= (b == 0) TEST_CASE_FIXTURE(Fixture, "RedundantNativeAttribute") { - ScopedFastFlag sff[] = {{FFlag::LuauNativeAttribute, true}, {FFlag::LintRedundantNativeAttribute, true}}; + ScopedFastFlag sff[] = {{FFlag::LintRedundantNativeAttribute, true}}; LintResult result = lint(R"( --!native diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index 30b0da1f5..5aacceb4c 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -16,10 +16,10 @@ LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTINT(LuauTypeLengthLimit) LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr) LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2) LUAU_FASTFLAG(LuauUserDefinedTypeFunParseExport) LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams) +LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes) namespace { @@ -3351,8 +3351,6 @@ end)"); TEST_CASE_FIXTURE(Fixture, "parse_attribute_for_function_expression") { - ScopedFastFlag sff[] = {{FFlag::LuauAttributeSyntaxFunExpr, true}}; - AstStatBlock* stat1 = parse(R"( local function invoker(f) return f(1) @@ -3521,8 +3519,6 @@ function foo1 () @checked return 'a' end TEST_CASE_FIXTURE(Fixture, "dont_parse_attribute_on_argument_non_function") { - ScopedFastFlag sff[] = {{FFlag::LuauAttributeSyntaxFunExpr, true}}; - ParseResult pr = tryParse(R"( local function invoker(f, y) return f(y) @@ -3743,5 +3739,18 @@ TEST_CASE_FIXTURE(Fixture, "complex_union_in_generic_ty") } } +TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type") +{ + ScopedFastFlag _{FFlag::LuauErrorRecoveryForTableTypes, true}; + ParseOptions opts; + opts.allowDeclarationSyntax = true; + const auto result = tryParse(R"( + declare class Widget + state: {string: function(string, Widget)} + end + )", opts); + CHECK_EQ(result.errors.size(), 2); +} + TEST_SUITE_END(); diff --git a/tests/ToDot.test.cpp b/tests/ToDot.test.cpp index cd7f6add0..c9eb34502 100644 --- a/tests/ToDot.test.cpp +++ b/tests/ToDot.test.cpp @@ -431,7 +431,7 @@ n1 [label="FreeTypePack 1"]; TEST_CASE_FIXTURE(Fixture, "error_pack") { - TypePackVar pack{TypePackVariant{Unifiable::Error{}}}; + TypePackVar pack{TypePackVariant{ErrorTypePack{}}}; ToDotOptions opts; opts.showPointers = false; diff --git a/tests/ToString.test.cpp b/tests/ToString.test.cpp index dedf88240..db018eda7 100644 --- a/tests/ToString.test.cpp +++ b/tests/ToString.test.cpp @@ -13,7 +13,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauRecursiveTypeParameterRestriction); LUAU_FASTFLAG(LuauSolverV2); LUAU_FASTFLAG(LuauAttributeSyntax); -LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2) TEST_SUITE_BEGIN("ToString"); @@ -969,7 +968,7 @@ TEST_CASE_FIXTURE(Fixture, "correct_stringification_user_defined_type_functions" Type tv{tftt}; - if (FFlag::LuauSolverV2 && FFlag::LuauUserDefinedTypeFunctions2) + if (FFlag::LuauSolverV2) CHECK_EQ(toString(&tv, {}), "woohoo"); } diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 68bfff57a..4aa6b6800 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -13,7 +13,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2) LUAU_DYNAMIC_FASTINT(LuauTypeFamilyApplicationCartesianProductLimit) struct TypeFunctionFixture : Fixture diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index eca633a8b..909017cc9 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -9,8 +9,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2) -LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2) -LUAU_FASTFLAG(LuauUserDefinedTypeFunctionNoEvaluation) LUAU_FASTFLAG(LuauUserTypeFunFixRegister) LUAU_FASTFLAG(LuauUserTypeFunFixNoReadWrite) LUAU_FASTFLAG(LuauUserTypeFunFixMetatable) @@ -25,7 +23,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_nil(arg) @@ -42,7 +39,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_nil_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getnil() @@ -63,7 +59,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_unknown(arg) @@ -80,7 +75,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_unknown_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getunknown() @@ -101,7 +95,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_never(arg) @@ -118,7 +111,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_never_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getnever() @@ -139,7 +131,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_any(arg) @@ -156,7 +147,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_any_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getany() @@ -177,7 +167,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_bool(arg) @@ -194,7 +183,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolean_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getboolean() @@ -215,7 +203,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_num(arg) @@ -232,7 +219,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_number_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getnumber() @@ -253,7 +239,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_str(arg) @@ -270,7 +255,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_string_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getstring() @@ -291,7 +275,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_boolsingleton(arg) @@ -308,7 +291,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_boolsingleton_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getboolsingleton() @@ -329,7 +311,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_strsingleton(arg) @@ -346,7 +327,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strsingleton_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getstrsingleton() @@ -367,7 +347,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_union(arg) @@ -388,7 +367,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_union_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getunion() @@ -418,7 +396,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_intersection(arg) @@ -439,7 +416,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_intersection_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getintersection() @@ -475,7 +451,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_negation_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getnegation() @@ -501,7 +476,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_table(arg) @@ -522,7 +496,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_table_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function gettable() @@ -562,7 +535,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_metatable_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getmetatable() @@ -596,7 +568,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_func(arg) @@ -613,7 +584,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_methods_work") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getfunction() @@ -644,7 +614,6 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_class(arg) @@ -660,7 +629,6 @@ TEST_CASE_FIXTURE(ClassFixture, "udtf_class_methods_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( @@ -684,7 +652,6 @@ TEST_CASE_FIXTURE(ClassFixture, "write_of_readonly_is_nil") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag udtfRwFix{FFlag::LuauUserTypeFunFixNoReadWrite, true}; @@ -714,7 +681,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_check_mutability") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function checkmut() @@ -747,7 +713,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_copy_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function getcopy() @@ -781,7 +746,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_simple_cyclic_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_cycle(arg) @@ -803,7 +767,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_createtable_bad_metatable") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function badmetatable() @@ -825,7 +788,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_complex_cyclic_serialization_works") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function serialize_cycle2(arg) @@ -855,7 +817,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_user_error_is_reported") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function errors_if_string(arg) @@ -878,7 +839,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_call_metamethod") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function hello(arg) @@ -897,7 +857,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_type_overrides_eq_metamethod") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function hello() @@ -923,7 +882,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_function_type_cant_call_get_props") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function hello(arg) @@ -945,7 +903,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_each_other") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function foo() @@ -967,7 +924,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_no_shared_state") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function foo() @@ -996,7 +952,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_math_reset") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserDefinedTypeFunctionResetState{FFlag::LuauUserDefinedTypeFunctionResetState, true}; CheckResult result = check(R"( @@ -1013,7 +968,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_optionify") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function optionify(tbl) @@ -1043,7 +997,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_calling_illegal_global") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function illegal(arg) @@ -1065,7 +1018,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recursion_and_gc") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function foo(tbl) @@ -1091,8 +1043,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_recovery_no_upvalues") { ScopedFastFlag solverV2{FFlag::LuauSolverV2, true}; ScopedFastFlag userDefinedTypeFunctionsSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag userDefinedTypeFunctions{FFlag::LuauUserDefinedTypeFunctions2, true}; - ScopedFastFlag userDefinedTypeFunctionNoEvaluation{FFlag::LuauUserDefinedTypeFunctionNoEvaluation, true}; CheckResult result = check(R"( local var @@ -1116,7 +1066,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_follow") { ScopedFastFlag solverV2{FFlag::LuauSolverV2, true}; ScopedFastFlag userDefinedTypeFunctionsSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag userDefinedTypeFunctions{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type t0 = any @@ -1133,7 +1082,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "udtf_strip_indexer") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; CheckResult result = check(R"( type function stripindexer(tbl) @@ -1159,7 +1107,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_type_methods_on_types") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; CheckResult result = check(R"( @@ -1177,7 +1124,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_types_functions_on_type") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; CheckResult result = check(R"( @@ -1195,7 +1141,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_metatable_writes") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; CheckResult result = check(R"( @@ -1215,7 +1160,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_eq_field") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; CheckResult result = check(R"( @@ -1233,7 +1177,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "tag_field") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; CheckResult result = check(R"( @@ -1256,7 +1199,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "metatable_serialization") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; ScopedFastFlag luauUserTypeFunFixMetatable{FFlag::LuauUserTypeFunFixMetatable, true}; @@ -1288,7 +1230,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "nonstrict_mode") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; ScopedFastFlag luauUserTypeFunNonstrict{FFlag::LuauUserTypeFunNonstrict, true}; @@ -1304,7 +1245,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "implicit_export") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true}; @@ -1335,7 +1275,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "local_scope") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true}; @@ -1361,7 +1300,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "explicit_export") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; ScopedFastFlag udtfSyntax{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag udtf{FFlag::LuauUserDefinedTypeFunctions2, true}; ScopedFastFlag luauUserTypeFunFixRegister{FFlag::LuauUserTypeFunFixRegister, true}; ScopedFastFlag luauUserTypeFunExportedAndLocal{FFlag::LuauUserTypeFunExportedAndLocal, true}; ScopedFastFlag luauUserDefinedTypeFunParseExport{FFlag::LuauUserDefinedTypeFunParseExport, true}; diff --git a/tests/TypeInfer.aliases.test.cpp b/tests/TypeInfer.aliases.test.cpp index 9cdaf1a17..2b5e64be4 100644 --- a/tests/TypeInfer.aliases.test.cpp +++ b/tests/TypeInfer.aliases.test.cpp @@ -10,7 +10,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauUserDefinedTypeFunctionsSyntax2) -LUAU_FASTFLAG(LuauUserDefinedTypeFunctions2) TEST_SUITE_BEGIN("TypeAliases"); @@ -1154,7 +1153,7 @@ type Foo = Foo | string TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_function") { - if (!FFlag::LuauSolverV2 || !FFlag::LuauUserDefinedTypeFunctions2) + if (!FFlag::LuauSolverV2) return; CheckResult result = check(R"( @@ -1166,20 +1165,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "type_alias_adds_reduce_constraint_for_type_f LUAU_CHECK_NO_ERRORS(result); } -TEST_CASE_FIXTURE(Fixture, "user_defined_type_function_errors") -{ - ScopedFastFlag sff{FFlag::LuauUserDefinedTypeFunctionsSyntax2, true}; - ScopedFastFlag noUDTFimpl{FFlag::LuauUserDefinedTypeFunctions2, false}; - - CheckResult result = check(R"( - type function foo() - return nil - end - )"); - LUAU_CHECK_ERROR_COUNT(1, result); - CHECK(toString(result.errors[0]) == "This syntax is not supported"); -} - TEST_CASE_FIXTURE(Fixture, "bound_type_in_alias_segfault") { ScopedFastFlag sff{FFlag::LuauSolverV2, true}; diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 064af97cb..dcbc712ed 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -2049,10 +2049,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "refinements_should_preserve_error_suppressio end )"); - if (FFlag::LuauSolverV2) - LUAU_REQUIRE_NO_ERRORS(result); - else - LUAU_REQUIRE_NO_ERRORS(result); + LUAU_REQUIRE_NO_ERRORS(result); } TEST_CASE_FIXTURE(BuiltinsFixture, "many_refinements_on_val") diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index d11a5f302..ee561e2f2 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -20,6 +20,7 @@ LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauFixIndexerSubtypingOrdering) LUAU_FASTFLAG(LuauRetrySubtypingWithoutHiddenPack) LUAU_FASTFLAG(LuauTableKeysAreRValues) +LUAU_FASTFLAG(LuauAllowNilAssignmentToIndexer) TEST_SUITE_BEGIN("TableTests"); @@ -1925,18 +1926,128 @@ TEST_CASE_FIXTURE(Fixture, "type_mismatch_on_massive_table_is_cut_short") TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_even_on_non_lvalue_base_expr") { - // CLI-100076 Assigning nil to an indexer should always succeed - DOES_NOT_PASS_NEW_SOLVER_GUARD(); + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; - CheckResult result = check(R"( + LUAU_REQUIRE_NO_ERRORS(check(R"( local function f(): { [string]: number } return { ["foo"] = 1 } end f()["foo"] = nil + )")); + + LUAU_REQUIRE_NO_ERRORS(check(R"( + local function f( + t: {known_prop: boolean, [string]: number}, + key: string + ) + t[key] = nil + t["hello"] = nil + t.undefined = nil + end + )")); + + auto result = check(R"( + local function f(t: {known_prop: boolean, [string]: number, }) + t.known_prop = nil + end )"); - LUAU_REQUIRE_NO_ERRORS(result); + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK_EQ(Location{{2, 27}, {2, 30}}, result.errors[0].location); + CHECK_EQ("Type 'nil' could not be converted into 'boolean'", toString(result.errors[0])); + + loadDefinition(R"( + declare class FancyHashtable + [string]: number + real_property: string + end + )"); + + LUAU_REQUIRE_NO_ERRORS(check(R"( + local function removekey(fh: FancyHashtable, other_key: string) + fh["hmmm"] = nil + fh[other_key] = nil + fh.dne = nil + end + )")); + + result = check(R"( + local function removekey(fh: FancyHashtable) + fh.real_property = nil + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK_EQ(result.errors[0].location, Location{{2, 31}, {2, 34}}); + CHECK_EQ(toString(result.errors[0]), "Type 'nil' could not be converted into 'string'"); +} + +TEST_CASE_FIXTURE(Fixture, "ok_to_set_nil_on_generic_map") +{ + + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + type MyMap = { [K]: V } + function set(m: MyMap, k: K, v: V) + m[k] = v + end + function unset(m: MyMap, k: K) + m[k] = nil + end + local m: MyMap = {} + set(m, "foo", true) + unset(m, "foo") + )")); +} + +TEST_CASE_FIXTURE(Fixture, "key_setting_inference_given_nil_upper_bound") +{ + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + local function setkey_object(t: { [string]: number }, v) + t.foo = v + t.foo = nil + end + local function setkey_constindex(t: { [string]: number }, v) + t["foo"] = v + t["foo"] = nil + end + local function setkey_unknown(t: { [string]: number }, k, v) + t[k] = v + t[k] = nil + end + )")); + CHECK_EQ(toString(requireType("setkey_object")), "({ [string]: number }, number) -> ()"); + CHECK_EQ(toString(requireType("setkey_constindex")), "({ [string]: number }, number) -> ()"); + CHECK_EQ(toString(requireType("setkey_unknown")), "({ [string]: number }, string, number) -> ()"); + + LUAU_REQUIRE_NO_ERRORS(check(R"( + local function on_number(v: number): () end + local function setkey_object(t: { [string]: number }, v) + t.foo = v + on_number(v) + end + )")); + CHECK_EQ(toString(requireType("setkey_object")), "({ [string]: number }, number) -> ()"); +} + +TEST_CASE_FIXTURE(Fixture, "explicit_nil_indexer") +{ + + ScopedFastFlag _{FFlag::LuauSolverV2, true}; + + auto result = check(R"( + local function _(t: { [string]: number? }): number + return t.hello + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK_EQ(result.errors[0].location, Location{{2, 12}, {2, 26}}); + CHECK(get(result.errors[0])); } TEST_CASE_FIXTURE(Fixture, "ok_to_provide_a_subtype_during_construction") @@ -2843,17 +2954,20 @@ TEST_CASE_FIXTURE(Fixture, "nil_assign_doesnt_hit_indexer") TEST_CASE_FIXTURE(Fixture, "wrong_assign_does_hit_indexer") { + ScopedFastFlag sffs[] = {{FFlag::LuauSolverV2, true}, {FFlag::LuauAllowNilAssignmentToIndexer, true}}; + CheckResult result = check(R"( local a = {} a[0] = 7 a[0] = 't' + a[0] = nil )"); LUAU_REQUIRE_ERROR_COUNT(1, result); CHECK((Location{Position{3, 15}, Position{3, 18}}) == result.errors[0].location); TypeMismatch* tm = get(result.errors[0]); REQUIRE(tm); - CHECK(tm->wantedType == builtinTypes->numberType); + CHECK_EQ("number?", toString(tm->wantedType)); CHECK(tm->givenType == builtinTypes->stringType); } diff --git a/tests/TypePack.test.cpp b/tests/TypePack.test.cpp index 7d8ed38f7..85425f777 100644 --- a/tests/TypePack.test.cpp +++ b/tests/TypePack.test.cpp @@ -199,14 +199,14 @@ TEST_CASE_FIXTURE(TypePackFixture, "std_distance") TEST_CASE("content_reassignment") { - TypePackVar myError{Unifiable::Error{}, /*presistent*/ true}; + TypePackVar myError{ErrorTypePack{}, /*presistent*/ true}; TypeArena arena; TypePackId futureError = arena.addTypePack(TypePackVar{FreeTypePack{TypeLevel{}}}); asMutable(futureError)->reassign(myError); - CHECK(get(futureError) != nullptr); + CHECK(get(futureError) != nullptr); CHECK(!futureError->persistent); CHECK(futureError->owningArena == &arena); } diff --git a/tests/conformance/vector_library.lua b/tests/conformance/vector_library.lua index f41650d1b..3f30d900d 100644 --- a/tests/conformance/vector_library.lua +++ b/tests/conformance/vector_library.lua @@ -1,6 +1,9 @@ -- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details print('testing vector library') +-- detect vector size +local vector_size = if pcall(function() return vector(0, 0, 0).w end) then 4 else 3 + function ecall(fn, ...) local ok, err = pcall(fn, ...) assert(not ok) @@ -156,4 +159,32 @@ assert(vector.clamp(vector.create(1, 1, 1), vector.create(0, 1, 2), vector.creat assert(vector.clamp(vector.create(1, 1, 1), vector.create(-1, -1, -1), vector.create(0, 1, 2)) == vector.create(0, 1, 1)) assert(select("#", vector.clamp(vector.zero, vector.zero, vector.one)) == 1) +-- validate component access +assert(vector.create(1, 2, 3).x == 1) +assert(vector.create(1, 2, 3).X == 1) +assert(vector.create(1, 2, 3)['X'] == 1) +assert(vector.create(1, 2, 3).y == 2) +assert(vector.create(1, 2, 3).Y == 2) +assert(vector.create(1, 2, 3)['Y'] == 2) +assert(vector.create(1, 2, 3).z == 3) +assert(vector.create(1, 2, 3).Z == 3) +assert(vector.create(1, 2, 3)['Z'] == 3) + +local function getcomp(v: vector, field: string) + return v[field] +end + +assert(getcomp(vector.create(1, 2, 3), 'x') == 1) +assert(getcomp(vector.create(1, 2, 3), 'y') == 2) +assert(getcomp(vector.create(1, 2, 3), 'z') == 3) + +assert(ecall(function() return vector.create(1, 2, 3).zz end) == "attempt to index vector with 'zz'") + +-- additional checks for 4-component vectors +if vector_size == 4 then + assert(vector.create(1, 2, 3, 4).w == 4) + assert(vector.create(1, 2, 3, 4).W == 4) + assert(vector.create(1, 2, 3, 4)['W'] == 4) +end + return 'OK'