diff --git a/Analysis/include/Luau/Clone.h b/Analysis/include/Luau/Clone.h index 5f383dc4..53f0df10 100644 --- a/Analysis/include/Luau/Clone.h +++ b/Analysis/include/Luau/Clone.h @@ -29,11 +29,10 @@ struct CloneState * Be mindful about which behavior you actually _want_. * * Persistent types are not cloned as an optimization. - * If a type is cloned in order to mutate it, 'ignorePersistent' has to be set + * If a type is cloned in order to mutate it, 'clonePersistentTypes' has to be set */ - -TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent = false); -TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent = false); +TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes); +TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes); TypePackId clone(TypePackId tp, TypeArena& dest, CloneState& cloneState); TypeId clone(TypeId tp, TypeArena& dest, CloneState& cloneState); diff --git a/Analysis/include/Luau/ConstraintGenerator.h b/Analysis/include/Luau/ConstraintGenerator.h index 3e1a9559..fc97316f 100644 --- a/Analysis/include/Luau/ConstraintGenerator.h +++ b/Analysis/include/Luau/ConstraintGenerator.h @@ -143,8 +143,8 @@ struct ConstraintGenerator NotNull moduleResolver, NotNull builtinTypes, NotNull ice, - const ScopePtr& globalScope, - const ScopePtr& typeFunctionScope, + ScopePtr globalScope, + ScopePtr typeFunctionScope, std::function prepareModuleScope, DcrLogger* logger, NotNull dfg, diff --git a/Analysis/include/Luau/Frontend.h b/Analysis/include/Luau/Frontend.h index 377cbc1b..3b5906b0 100644 --- a/Analysis/include/Luau/Frontend.h +++ b/Analysis/include/Luau/Frontend.h @@ -9,7 +9,6 @@ #include "Luau/Scope.h" #include "Luau/Set.h" #include "Luau/TypeCheckLimits.h" -#include "Luau/Variant.h" #include #include @@ -116,6 +115,8 @@ struct FrontendOptions // An optional callback which is called for every *dirty* module was checked // Is multi-threaded typechecking is used, this callback might be called from multiple threads and has to be thread-safe std::function customModuleCheck; + + bool collectTypeAllocationStats = false; }; struct CheckResult @@ -139,6 +140,7 @@ struct FrontendModuleResolver : ModuleResolver bool setModule(const ModuleName& moduleName, ModulePtr module); void clearModules(); + private: Frontend* frontend; @@ -156,6 +158,13 @@ struct Frontend size_t filesStrict = 0; size_t filesNonstrict = 0; + size_t typesAllocated = 0; + size_t typePacksAllocated = 0; + + size_t boolSingletonsMinted = 0; + size_t strSingletonsMinted = 0; + size_t uniqueStrSingletonsMinted = 0; + double timeRead = 0; double timeParse = 0; double timeCheck = 0; @@ -164,6 +173,11 @@ struct Frontend Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, const FrontendOptions& options = {}); + void setLuauSolverSelectionFromWorkspace(bool newSolverEnabled); + bool getLuauSolverSelection() const; + bool getLuauSolverSelectionFlagged() const; + // The default value assuming there is no workspace setup yet + std::atomic useNewLuauSolver{FFlag::LuauSolverV2}; // Parse module graph and prepare SourceNode/SourceModule data, including required dependencies without running typechecking void parse(const ModuleName& name); void parseModules(const std::vector& name); diff --git a/Analysis/include/Luau/Simplify.h b/Analysis/include/Luau/Simplify.h index 01c1c9a2..ebb14259 100644 --- a/Analysis/include/Luau/Simplify.h +++ b/Analysis/include/Luau/Simplify.h @@ -5,6 +5,8 @@ #include "Luau/DenseHash.h" #include "Luau/NotNull.h" #include "Luau/TypeFwd.h" + +#include #include namespace Luau @@ -27,6 +29,13 @@ SimplifyResult simplifyUnion(NotNull builtinTypes, NotNull builtinTypes, NotNull arena, TypeId target); SimplifyResult simplifyIntersectWithFalsy(NotNull builtinTypes, NotNull arena, TypeId target); +std::optional intersectWithSimpleDiscriminant( + NotNull builtinTypes, + NotNull arena, + TypeId target, + TypeId discriminant +); + enum class Relation { Disjoint, // No A is a B or vice versa diff --git a/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index f3de41a8..95c0c891 100644 --- a/Analysis/include/Luau/Type.h +++ b/Analysis/include/Luau/Type.h @@ -456,9 +456,8 @@ struct Property std::optional typeLocation = std::nullopt ); - // DEPRECATED: Should only be called in non-RWP! We assert that the `readTy` is not nullopt. - // TODO: Kill once we don't have non-RWP. - TypeId type() const; + // DEPRECATED: This should be removed with `LuauTypeSolverV2` clean up + TypeId type_DEPRECATED() const; void setType(TypeId ty); // If this property has a present `writeTy`, set it equal to the `readTy`. @@ -565,13 +564,13 @@ struct ExternType ModuleName definitionModuleName, std::optional definitionLocation ) - : name(name) - , props(props) + : name(std::move(name)) + , props(std::move(props)) , parent(parent) , metatable(metatable) - , tags(tags) - , userData(userData) - , definitionModuleName(definitionModuleName) + , tags(std::move(tags)) + , userData(std::move(userData)) + , definitionModuleName(std::move(definitionModuleName)) , definitionLocation(definitionLocation) { } @@ -587,13 +586,13 @@ struct ExternType std::optional definitionLocation, std::optional indexer ) - : name(name) - , props(props) + : name(std::move(name)) + , props(std::move(props)) , parent(parent) , metatable(metatable) - , tags(tags) - , userData(userData) - , definitionModuleName(definitionModuleName) + , tags(std::move(tags)) + , userData(std::move(userData)) + , definitionModuleName(std::move(definitionModuleName)) , definitionLocation(definitionLocation) , indexer(indexer) { @@ -638,31 +637,31 @@ struct TypeFunctionInstanceType UserDefinedFunctionData userFuncData ) : function(function) - , typeArguments(typeArguments) - , packArguments(packArguments) + , typeArguments(std::move(typeArguments)) + , packArguments(std::move(packArguments)) , userFuncName(userFuncName) - , userFuncData(userFuncData) + , userFuncData(std::move(userFuncData)) { } TypeFunctionInstanceType(const TypeFunction& function, std::vector typeArguments) : function{&function} - , typeArguments(typeArguments) + , typeArguments(std::move(typeArguments)) , packArguments{} { } TypeFunctionInstanceType(const TypeFunction& function, std::vector typeArguments, std::vector packArguments) : function{&function} - , typeArguments(typeArguments) - , packArguments(packArguments) + , typeArguments(std::move(typeArguments)) + , packArguments(std::move(packArguments)) { } TypeFunctionInstanceType(NotNull function, std::vector typeArguments, std::vector packArguments) : function{function} - , typeArguments(typeArguments) - , packArguments(packArguments) + , typeArguments(std::move(typeArguments)) + , packArguments(std::move(packArguments)) { } }; @@ -713,7 +712,7 @@ struct LazyType { LazyType() = default; LazyType(std::function unwrap) - : unwrap(unwrap) + : unwrap(std::move(unwrap)) { } @@ -800,8 +799,8 @@ struct Type final { } - Type(const TypeVariant& ty, bool persistent) - : ty(ty) + Type(TypeVariant ty, bool persistent) + : ty(std::move(ty)) , persistent(persistent) { } diff --git a/Analysis/include/Luau/TypeArena.h b/Analysis/include/Luau/TypeArena.h index dbc5d513..203a62d9 100644 --- a/Analysis/include/Luau/TypeArena.h +++ b/Analysis/include/Luau/TypeArena.h @@ -8,6 +8,8 @@ #include +LUAU_FASTFLAG(LuauTrackTypeAllocations) + namespace Luau { struct Module; @@ -20,6 +22,11 @@ struct TypeArena // Owning module, if any Module* owningModule = nullptr; + bool collectSingletonStats = false; + size_t boolSingletonsMinted = 0; + size_t strSingletonsMinted = 0; + DenseHashSet> uniqueStrSingletonsMinted{std::nullopt}; + void clear(); template @@ -28,6 +35,12 @@ struct TypeArena if constexpr (std::is_same_v) LUAU_ASSERT(tv.options.size() >= 2); + if constexpr (std::is_same_v) + { + if (FFlag::LuauTrackTypeAllocations && collectSingletonStats) + recordSingletonStats(NotNull{&tv}); + } + return addTV(Type(std::move(tv))); } @@ -54,6 +67,8 @@ struct TypeArena TypeId addTypeFunction(const TypeFunction& function, std::vector typeArguments, std::vector packArguments = {}); TypePackId addTypePackFunction(const TypePackFunction& function, std::initializer_list types); TypePackId addTypePackFunction(const TypePackFunction& function, std::vector typeArguments, std::vector packArguments = {}); + + void recordSingletonStats(NotNull singleton); }; void freeze(TypeArena& arena); diff --git a/Analysis/include/Luau/TypeUtils.h b/Analysis/include/Luau/TypeUtils.h index 8326d618..de4d9413 100644 --- a/Analysis/include/Luau/TypeUtils.h +++ b/Analysis/include/Luau/TypeUtils.h @@ -318,6 +318,32 @@ std::optional extractMatchingTableType(std::vector& tables, Type */ bool isRecord(const AstExprTable::Item& item); +/** + * Do a quick check for whether the type `ty` is exactly `false | nil`. This + * will *not* do any sort of semantic analysis, for example the type: + * + * (boolean?) & (false | nil) + * + * ... will not be considered falsy, despite it being semantically equivalent + * to `false | nil`. + * + * @return Whether the input is approximately `false | nil`. + */ +bool isApproximatelyFalsyType(TypeId ty); + +/** + * Do a quick check for whether the type `ty` is exactly `~(false | nil)`. + * This will *not* do any sort of semantic analysis, for example the type: + * + * unknown & ~(false | nil) + * + * ... will not be considered falsy, despite it being semantically equivalent + * to `~(false | nil)`. + * + * @return Whether the input is approximately `~(false | nil)`. + */ +bool isApproximatelyTruthyType(TypeId ty); + // Unwraps any grouping expressions iteratively. AstExpr* unwrapGroup(AstExpr* expr); diff --git a/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index 06e20a94..3621d1bb 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/Analysis/include/Luau/VisitType.h @@ -294,7 +294,7 @@ struct GenericTypeVisitor traverse(*ty); } else - traverse(prop.type()); + traverse(prop.type_DEPRECATED()); } if (ttv->indexer) @@ -332,7 +332,7 @@ struct GenericTypeVisitor traverse(*ty); } else - traverse(prop.type()); + traverse(prop.type_DEPRECATED()); } if (etv->parent) diff --git a/Analysis/src/AstJsonEncoder.cpp b/Analysis/src/AstJsonEncoder.cpp index 2ae7d8c1..65b3018e 100644 --- a/Analysis/src/AstJsonEncoder.cpp +++ b/Analysis/src/AstJsonEncoder.cpp @@ -8,8 +8,6 @@ #include -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - namespace Luau { @@ -433,16 +431,8 @@ struct AstJsonEncoder : public AstVisitor if (node->self) PROP(self); PROP(args); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - if (node->returnAnnotation) - PROP(returnAnnotation); - } - else - { - if (node->returnAnnotation_DEPRECATED) - write("returnAnnotation", node->returnAnnotation_DEPRECATED); - } + if (node->returnAnnotation) + PROP(returnAnnotation); PROP(vararg); PROP(varargLocation); if (node->varargAnnotation) @@ -912,10 +902,7 @@ struct AstJsonEncoder : public AstVisitor PROP(paramNames); PROP(vararg); PROP(varargLocation); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - PROP(retTypes); - else - write("retTypes", node->retTypes_DEPRECATED); + PROP(retTypes); PROP(generics); PROP(genericPacks); } @@ -1061,10 +1048,7 @@ struct AstJsonEncoder : public AstVisitor PROP(genericPacks); PROP(argTypes); PROP(argNames); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - PROP(returnTypes); - else - write("returnTypes", node->returnTypes_DEPRECATED); + PROP(returnTypes); } ); } diff --git a/Analysis/src/AstQuery.cpp b/Analysis/src/AstQuery.cpp index ec674d93..602c6a0e 100644 --- a/Analysis/src/AstQuery.cpp +++ b/Analysis/src/AstQuery.cpp @@ -12,6 +12,7 @@ #include LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -524,7 +525,18 @@ static std::optional getMetatableDocumentation( if (indexIt == mtable->props.end()) return std::nullopt; - TypeId followed = follow(indexIt->second.type()); + TypeId followed; + if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + if (indexIt->second.readTy) + followed = follow(*indexIt->second.readTy); + else if (indexIt->second.writeTy) + followed = follow(*indexIt->second.writeTy); + else + return std::nullopt; + } + else + followed = follow(indexIt->second.type_DEPRECATED()); const TableType* ttv = get(followed); if (!ttv) return std::nullopt; @@ -539,7 +551,7 @@ static std::optional getMetatableDocumentation( return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol); } else - return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol); + return checkOverloadedDocumentationSymbol(module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol); return std::nullopt; } @@ -571,7 +583,7 @@ std::optional getDocumentationSymbolAtPosition(const Source return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol); } else - return checkOverloadedDocumentationSymbol(module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol); + return checkOverloadedDocumentationSymbol(module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol); } } else if (const ExternType* etv = get(parentTy)) @@ -587,7 +599,7 @@ std::optional getDocumentationSymbolAtPosition(const Source } else return checkOverloadedDocumentationSymbol( - module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol + module, propIt->second.type_DEPRECATED(), parentExpr, propIt->second.documentationSymbol ); } etv = etv->parent ? Luau::get(*etv->parent) : nullptr; diff --git a/Analysis/src/AutocompleteCore.cpp b/Analysis/src/AutocompleteCore.cpp index 78a80448..cc108cb1 100644 --- a/Analysis/src/AutocompleteCore.cpp +++ b/Analysis/src/AutocompleteCore.cpp @@ -21,11 +21,12 @@ #include LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTINT(LuauTypeInferIterationLimit) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAGVARIABLE(DebugLuauMagicVariableNames) LUAU_FASTFLAGVARIABLE(LuauAutocompleteMissingFollows) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) +LUAU_FASTFLAG(LuauImplicitTableIndexerKeys2) static const std::unordered_set kStatementStartingKeywords = {"while", "if", "local", "repeat", "function", "do", "for", "return", "break", "continue", "type", "export"}; @@ -336,7 +337,7 @@ static void autocompleteProps( continue; } else - type = follow(prop.type()); + type = follow(prop.type_DEPRECATED()); TypeCorrectKind typeCorrect = indexType == PropIndexType::Key ? TypeCorrectKind::Correct @@ -368,7 +369,11 @@ static void autocompleteProps( auto indexIt = mtable->props.find("__index"); if (indexIt != mtable->props.end()) { - TypeId followed = follow(indexIt->second.type()); + TypeId followed; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + followed = follow(*indexIt->second.readTy); + else + followed = follow(indexIt->second.type_DEPRECATED()); if (get(followed) || get(followed)) { autocompleteProps(module, typeArena, builtinTypes, rootTy, followed, indexType, nodes, result, seen); @@ -652,7 +657,6 @@ static std::optional findTypeElementAt(const AstTypeList& astTypeList, T static std::optional findTypeElementAt(AstTypePack* astTypePack, TypePackId tp, Position position) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); if (const auto typePack = astTypePack->as()) { return findTypeElementAt(typePack->typeList, tp, position); @@ -694,16 +698,8 @@ static std::optional findTypeElementAt(AstType* astType, TypeId ty, Posi if (auto element = findTypeElementAt(type->argTypes, ftv->argTypes, position)) return element; - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position)) - return element; - } - else - { - if (auto element = findTypeElementAt(type->returnTypes_DEPRECATED, ftv->retTypes, position)) - return element; - } + if (auto element = findTypeElementAt(type->returnTypes, ftv->retTypes, position)) + return element; } // It's possible to walk through other types like intrsection and unions if we find value in doing that @@ -1033,65 +1029,14 @@ AutocompleteEntryMap autocompleteTypeNames( } } - if (FFlag::LuauStoreReturnTypesAsPackOnAst) + if (!node->returnAnnotation) + return result; + + if (const auto typePack = node->returnAnnotation->as()) { - if (!node->returnAnnotation) - return result; - - if (const auto typePack = node->returnAnnotation->as()) + for (size_t i = 0; i < typePack->typeList.types.size; i++) { - for (size_t i = 0; i < typePack->typeList.types.size; i++) - { - AstType* ret = typePack->typeList.types.data[i]; - - if (ret->location.containsClosed(position)) - { - if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) - { - if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, i)) - tryAddTypeCorrectSuggestion(result, startScope, topType, *ty, position); - } - - // TODO: with additional type information, we could suggest inferred return type here - break; - } - } - - if (AstTypePack* retTp = typePack->typeList.tailType) - { - if (auto variadic = retTp->as()) - { - if (variadic->location.containsClosed(position)) - { - if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) - { - if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) - tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position); - } - } - } - } - } - else if (auto variadic = node->returnAnnotation->as()) - { - if (variadic->location.containsClosed(position)) - { - if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) - { - if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) - tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position); - } - } - } - } - else - { - if (!node->returnAnnotation_DEPRECATED) - return result; - - for (size_t i = 0; i < node->returnAnnotation_DEPRECATED->types.size; i++) - { - AstType* ret = node->returnAnnotation_DEPRECATED->types.data[i]; + AstType* ret = typePack->typeList.types.data[i]; if (ret->location.containsClosed(position)) { @@ -1106,7 +1051,7 @@ AutocompleteEntryMap autocompleteTypeNames( } } - if (AstTypePack* retTp = node->returnAnnotation_DEPRECATED->tailType) + if (AstTypePack* retTp = typePack->typeList.tailType) { if (auto variadic = retTp->as()) { @@ -1121,6 +1066,17 @@ AutocompleteEntryMap autocompleteTypeNames( } } } + else if (auto variadic = node->returnAnnotation->as()) + { + if (variadic->location.containsClosed(position)) + { + if (const FunctionType* ftv = tryGetExpectedFunctionType(module, node)) + { + if (auto ty = tryGetTypePackTypeAt(ftv->retTypes, ~0u)) + tryAddTypeCorrectSuggestion(result, std::move(startScope), topType, *ty, position); + } + } + } } return result; @@ -2074,8 +2030,11 @@ AutocompleteResult autocomplete_( { AutocompleteEntryMap result; - if (auto it = module->astExpectedTypes.find(node->asExpr())) - autocompleteStringSingleton(*it, false, node, position, result); + if (!FFlag::LuauImplicitTableIndexerKeys2) + { + if (auto it = module->astExpectedTypes.find(node->asExpr())) + autocompleteStringSingleton(*it, false, node, position, result); + } if (ancestry.size() >= 2) { @@ -2094,6 +2053,12 @@ AutocompleteResult autocomplete_( } } + if (FFlag::LuauImplicitTableIndexerKeys2) + { + if (auto it = module->astExpectedTypes.find(node->asExpr())) + autocompleteStringSingleton(*it, false, node, position, result); + } + return {std::move(result), ancestry, AutocompleteContext::String}; } else if (stringPartOfInterpString(node, position)) diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index ff9f1bac..3954bc25 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -31,6 +31,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauTableCloneClonesType3) LUAU_FASTFLAGVARIABLE(LuauStringFormatImprovements) LUAU_FASTFLAGVARIABLE(LuauMagicFreezeCheckBlocked2) @@ -336,7 +337,14 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC auto it = stringMetatableTable->props.find("__index"); LUAU_ASSERT(it != stringMetatableTable->props.end()); - addGlobalBinding(globals, "string", it->second.type(), "@luau"); + + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + addGlobalBinding(globals, "string", *it->second.readTy, "@luau"); + addGlobalBinding(globals, "string", *it->second.writeTy, "@luau"); + } + else + addGlobalBinding(globals, "string", it->second.type_DEPRECATED(), "@luau"); // Setup 'vector' metatable if (auto it = globals.globalScope->exportedTypeBindings.find("vector"); it != globals.globalScope->exportedTypeBindings.end()) @@ -494,10 +502,20 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC ttv->props["foreach"].deprecated = true; ttv->props["foreachi"].deprecated = true; - attachMagicFunction(ttv->props["pack"].type(), std::make_shared()); - if (FFlag::LuauTableCloneClonesType3) - attachMagicFunction(ttv->props["clone"].type(), std::make_shared()); - attachMagicFunction(ttv->props["freeze"].type(), std::make_shared()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + attachMagicFunction(*ttv->props["pack"].readTy, std::make_shared()); + if (FFlag::LuauTableCloneClonesType3) + attachMagicFunction(*ttv->props["clone"].readTy, std::make_shared()); + attachMagicFunction(*ttv->props["freeze"].readTy, std::make_shared()); + } + else + { + attachMagicFunction(ttv->props["pack"].type_DEPRECATED(), std::make_shared()); + if (FFlag::LuauTableCloneClonesType3) + attachMagicFunction(ttv->props["clone"].type_DEPRECATED(), std::make_shared()); + attachMagicFunction(ttv->props["freeze"].type_DEPRECATED(), std::make_shared()); + } } TypeId requireTy = getGlobalBinding(globals, "require"); @@ -1649,7 +1667,7 @@ std::optional> MagicClone::handleOldSolver( return std::nullopt; CloneState cloneState{typechecker.builtinTypes}; - TypeId resultType = shallowClone(inputType, arena, cloneState); + TypeId resultType = shallowClone(inputType, arena, cloneState, /* clonePersistentTypes */ false); TypePackId clonedTypePack = arena.addTypePack({resultType}); return WithPredicate{clonedTypePack}; diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index 69785d30..9a5879b1 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -232,7 +232,7 @@ private: else { return Property{ - shallowClone(p.type()), + shallowClone(p.type_DEPRECATED()), p.deprecated, p.deprecatedSuggestion, p.location, @@ -551,9 +551,9 @@ public: } // namespace -TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool ignorePersistent) +TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes) { - if (tp->persistent && !ignorePersistent) + if (tp->persistent && !clonePersistentTypes) return tp; TypeCloner cloner{ @@ -562,15 +562,15 @@ TypePackId shallowClone(TypePackId tp, TypeArena& dest, CloneState& cloneState, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, nullptr, - ignorePersistent ? tp : nullptr + clonePersistentTypes ? tp : nullptr }; return cloner.shallowClone(tp); } -TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool ignorePersistent) +TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool clonePersistentTypes) { - if (typeId->persistent && !ignorePersistent) + if (typeId->persistent && !clonePersistentTypes) return typeId; TypeCloner cloner{ @@ -578,7 +578,7 @@ TypeId shallowClone(TypeId typeId, TypeArena& dest, CloneState& cloneState, bool cloneState.builtinTypes, NotNull{&cloneState.seenTypes}, NotNull{&cloneState.seenTypePacks}, - ignorePersistent ? typeId : nullptr, + clonePersistentTypes ? typeId : nullptr, nullptr }; diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 1d634a14..b904fe7f 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -36,7 +36,6 @@ LUAU_FASTFLAG(DebugLuauLogSolverToJson) LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauGlobalVariableModuleIsolation) LUAU_FASTFLAGVARIABLE(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) @@ -49,8 +48,10 @@ LUAU_FASTFLAGVARIABLE(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTINTVARIABLE(LuauPrimitiveInferenceInTableLimit, 500) LUAU_FASTFLAGVARIABLE(LuauUserTypeFunctionAliases) LUAU_FASTFLAGVARIABLE(LuauSkipLvalueForCompoundAssignment) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauFollowTypeAlias) LUAU_FASTFLAGVARIABLE(LuauFollowExistingTypeFunction) +LUAU_FASTFLAGVARIABLE(LuauRefineTablesWithReadType) namespace Luau { @@ -206,8 +207,8 @@ ConstraintGenerator::ConstraintGenerator( NotNull moduleResolver, NotNull builtinTypes, NotNull ice, - const ScopePtr& globalScope, - const ScopePtr& typeFunctionScope, + ScopePtr globalScope, + ScopePtr typeFunctionScope, std::function prepareModuleScope, DcrLogger* logger, NotNull dfg, @@ -223,8 +224,8 @@ ConstraintGenerator::ConstraintGenerator( , typeFunctionRuntime(typeFunctionRuntime) , moduleResolver(moduleResolver) , ice(ice) - , globalScope(globalScope) - , typeFunctionScope(typeFunctionScope) + , globalScope(std::move(globalScope)) + , typeFunctionScope(std::move(typeFunctionScope)) , prepareModuleScope(std::move(prepareModuleScope)) , requireCycles(std::move(requireCycles)) , logger(logger) @@ -607,11 +608,18 @@ void ConstraintGenerator::computeRefinement( TypeId nextDiscriminantTy = arena->addType(TableType{}); NotNull table{getMutable(nextDiscriminantTy)}; - // When we fully support read-write properties (i.e. when we allow properties with - // completely disparate read and write types), then the following property can be - // set to read-only since refinements only tell us about what we read. This cannot - // be allowed yet though because it causes read and write types to diverge. - table->props[*key->propName] = Property::rw(discriminantTy); + if (FFlag::LuauRefineTablesWithReadType) + { + table->props[*key->propName] = Property::readonly(discriminantTy); + } + else + { + // When we fully support read-write properties (i.e. when we allow properties with + // completely disparate read and write types), then the following property can be + // set to read-only since refinements only tell us about what we read. This cannot + // be allowed yet though because it causes read and write types to diverge. + table->props[*key->propName] = Property::rw(discriminantTy); + } table->scope = scope.get(); table->state = TableState::Sealed; @@ -1998,29 +2006,91 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareExte else { Luau::Property& prop = props[propName]; - TypeId currentTy = prop.type(); - // We special-case this logic to keep the intersection flat; otherwise we - // would create a ton of nested intersection types. - if (const IntersectionType* itv = get(currentTy)) + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) { - std::vector options = itv->parts; - options.push_back(propTy); - TypeId newItv = arena->addType(IntersectionType{std::move(options)}); + if (auto readTy = prop.readTy) + { + // We special-case this logic to keep the intersection flat; otherwise we + // would create a ton of nested intersection types. + if (const IntersectionType* itv = get(*readTy)) + { + std::vector options = itv->parts; + options.push_back(propTy); + TypeId newItv = arena->addType(IntersectionType{std::move(options)}); - prop.readTy = newItv; - prop.writeTy = newItv; - } - else if (get(currentTy)) - { - TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}}); + prop.readTy = newItv; + } + else if (get(*readTy)) + { + TypeId intersection = arena->addType(IntersectionType{{*readTy, propTy}}); - prop.readTy = intersection; - prop.writeTy = intersection; + prop.readTy = intersection; + } + else + { + reportError( + declaredExternType->location, + GenericError{format("Cannot overload read type of non-function class member '%s'", propName.c_str())} + ); + } + } + + if (auto writeTy = prop.writeTy) + { + // We special-case this logic to keep the intersection flat; otherwise we + // would create a ton of nested intersection types. + if (const IntersectionType* itv = get(*writeTy)) + { + std::vector options = itv->parts; + options.push_back(propTy); + TypeId newItv = arena->addType(IntersectionType{std::move(options)}); + + prop.writeTy = newItv; + } + else if (get(*writeTy)) + { + TypeId intersection = arena->addType(IntersectionType{{*writeTy, propTy}}); + + prop.writeTy = intersection; + } + else + { + reportError( + declaredExternType->location, + GenericError{format("Cannot overload write type of non-function class member '%s'", propName.c_str())} + ); + } + } } else { - reportError(declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())}); + TypeId currentTy = prop.type_DEPRECATED(); + + // We special-case this logic to keep the intersection flat; otherwise we + // would create a ton of nested intersection types. + if (const IntersectionType* itv = get(currentTy)) + { + std::vector options = itv->parts; + options.push_back(propTy); + TypeId newItv = arena->addType(IntersectionType{std::move(options)}); + + prop.readTy = newItv; + prop.writeTy = newItv; + } + else if (get(currentTy)) + { + TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}}); + + prop.readTy = intersection; + prop.writeTy = intersection; + } + else + { + reportError( + declaredExternType->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())} + ); + } } } } @@ -2052,8 +2122,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc funScope = childScope(global, scope); TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false); - TypePackId retPack = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false) - : resolveTypePack(funScope, global->retTypes_DEPRECATED, /* inTypeArguments */ false); + TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false); FunctionDefinition defn; @@ -3538,7 +3607,7 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu // If there is both an annotation and an expected type, the annotation wins. // Type checking will sort out any discrepancies later. - if (FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation) + if (fn->returnAnnotation) { TypePackId annotatedRetType = resolveTypePack(signatureScope, fn->returnAnnotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true); @@ -3548,16 +3617,6 @@ ConstraintGenerator::FunctionSignature ConstraintGenerator::checkFunctionSignatu LUAU_ASSERT(get(returnType)); emplaceTypePack(asMutable(returnType), annotatedRetType); } - else if (!FFlag::LuauStoreReturnTypesAsPackOnAst && fn->returnAnnotation_DEPRECATED) - { - TypePackId annotatedRetType = - resolveTypePack(signatureScope, *fn->returnAnnotation_DEPRECATED, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true); - // We bind the annotated type directly here so that, when we need to - // generate constraints for return types, we have a guarantee that we - // know the annotated return type already, if one was provided. - LUAU_ASSERT(get(returnType)); - emplaceTypePack(asMutable(returnType), annotatedRetType); - } else if (expectedFunction) { emplaceTypePack(asMutable(returnType), expectedFunction->retTypes); @@ -3805,16 +3864,7 @@ TypeId ConstraintGenerator::resolveFunctionType( AstTypePackExplicit tempArgTypes{Location{}, fn->argTypes}; TypePackId argTypes = resolveTypePack_(signatureScope, &tempArgTypes, inTypeArguments, replaceErrorWithFresh); - TypePackId returnTypes; - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - returnTypes = resolveTypePack_(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh); - } - else - { - AstTypePackExplicit tempRetTypes{Location{}, fn->returnTypes_DEPRECATED}; - returnTypes = resolveTypePack_(signatureScope, &tempRetTypes, inTypeArguments, replaceErrorWithFresh); - } + TypePackId returnTypes = resolveTypePack_(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh); // TODO: FunctionType needs a pointer to the scope so that we know // how to quantify/instantiate it. diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 9548ca5f..d40cac68 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -39,6 +39,8 @@ LUAU_FASTFLAGVARIABLE(LuauInsertErrorTypesIntoIndexerResult) LUAU_FASTFLAGVARIABLE(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAGVARIABLE(LuauAvoidGenericsLeakingDuringFunctionCallCheck) LUAU_FASTFLAGVARIABLE(LuauMissingFollowInAssignIndexConstraint) +LUAU_FASTFLAGVARIABLE(LuauRemoveTypeCallsForReadWriteProps) +LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeCheckFunctionCalls) namespace Luau { @@ -1834,15 +1836,26 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNullis() || expr->is() || expr->is() || - expr->is()) + expr->is() || (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is())) { ReferentialReplacer replacer{arena, NotNull{&replacements}, NotNull{&replacementPacks}}; if (auto res = replacer.substitute(expectedArgTy)) + { + if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls) + { + // If we do this replacement and there are type + // functions in the final type, then we need to + // ensure those get reduced. + InstantiationQueuer queuer{constraint->scope, constraint->location, this}; + queuer.traverse(*res); + } u2.unify(actualArgTy, *res); + } else u2.unify(actualArgTy, expectedArgTy); } - else if (expr->is() && !ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) + else if (!FFlag::LuauTableLiteralSubtypeCheckFunctionCalls && expr->is() && + !ContainsGenerics::hasGeneric(expectedArgTy, NotNull{&genericTypesAndPacks})) { Subtyping sp{builtinTypes, arena, simplifier, normalizer, typeFunctionRuntime, NotNull{&iceReporter}}; std::vector toBlock; @@ -1852,6 +1865,27 @@ bool ConstraintSolver::tryDispatch(const FunctionCheckConstraint& c, NotNull + // + // local function move(dirs: { Direction }) --[[...]] end + // + // move({ "Left", "Right", "Left", "Right" }) + // + // We need `keyof` to reduce prior to inferring that the + // arguments to `move` must generalize to their lower bounds. This + // is how we ensure that ordering. + for (auto& c : u2.incompleteSubtypes) + { + NotNull addition = pushConstraint(constraint->scope, constraint->location, std::move(c)); + inheritBlocks(constraint, addition); + } + } } else { @@ -3173,8 +3207,16 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( return {{}, result.propType}; // TODO: __index can be an overloaded function. + // - TypeId indexType = follow(indexProp->second.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + // if the property is write-only, then surely we cannot read from it. + if (indexProp->second.isWriteOnly()) + return {{}, builtinTypes->errorType}; + } + + TypeId indexType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? follow(*indexProp->second.readTy) : follow(indexProp->second.type_DEPRECATED()); if (auto ft = get(indexType)) { @@ -3212,7 +3254,16 @@ TablePropLookupResult ConstraintSolver::lookupTableProp( if (indexProp == metatable->props.end()) return {{}, std::nullopt}; - return lookupTableProp(constraint, indexProp->second.type(), propName, context, inConditional, suppressSimplification, seen); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + // if the property is write-only, then surely we cannot read from it. + if (indexProp->second.isWriteOnly()) + return {{}, builtinTypes->errorType}; + + return lookupTableProp(constraint, *indexProp->second.readTy, propName, context, inConditional, suppressSimplification, seen); + } + else + return lookupTableProp(constraint, indexProp->second.type_DEPRECATED(), propName, context, inConditional, suppressSimplification, seen); } else if (auto ft = get(subjectType)) { diff --git a/Analysis/src/DataFlowGraph.cpp b/Analysis/src/DataFlowGraph.cpp index d12b2e19..91f4d93e 100644 --- a/Analysis/src/DataFlowGraph.cpp +++ b/Analysis/src/DataFlowGraph.cpp @@ -14,7 +14,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAGVARIABLE(LuauDfgScopeStackNotNull) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAGVARIABLE(LuauDfgIfBlocksShouldRespectControlFlow) LUAU_FASTFLAGVARIABLE(LuauDfgAllowUpdatesInLoops) @@ -951,10 +950,7 @@ ControlFlow DataFlowGraphBuilder::visit(AstStatDeclareFunction* d) visitGenerics(d->generics); visitGenericPacks(d->genericPacks); visitTypeList(d->params); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visitTypePack(d->retTypes); - else - visitTypeList(d->retTypes_DEPRECATED); + visitTypePack(d->retTypes); return ControlFlow::None; } @@ -1169,16 +1165,8 @@ DataFlowResult DataFlowGraphBuilder::visitExpr(AstExprFunction* f) if (f->varargAnnotation) visitTypePack(f->varargAnnotation); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - if (f->returnAnnotation) - visitTypePack(f->returnAnnotation); - } - else - { - if (f->returnAnnotation_DEPRECATED) - visitTypeList(*f->returnAnnotation_DEPRECATED); - } + if (f->returnAnnotation) + visitTypePack(f->returnAnnotation); // TODO: function body can be re-entrant, as in mutations that occurs at the end of the function can also be // visible to the beginning of the function, so statically speaking, the body of the function has an exit point @@ -1420,10 +1408,7 @@ void DataFlowGraphBuilder::visitType(AstTypeFunction* f) visitGenerics(f->generics); visitGenericPacks(f->genericPacks); visitTypeList(f->argTypes); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visitTypePack(f->returnTypes); - else - visitTypeList(f->returnTypes_DEPRECATED); + visitTypePack(f->returnTypes); } void DataFlowGraphBuilder::visitType(AstTypeTypeof* t) diff --git a/Analysis/src/EqSatSimplification.cpp b/Analysis/src/EqSatSimplification.cpp index d26697a7..bc2725f3 100644 --- a/Analysis/src/EqSatSimplification.cpp +++ b/Analysis/src/EqSatSimplification.cpp @@ -25,6 +25,7 @@ LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplification) LUAU_FASTFLAGVARIABLE(DebugLuauLogSimplificationToDot) LUAU_FASTFLAGVARIABLE(DebugLuauExtraEqSatSanityChecks) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau::EqSatSimplification { @@ -2369,10 +2370,16 @@ void Simplifier::intersectTableProperty(Id id) newIntersectionParts.push_back(intersectionParts[index]); } - Id newTableProp = egraph.add(Intersection{ - toId(egraph, builtinTypes, mappingIdToClass, stringCache, it->second.type()), - toId(egraph, builtinTypes, mappingIdToClass, stringCache, table1Ty->props.begin()->second.type()) - }); + Id newTableProp = + FFlag::LuauRemoveTypeCallsForReadWriteProps + ? egraph.add(Intersection{ + toId(egraph, builtinTypes, mappingIdToClass, stringCache, *it->second.readTy), + toId(egraph, builtinTypes, mappingIdToClass, stringCache, *table1Ty->props.begin()->second.readTy) + }) + : egraph.add(Intersection{ + toId(egraph, builtinTypes, mappingIdToClass, stringCache, it->second.type_DEPRECATED()), + toId(egraph, builtinTypes, mappingIdToClass, stringCache, table1Ty->props.begin()->second.type_DEPRECATED()) + }); newIntersectionParts.push_back(egraph.add(TTable{jId, {stringCache.add(it->first)}, {newTableProp}})); @@ -2444,7 +2451,10 @@ void Simplifier::unneededTableModification(Id id) StringId propName = tbl->propNames[i]; const Id propType = tbl->propTypes()[i]; - Id importedProp = toId(egraph, builtinTypes, mappingIdToClass, stringCache, tt->props.at(stringCache.asString(propName)).type()); + Id importedProp = + FFlag::LuauRemoveTypeCallsForReadWriteProps + ? toId(egraph, builtinTypes, mappingIdToClass, stringCache, *tt->props.at(stringCache.asString(propName)).readTy) + : toId(egraph, builtinTypes, mappingIdToClass, stringCache, tt->props.at(stringCache.asString(propName)).type_DEPRECATED()); if (find(importedProp) != find(propType)) { diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index acc91b19..ae3184b3 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -20,6 +20,7 @@ LUAU_FASTINTVARIABLE(LuauIndentTypeMismatchMaxTypeLength, 10) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauBetterCannotCallFunctionPrimitive) static std::string wrongNumberOfArgsString( @@ -418,7 +419,12 @@ struct ErrorConverter auto it = mtt->props.find("__call"); if (it != mtt->props.end()) - return it->second.type(); + { + if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps) + return it->second.readTy; + else + return it->second.type_DEPRECATED(); + } else return std::nullopt; } diff --git a/Analysis/src/ExpectedTypeVisitor.cpp b/Analysis/src/ExpectedTypeVisitor.cpp index 4862a8cb..2f5f1fe0 100644 --- a/Analysis/src/ExpectedTypeVisitor.cpp +++ b/Analysis/src/ExpectedTypeVisitor.cpp @@ -8,7 +8,7 @@ #include "Luau/TypeUtils.h" #include "Luau/VisitType.h" -LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys) +LUAU_FASTFLAGVARIABLE(LuauImplicitTableIndexerKeys2) namespace Luau { @@ -147,7 +147,7 @@ struct IndexCollector : public TypeOnceVisitor bool ExpectedTypeVisitor::visit(AstExprIndexExpr* expr) { - if (!FFlag::LuauImplicitTableIndexerKeys) + if (!FFlag::LuauImplicitTableIndexerKeys2) return true; if (auto ty = astTypes->find(expr->expr)) diff --git a/Analysis/src/FragmentAutocomplete.cpp b/Analysis/src/FragmentAutocomplete.cpp index 7d056bb8..20655996 100644 --- a/Analysis/src/FragmentAutocomplete.cpp +++ b/Analysis/src/FragmentAutocomplete.cpp @@ -34,7 +34,6 @@ LUAU_FASTFLAGVARIABLE(LuauBetterScopeSelection) LUAU_FASTFLAGVARIABLE(LuauBlockDiffFragmentSelection) LUAU_FASTFLAGVARIABLE(LuauFragmentAcMemoryLeak) LUAU_FASTFLAGVARIABLE(LuauGlobalVariableModuleIsolation) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauFragmentAutocompleteIfRecommendations) LUAU_FASTFLAG(LuauExpectedTypeVisitor) LUAU_FASTFLAGVARIABLE(LuauPopulateRefinedTypesInFragmentFromOldSolver) @@ -51,17 +50,10 @@ Location getFunctionDeclarationExtents(AstExprFunction* exprFn, AstExpr* exprNam { auto fnBegin = exprFn->location.begin; auto fnEnd = exprFn->location.end; - if (auto returnAnnot = exprFn->returnAnnotation; FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot) + if (auto returnAnnot = exprFn->returnAnnotation) { fnEnd = returnAnnot->location.end; } - else if (auto returnAnnot = exprFn->returnAnnotation_DEPRECATED; !FFlag::LuauStoreReturnTypesAsPackOnAst && returnAnnot) - { - if (returnAnnot->tailType) - fnEnd = returnAnnot->tailType->location.end; - else if (returnAnnot->types.size != 0) - fnEnd = returnAnnot->types.data[returnAnnot->types.size - 1]->location.end; - } else if (exprFn->args.size != 0) { auto last = exprFn->args.data[exprFn->args.size - 1]; @@ -581,7 +573,7 @@ struct UsageFinder : public AstVisitor bool visit(AstTypePack* node) override { - return FFlag::LuauStoreReturnTypesAsPackOnAst; + return true; } bool visit(AstStatTypeAlias* alias) override diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 6b3af463..a521a062 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -2,8 +2,8 @@ #include "Luau/Frontend.h" #include "Luau/BuiltinDefinitions.h" -#include "Luau/Clone.h" #include "Luau/Common.h" +#include "Luau/Clone.h" #include "Luau/Config.h" #include "Luau/ConstraintGenerator.h" #include "Luau/ConstraintSolver.h" @@ -18,8 +18,6 @@ #include "Luau/Scope.h" #include "Luau/StringUtils.h" #include "Luau/TimeTrace.h" -#include "Luau/ToString.h" -#include "Luau/Transpiler.h" #include "Luau/TypeArena.h" #include "Luau/TypeChecker2.h" #include "Luau/TypeInfer.h" @@ -48,6 +46,8 @@ LUAU_FASTFLAGVARIABLE(DebugLuauForceStrictMode) LUAU_FASTFLAGVARIABLE(DebugLuauForceNonStrictMode) LUAU_FASTFLAGVARIABLE(LuauNewSolverTypecheckCatchTimeouts) LUAU_FASTFLAGVARIABLE(LuauExpectedTypeVisitor) +LUAU_FASTFLAGVARIABLE(LuauTrackTypeAllocations) +LUAU_FASTFLAGVARIABLE(LuauUseWorkspacePropToChooseSolver) namespace Luau { @@ -399,6 +399,21 @@ Frontend::Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, c { } +void Frontend::setLuauSolverSelectionFromWorkspace(bool newSolverEnabled) +{ + useNewLuauSolver.store(newSolverEnabled); +} + +bool Frontend::getLuauSolverSelection() const +{ + return useNewLuauSolver.load(); +} + +bool Frontend::getLuauSolverSelectionFlagged() const +{ + return FFlag::LuauUseWorkspacePropToChooseSolver ? getLuauSolverSelection() : FFlag::LuauSolverV2; +} + void Frontend::parse(const ModuleName& name) { LUAU_TIMETRACE_SCOPE("Frontend::parse", "Frontend"); @@ -983,6 +998,15 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) item.stats.timeCheck += duration; item.stats.filesStrict += 1; + if (FFlag::LuauTrackTypeAllocations && item.options.collectTypeAllocationStats) + { + item.stats.typesAllocated += moduleForAutocomplete->internalTypes.types.size(); + item.stats.typePacksAllocated += moduleForAutocomplete->internalTypes.typePacks.size(); + item.stats.boolSingletonsMinted += moduleForAutocomplete->internalTypes.boolSingletonsMinted; + item.stats.strSingletonsMinted += moduleForAutocomplete->internalTypes.strSingletonsMinted; + item.stats.uniqueStrSingletonsMinted += moduleForAutocomplete->internalTypes.uniqueStrSingletonsMinted.size(); + } + if (item.options.customModuleCheck) item.options.customModuleCheck(sourceModule, *moduleForAutocomplete); @@ -1003,6 +1027,15 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) item.stats.filesStrict += mode == Mode::Strict; item.stats.filesNonstrict += mode == Mode::Nonstrict; + if (FFlag::LuauTrackTypeAllocations && item.options.collectTypeAllocationStats) + { + item.stats.typesAllocated += module->internalTypes.types.size(); + item.stats.typePacksAllocated += module->internalTypes.typePacks.size(); + item.stats.boolSingletonsMinted += module->internalTypes.boolSingletonsMinted; + item.stats.strSingletonsMinted += module->internalTypes.strSingletonsMinted; + item.stats.uniqueStrSingletonsMinted += module->internalTypes.uniqueStrSingletonsMinted.size(); + } + if (item.options.customModuleCheck) item.options.customModuleCheck(sourceModule, *module); @@ -1124,6 +1157,16 @@ void Frontend::recordItemResult(const BuildQueueItem& item) stats.filesStrict += item.stats.filesStrict; stats.filesNonstrict += item.stats.filesNonstrict; + + if (FFlag::LuauTrackTypeAllocations && item.options.collectTypeAllocationStats) + { + stats.typesAllocated += item.stats.typesAllocated; + stats.typePacksAllocated += item.stats.typePacksAllocated; + + stats.boolSingletonsMinted += item.stats.boolSingletonsMinted; + stats.strSingletonsMinted += item.stats.strSingletonsMinted; + stats.uniqueStrSingletonsMinted += item.stats.uniqueStrSingletonsMinted; + } } void Frontend::performQueueItemTask(std::shared_ptr state, size_t itemPos) @@ -1393,6 +1436,8 @@ ModulePtr check( result->mode = mode; result->internalTypes.owningModule = result.get(); result->interfaceTypes.owningModule = result.get(); + if (FFlag::LuauTrackTypeAllocations) + result->internalTypes.collectSingletonStats = options.collectTypeAllocationStats; result->allocator = sourceModule.allocator; result->names = sourceModule.names; result->root = sourceModule.root; diff --git a/Analysis/src/Generalization.cpp b/Analysis/src/Generalization.cpp index 785abb75..a2f6632b 100644 --- a/Analysis/src/Generalization.cpp +++ b/Analysis/src/Generalization.cpp @@ -15,6 +15,7 @@ #include "Luau/VisitType.h" LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauGeneralizationCannotMutateAcrossModules) @@ -562,7 +563,10 @@ struct FreeTypeSearcher : TypeVisitor { Polarity p = polarity; polarity = Polarity::Mixed; - traverse(prop.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + traverse(*prop.readTy); + else + traverse(prop.type_DEPRECATED()); polarity = p; } else @@ -586,7 +590,10 @@ struct FreeTypeSearcher : TypeVisitor Polarity p = polarity; polarity = Polarity::Mixed; - traverse(prop.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + traverse(*prop.readTy); + else + traverse(prop.type_DEPRECATED()); polarity = p; } } @@ -1518,7 +1525,10 @@ struct GenericCounter : TypeVisitor { Polarity p = polarity; polarity = Polarity::Mixed; - traverse(prop.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + traverse(*prop.readTy); + else + traverse(prop.type_DEPRECATED()); polarity = p; } else @@ -1542,7 +1552,10 @@ struct GenericCounter : TypeVisitor Polarity p = polarity; polarity = Polarity::Mixed; - traverse(prop.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + traverse(*prop.readTy); + else + traverse(prop.type_DEPRECATED()); polarity = p; } } diff --git a/Analysis/src/InferPolarity.cpp b/Analysis/src/InferPolarity.cpp index db845c59..57144a74 100644 --- a/Analysis/src/InferPolarity.cpp +++ b/Analysis/src/InferPolarity.cpp @@ -6,6 +6,7 @@ #include "Luau/VisitType.h" LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauInferPolarityOfReadWriteProperties) namespace Luau @@ -49,7 +50,7 @@ struct InferPolarity : TypeVisitor return false; const Polarity p = polarity; - if (FFlag::LuauInferPolarityOfReadWriteProperties) + if (FFlag::LuauInferPolarityOfReadWriteProperties || FFlag::LuauRemoveTypeCallsForReadWriteProps) { for (const auto& [name, prop] : tt.props) { @@ -80,7 +81,7 @@ struct InferPolarity : TypeVisitor if (prop.isShared()) { polarity = Polarity::Mixed; - traverse(prop.type()); + traverse(prop.type_DEPRECATED()); } else if (prop.isReadOnly()) { diff --git a/Analysis/src/Linter.cpp b/Analysis/src/Linter.cpp index 2bdceebe..9a91afad 100644 --- a/Analysis/src/Linter.cpp +++ b/Analysis/src/Linter.cpp @@ -16,8 +16,6 @@ LUAU_FASTINTVARIABLE(LuauSuggestionDistance, 4) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - namespace Luau { @@ -907,7 +905,7 @@ private: bool visit(AstTypePack* node) override { - return FFlag::LuauStoreReturnTypesAsPackOnAst; + return true; } bool visit(AstTypeReference* node) override @@ -1974,7 +1972,7 @@ private: bool visit(AstTypePack* node) override { - return FFlag::LuauStoreReturnTypesAsPackOnAst; + return true; } bool visit(AstTypeTable* node) override diff --git a/Analysis/src/NonStrictTypeChecker.cpp b/Analysis/src/NonStrictTypeChecker.cpp index b8316e2b..cc8f8aac 100644 --- a/Analysis/src/NonStrictTypeChecker.cpp +++ b/Analysis/src/NonStrictTypeChecker.cpp @@ -23,7 +23,6 @@ LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictVisitTypes2) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauNewNonStrictFixGenericTypePacks) namespace Luau @@ -773,13 +772,7 @@ struct NonStrictTypeChecker { visitGenerics(exprFn->generics, exprFn->genericPacks); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visit(exprFn->returnAnnotation); - else - { - if (exprFn->returnAnnotation_DEPRECATED) - visit(*exprFn->returnAnnotation_DEPRECATED); - } + visit(exprFn->returnAnnotation); if (exprFn->varargAnnotation) visit(exprFn->varargAnnotation); diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index 8d89a22d..ab5d55b6 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -23,6 +23,7 @@ LUAU_FASTINTVARIABLE(LuauNormalizeUnionLimit, 100) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAGVARIABLE(LuauNormalizationIntersectTablesPreservesExternTypes) LUAU_FASTFLAGVARIABLE(LuauNormalizationReorderFreeTypeIntersect) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) namespace Luau { @@ -404,7 +405,7 @@ NormalizationResult Normalizer::isInhabited(TypeId ty, Set& seen) } else { - NormalizationResult res = isInhabited(prop.type(), seen); + NormalizationResult res = isInhabited(prop.type_DEPRECATED(), seen); if (res != NormalizationResult::True) return res; } @@ -2513,9 +2514,9 @@ std::optional Normalizer::intersectionOfTables(TypeId here, TypeId there } else { - prop.setType(intersectionType(hprop.type(), tprop.type())); - hereSubThere &= (prop.type() == hprop.type()); - thereSubHere &= (prop.type() == tprop.type()); + prop.setType(intersectionType(hprop.type_DEPRECATED(), tprop.type_DEPRECATED())); + hereSubThere &= (prop.type_DEPRECATED() == hprop.type_DEPRECATED()); + thereSubHere &= (prop.type_DEPRECATED() == tprop.type_DEPRECATED()); } } @@ -3327,7 +3328,8 @@ TypeId Normalizer::typeFromNormal(const NormalizedType& norm) result.reserve(result.size() + norm.tables.size()); for (auto table : norm.tables) { - makeTableShared(table); + if (!FFlag::LuauRefineTablesWithReadType) + makeTableShared(table); result.push_back(table); } } diff --git a/Analysis/src/RequireTracer.cpp b/Analysis/src/RequireTracer.cpp index 6e9dd854..b286ad00 100644 --- a/Analysis/src/RequireTracer.cpp +++ b/Analysis/src/RequireTracer.cpp @@ -4,8 +4,6 @@ #include "Luau/Ast.h" #include "Luau/Module.h" -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - namespace Luau { @@ -70,7 +68,7 @@ struct RequireTracer : AstVisitor bool visit(AstTypePack* node) override { // allow resolving require inside `typeof` annotations - return FFlag::LuauStoreReturnTypesAsPackOnAst; + return true; } AstExpr* getDependent_DEPRECATED(AstExpr* node) diff --git a/Analysis/src/Simplify.cpp b/Analysis/src/Simplify.cpp index 915550eb..cb52dc1c 100644 --- a/Analysis/src/Simplify.cpp +++ b/Analysis/src/Simplify.cpp @@ -2,12 +2,14 @@ #include "Luau/Simplify.h" +#include "Luau/Clone.h" #include "Luau/Common.h" #include "Luau/DenseHash.h" #include "Luau/RecursionCounter.h" #include "Luau/Set.h" #include "Luau/Type.h" #include "Luau/TypeArena.h" +#include "Luau/TypeIds.h" #include "Luau/TypePairHash.h" #include "Luau/TypeUtils.h" @@ -17,6 +19,9 @@ LUAU_FASTINT(LuauTypeReductionRecursionLimit) LUAU_FASTFLAG(LuauSolverV2) LUAU_DYNAMIC_FASTINTVARIABLE(LuauSimplificationComplexityLimit, 8) LUAU_FASTFLAGVARIABLE(LuauSimplificationTableExternType) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) +LUAU_FASTFLAGVARIABLE(LuauRelateTablesAreNeverDisjoint) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) namespace Luau { @@ -37,10 +42,13 @@ struct TypeSimplifier TypeId intersectFromParts(std::set parts); TypeId intersectUnionWithType(TypeId left, TypeId right); + TypeId intersectUnions(TypeId left, TypeId right); + TypeId intersectNegatedUnion(TypeId left, TypeId right); TypeId intersectTypeWithNegation(TypeId left, TypeId right); + TypeId intersectNegations(TypeId left, TypeId right); TypeId intersectIntersectionWithType(TypeId left, TypeId right); @@ -48,18 +56,32 @@ struct TypeSimplifier // Attempt to intersect the two types. Does not recurse. Does not handle // unions, intersections, or negations. std::optional basicIntersect(TypeId left, TypeId right); + std::optional basicIntersectWithTruthy(TypeId target) const; + std::optional basicIntersectWithFalsy(TypeId target) const; TypeId intersect(TypeId left, TypeId right); + TypeId union_(TypeId left, TypeId right); TypeId simplify(TypeId ty); + TypeId simplify(TypeId ty, DenseHashSet& seen); + + std::optional intersectOne(TypeId target, TypeId discriminant) const; + + std::optional subtractOne(TypeId target, TypeId discriminant) const; + + std::optional intersectProperty(const Property& target, const Property& discriminant, DenseHashSet& seen) const; + + std::optional intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant, DenseHashSet& seen) const; + + std::optional intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant) const; }; // Match the exact type false|nil -static bool isFalsyType(TypeId ty) +static bool isFalsyType_DEPRECATED(TypeId ty) { ty = follow(ty); const UnionType* ut = get(ty); @@ -103,7 +125,7 @@ static bool isFalsyType(TypeId ty) } // Match the exact type ~(false|nil) -bool isTruthyType(TypeId ty) +bool isTruthyType_DEPRECATED(TypeId ty) { ty = follow(ty); @@ -111,7 +133,7 @@ bool isTruthyType(TypeId ty) if (!nt) return false; - return isFalsyType(nt->ty); + return isFalsyType_DEPRECATED(nt->ty); } Relation flip(Relation rel) @@ -266,7 +288,7 @@ Relation relateTables(TypeId left, TypeId right, SimplifierSeenSet& seen) ); if (!foundPropFromLeftInRight && !foundPropFromRightInLeft && leftTable->props.size() >= 1 && rightTable->props.size() >= 1) - return Relation::Disjoint; + return FFlag::LuauRelateTablesAreNeverDisjoint ? Relation::Intersects : Relation::Disjoint; const auto [propName, rightProp] = *begin(rightTable->props); @@ -283,7 +305,11 @@ Relation relateTables(TypeId left, TypeId right, SimplifierSeenSet& seen) if (!leftProp.isShared() || !rightProp.isShared()) return Relation::Intersects; - Relation r = relate(leftProp.type(), rightProp.type(), seen); + Relation r; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + r = relate(*leftProp.readTy, *rightProp.readTy, seen); + else + r = relate(leftProp.type_DEPRECATED(), rightProp.type_DEPRECATED(), seen); if (r == Relation::Coincident && 1 != leftTable->props.size()) { // eg {tag: "cat", prop: string} & {tag: "cat"} @@ -570,7 +596,14 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen) { if (auto propInExternType = re->props.find(name); propInExternType != re->props.end()) { - Relation propRel = relate(prop.type(), propInExternType->second.type()); + Relation propRel; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + LUAU_ASSERT(prop.readTy && propInExternType->second.readTy); + propRel = relate(*prop.readTy, *propInExternType->second.readTy); + } + else + propRel = relate(prop.type_DEPRECATED(), propInExternType->second.type_DEPRECATED()); if (propRel == Relation::Disjoint) return Relation::Disjoint; @@ -604,6 +637,15 @@ Relation relate(TypeId left, TypeId right, SimplifierSeenSet& seen) return Relation::Disjoint; } + if (FFlag::LuauRefineTablesWithReadType && is(right)) + { + // FIXME: This could be better in that we can say a table only + // intersects with an extern type if they share a property, but + // for now it is within the contract of the function to claim + // the two intersect. + return Relation::Intersects; + } + return Relation::Disjoint; } @@ -857,13 +899,13 @@ TypeId TypeSimplifier::intersectNegatedUnion(TypeId left, TypeId right) newParts.insert(right); break; case Relation::Coincident: - // If A is coincident with or a superset of B, then ~A & B is never. - // - // ~(false?) & false - // (~false & false) & (~nil & false) - // never & false - // - // fallthrough + // If A is coincident with or a superset of B, then ~A & B is never. + // + // ~(false?) & false + // (~false & false) & (~nil & false) + // never & false + // + // fallthrough case Relation::Superset: // If A is a superset of B, then ~A & B is never. // @@ -898,6 +940,15 @@ std::optional TypeSimplifier::basicIntersectWithTruthy(TypeId target) co { target = follow(target); + if (FFlag::LuauRefineTablesWithReadType) + { + if (isApproximatelyTruthyType(target)) + return target; + + if (isApproximatelyFalsyType(target)) + return builtinTypes->neverType; + } + if (is(target)) return builtinTypes->truthyType; @@ -934,6 +985,15 @@ std::optional TypeSimplifier::basicIntersectWithFalsy(TypeId target) con { target = follow(target); + if (FFlag::LuauRefineTablesWithReadType) + { + if (isApproximatelyTruthyType(target)) + return builtinTypes->neverType; + + if (isApproximatelyFalsyType(target)) + return target; + } + if (is(target)) return target; @@ -990,11 +1050,11 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right) switch (r) { case Relation::Coincident: - // ~(false?) & nil - // (~false & nil) & (~nil & nil) - // nil & never - // - // fallthrough + // ~(false?) & nil + // (~false & nil) & (~nil & nil) + // nil & never + // + // fallthrough case Relation::Superset: // ~(boolean | string) & true // (~boolean & true) & (~boolean & string) @@ -1008,8 +1068,8 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right) break; case Relation::Subset: - // ~false & boolean - // fallthrough + // ~false & boolean + // fallthrough case Relation::Intersects: // FIXME: The mkNegation here is pretty unfortunate. // Memoizing this will probably be important. @@ -1046,7 +1106,7 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right) changed = true; continue; case Relation::Subset: - // fallthrough + // fallthrough case Relation::Intersects: changed = true; newParts.insert(arena->addType(IntersectionType{{left, part}})); @@ -1085,15 +1145,15 @@ TypeId TypeSimplifier::intersectTypeWithNegation(TypeId left, TypeId right) // ~boolean & string return right; case Relation::Coincident: - // ~string & string - // fallthrough + // ~string & string + // fallthrough case Relation::Superset: // ~string & "hello" return builtinTypes->neverType; case Relation::Subset: - // ~string & unknown - // ~"hello" & string - // fallthrough + // ~string & unknown + // ~"hello" & string + // fallthrough case Relation::Intersects: // ~("hello" | boolean) & string // fallthrough @@ -1252,7 +1312,11 @@ std::optional TypeSimplifier::basicIntersect(TypeId left, TypeId right) auto it = rt->props.find(propName); if (it != rt->props.end() && leftProp.isShared() && it->second.isShared()) { - Relation r = relate(leftProp.type(), it->second.type()); + Relation r; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + r = relate(*leftProp.readTy, *it->second.readTy); + else + r = relate(leftProp.type_DEPRECATED(), it->second.type_DEPRECATED()); switch (r) { @@ -1302,21 +1366,43 @@ std::optional TypeSimplifier::basicIntersect(TypeId left, TypeId right) return std::nullopt; } - if (isTruthyType(left)) - if (auto res = basicIntersectWithTruthy(right)) - return res; + if (FFlag::LuauRefineTablesWithReadType) + { + if (isApproximatelyTruthyType(left)) + if (auto res = basicIntersectWithTruthy(right)) + return res; - if (isTruthyType(right)) - if (auto res = basicIntersectWithTruthy(left)) - return res; + if (isApproximatelyTruthyType(right)) + if (auto res = basicIntersectWithTruthy(left)) + return res; - if (isFalsyType(left)) - if (auto res = basicIntersectWithFalsy(right)) - return res; + if (isApproximatelyFalsyType(left)) + if (auto res = basicIntersectWithFalsy(right)) + return res; + + if (isApproximatelyFalsyType(right)) + if (auto res = basicIntersectWithFalsy(left)) + return res; + } + else + { + if (isTruthyType_DEPRECATED(left)) + if (auto res = basicIntersectWithTruthy(right)) + return res; + + if (isTruthyType_DEPRECATED(right)) + if (auto res = basicIntersectWithTruthy(left)) + return res; + + if (isFalsyType_DEPRECATED(left)) + if (auto res = basicIntersectWithFalsy(right)) + return res; + + if (isFalsyType_DEPRECATED(right)) + if (auto res = basicIntersectWithFalsy(left)) + return res; + } - if (isFalsyType(right)) - if (auto res = basicIntersectWithFalsy(left)) - return res; Relation relation = relate(left, right); if (left == right || Relation::Coincident == relation) @@ -1548,6 +1634,276 @@ TypeId TypeSimplifier::simplify(TypeId ty, DenseHashSet& seen) return ty; } +namespace +{ + +bool isSimpleDiscriminant(TypeId ty, DenseHashSet& seen) +{ + ty = follow(ty); + // If we *ever* see a recursive type, bail right away, clearly that is + // not simple. + if (seen.contains(ty)) + return false; + seen.insert(ty); + + // NOTE: We could probably support `{}` as a simple discriminant. + if (auto ttv = get(ty); ttv && ttv->props.size() == 1 && !ttv->indexer) + { + auto prop = begin(ttv->props)->second; + return (!prop.readTy || isSimpleDiscriminant(*prop.readTy, seen)) && (!prop.writeTy || isSimpleDiscriminant(*prop.writeTy, seen)); + } + + if (auto nt = get(ty)) + return isSimpleDiscriminant(nt->ty, seen); + + return is(ty) || isApproximatelyTruthyType(ty) || isApproximatelyFalsyType(ty); +} + +/** + * There are some types that are "simple", and thus easy to intersect against: + * - The "truthy" (`~(false?)`) and "falsy" (`false?`) types are simple. + * - Primitive types, singleton types, and extern types are simple + * - Table types are simple if they have no indexer, and have a single property + * who's read and write types are also simple. + * - Cyclic types are never simple. + */ +bool isSimpleDiscriminant(TypeId ty) +{ + DenseHashSet seenSet{nullptr}; + return isSimpleDiscriminant(ty, seenSet); +} + +} // namespace + +std::optional TypeSimplifier::intersectOne(TypeId target, TypeId discriminant) const +{ + switch (relate(target, discriminant)) + { + case Relation::Disjoint: // No A is a B or vice versa + return builtinTypes->neverType; + case Relation::Subset: // Every A is in B + case Relation::Coincident: // Every A is in B and vice versa + return target; + case Relation::Superset: // Every B is in A + return discriminant; + case Relation::Intersects: + default: + // Some As are in B and some Bs are in A. ex (number | string) <-> (string | boolean). + return std::nullopt; + } +} + +std::optional TypeSimplifier::subtractOne(TypeId target, TypeId discriminant) const +{ + target = follow(target); + discriminant = follow(discriminant); + + if (auto nt = get(discriminant)) + return intersectOne(target, nt->ty); + + switch (relate(target, discriminant)) + { + case Relation::Disjoint: // A v B is empty => A - B is equivalent to A + return target; + case Relation::Subset: // A v B is A => A - B is empty + case Relation::Coincident: // Same as above: A == B so A - B = {} + return builtinTypes->neverType; + case Relation::Superset: + case Relation::Intersects: + default: + return std::nullopt; + } +} + +std::optional TypeSimplifier::intersectProperty(const Property& target, const Property& discriminant, DenseHashSet& seen) const +{ + // NOTE: I invite the reader to refactor the below code as a fun coding + // exercise. It looks ugly to me, but I don't think we can make it + // any cleaner. + + Property prop; + prop.deprecated = target.deprecated || discriminant.deprecated; + + // We're trying to follow the following rules for both read and write types: + // * If the type is present on both properties, intersect it, and return + // `std::nullopt` if we fail. + // * If the type only exists on one property or the other, take that. + + if (target.readTy && discriminant.readTy) + { + prop.readTy = intersectWithSimpleDiscriminant(*target.readTy, *discriminant.readTy, seen); + if (!prop.readTy) + return std::nullopt; + } + else if (target.readTy && !discriminant.readTy) + prop.readTy = target.readTy; + else if (!target.readTy && discriminant.readTy) + prop.readTy = discriminant.readTy; + + if (target.writeTy && discriminant.writeTy) + { + prop.writeTy = intersectWithSimpleDiscriminant(*target.writeTy, *discriminant.writeTy, seen); + if (!prop.writeTy) + return std::nullopt; + } + else if (target.writeTy && !discriminant.writeTy) + prop.writeTy = target.writeTy; + else if (!target.writeTy && discriminant.writeTy) + prop.writeTy = discriminant.writeTy; + + return {prop}; +} + +std::optional TypeSimplifier::intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant, DenseHashSet& seen) const +{ + if (seen.contains(target)) + return std::nullopt; + + target = follow(target); + discriminant = follow(discriminant); + + if (auto ut = get(target)) + { + seen.insert(target); + TypeIds options; + for (TypeId option : ut) + { + auto result = intersectWithSimpleDiscriminant(option, discriminant, seen); + + if (!result) + return std::nullopt; + + if (is(result)) + return builtinTypes->unknownType; + + if (!is(*result)) + options.insert(*result); + } + if (options.empty()) + return builtinTypes->neverType; + if (options.size() == 1) + return *options.begin(); + return arena->addType(UnionType{options.take()}); + } + + if (auto it = get(target)) + { + seen.insert(target); + TypeIds parts; + for (TypeId part : it) + { + auto result = intersectWithSimpleDiscriminant(part, discriminant, seen); + if (!result) + return std::nullopt; + + if (is(*result)) + return builtinTypes->neverType; + + if (auto subIntersection = get(*result)) + { + for (TypeId subOption : subIntersection) + { + if (is(subOption)) + return builtinTypes->neverType; + if (!is(result)) + parts.insert(*result); + } + } + else if (!is(*result)) + parts.insert(*result); + } + if (parts.empty()) + return builtinTypes->unknownType; + if (parts.size() == 1) + return *parts.begin(); + return arena->addType(IntersectionType{parts.take()}); + } + + if (auto ttv = get(target)) + { + if (auto discTtv = get(discriminant)) + { + // The precondition of this function is that `discriminant` is + // simple, so if it's a table it *must* be a sealed table with + // a single property and no indexer. + LUAU_ASSERT(discTtv->props.size() == 1 && !discTtv->indexer); + const auto discProp = begin(discTtv->props); + if (auto tyProp = ttv->props.find(discProp->first); tyProp != ttv->props.end()) + { + auto property = intersectProperty(tyProp->second, discProp->second, seen); + if (!property) + return std::nullopt; + if (property->readTy && is(follow(property->readTy))) + return builtinTypes->neverType; + if (property->writeTy && is(follow(property->writeTy))) + return builtinTypes->neverType; + + // If the property we get back is pointer identical to the + // original property, return the underlying property as an + // optimization. + if (tyProp->second.readTy == property->readTy && tyProp->second.writeTy == property->writeTy) + return target; + + CloneState cs{builtinTypes}; + TypeId result = shallowClone(target, *arena, cs, /* clonePersistentTypes */ true); + auto resultTtv = getMutable(result); + LUAU_ASSERT(resultTtv); + resultTtv->props[tyProp->first] = *property; + // Shallow cloning clears out scopes, so let's put back the + // scope from the original type. + resultTtv->scope = ttv->scope; + return result; + } + + CloneState cs{builtinTypes}; + TypeId result = shallowClone(target, *arena, cs, /* clonePersistentTypes */ true); + // Shallow cloning clears out scopes, so let's put back the + // scope from the original type. + auto resultTtv = getMutable(result); + LUAU_ASSERT(resultTtv); + resultTtv->props.emplace(discProp->first, discProp->second); + resultTtv->scope = ttv->scope; + return result; + } + + // At this point, we're doing something like: + // + // { ... } & ~nil + // + // Which can be handled via fallthrough. + } + + // FIXME: We could probably return to this. + if (is(target)) + return std::nullopt; + + if (isApproximatelyTruthyType(discriminant)) + return basicIntersectWithTruthy(target); + + if (isApproximatelyTruthyType(target)) + return basicIntersectWithTruthy(discriminant); + + if (isApproximatelyFalsyType(discriminant)) + return basicIntersectWithFalsy(target); + + if (isApproximatelyFalsyType(target)) + return basicIntersectWithFalsy(discriminant); + + if (is(target)) + return arena->addType(UnionType{{builtinTypes->errorType, discriminant}}); + + if (auto nty = get(discriminant)) + return subtractOne(target, nty->ty); + + return intersectOne(target, discriminant); +} + +std::optional TypeSimplifier::intersectWithSimpleDiscriminant(TypeId target, TypeId discriminant) const +{ + DenseHashSet seenSet{nullptr}; + return intersectWithSimpleDiscriminant(target, discriminant, seenSet); +} + SimplifyResult simplifyIntersection(NotNull builtinTypes, NotNull arena, TypeId left, TypeId right) { TypeSimplifier s{builtinTypes, arena}; @@ -1581,4 +1937,25 @@ SimplifyResult simplifyUnion(NotNull builtinTypes, NotNull intersectWithSimpleDiscriminant( + NotNull builtinTypes, + NotNull arena, + TypeId target, + TypeId discriminant +) +{ + if (!isSimpleDiscriminant(discriminant)) + { + if (isSimpleDiscriminant(target)) + return intersectWithSimpleDiscriminant(builtinTypes, arena, discriminant, target); + return std::nullopt; + } + + TypeSimplifier s{builtinTypes, arena}; + + return s.intersectWithSimpleDiscriminant(target, discriminant); +} + + } // namespace Luau diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index b99e2238..c714decb 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -10,6 +10,7 @@ LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTINTVARIABLE(LuauTarjanPreallocationSize, 256) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauSolverAgnosticClone) namespace Luau @@ -196,7 +197,7 @@ void Tarjan::visitChildren(TypeId ty, int index) visitChild(prop.writeTy); } else - visitChild(prop.type()); + visitChild(prop.type_DEPRECATED()); } if (ttv->indexer) @@ -245,7 +246,15 @@ void Tarjan::visitChildren(TypeId ty, int index) else if (const ExternType* etv = get(ty)) { for (const auto& [name, prop] : etv->props) - visitChild(prop.type()); + { + if (FFlag::LuauSolverV2 && FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + visitChild(prop.readTy); + visitChild(prop.writeTy); + } + else + visitChild(prop.type_DEPRECATED()); + } if (etv->parent) visitChild(*etv->parent); @@ -782,7 +791,7 @@ void Substitution::replaceChildren(TypeId ty) prop.writeTy = replace(prop.writeTy); } else - prop.setType(replace(prop.type())); + prop.setType(replace(prop.type_DEPRECATED())); } if (ttv->indexer) @@ -831,7 +840,17 @@ void Substitution::replaceChildren(TypeId ty) else if (ExternType* etv = getMutable(ty)) { for (auto& [name, prop] : etv->props) - prop.setType(replace(prop.type())); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + { + if (prop.readTy) + prop.readTy = replace(prop.readTy); + if (prop.writeTy) + prop.writeTy = replace(prop.writeTy); + } + else + prop.setType(replace(prop.type_DEPRECATED())); + } if (etv->parent) etv->parent = replace(*etv->parent); diff --git a/Analysis/src/Subtyping.cpp b/Analysis/src/Subtyping.cpp index 3f61fea9..fe586e76 100644 --- a/Analysis/src/Subtyping.cpp +++ b/Analysis/src/Subtyping.cpp @@ -21,6 +21,7 @@ LUAU_FASTINTVARIABLE(LuauSubtypingReasoningLimit, 100) LUAU_FASTFLAG(LuauClipVariadicAnysFromArgsToGenericFuncs2) LUAU_FASTFLAGVARIABLE(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -1466,9 +1467,20 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Tabl if (isCovariantWith(env, builtinTypes->stringType, subTable->indexer->indexType, scope).isSubtype) { if (superProp.isShared()) - results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type(), scope) - .withSubComponent(TypePath::TypeField::IndexResult) - .withSuperComponent(TypePath::Property::read(name))); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, *superProp.readTy, scope) + .withSubComponent(TypePath::TypeField::IndexResult) + .withSuperComponent(TypePath::Property::read(name))); + } + else + { + results.push_back(isInvariantWith(env, subTable->indexer->indexResultType, superProp.type_DEPRECATED(), scope) + .withSubComponent(TypePath::TypeField::IndexResult) + .withSuperComponent(TypePath::Property::read(name))); + } + } else { if (superProp.readTy) @@ -1643,10 +1655,22 @@ SubtypingResult Subtyping::isCovariantWith(SubtypingEnvironment& env, const Prim { if (auto it = mttv->props.find("__index"); it != mttv->props.end()) { - if (auto stringTable = get(it->second.type())) - result.orElse( - isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()) - ); + + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + // the `string` metatable should not have any write-only types. + LUAU_ASSERT(*it->second.readTy); + + if (auto stringTable = get(*it->second.readTy)) + result.orElse(isCovariantWith(env, stringTable, superTable, scope) + .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); + } + else + { + if (auto stringTable = get(it->second.type_DEPRECATED())) + result.orElse(isCovariantWith(env, stringTable, superTable, scope) + .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); + } } } } @@ -1676,10 +1700,21 @@ SubtypingResult Subtyping::isCovariantWith( { if (auto it = mttv->props.find("__index"); it != mttv->props.end()) { - if (auto stringTable = get(it->second.type())) - result.orElse( - isCovariantWith(env, stringTable, superTable, scope).withSubPath(TypePath::PathBuilder().mt().readProp("__index").build()) - ); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + // the `string` metatable should not have any write-only types. + LUAU_ASSERT(*it->second.readTy); + + if (auto stringTable = get(*it->second.readTy)) + result.orElse(isCovariantWith(env, stringTable, superTable, scope) + .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); + } + else + { + if (auto stringTable = get(it->second.type_DEPRECATED())) + result.orElse(isCovariantWith(env, stringTable, superTable, scope) + .withSubPath(TypePath::PathBuilder().mt().readProp("__index").build())); + } } } } @@ -1712,7 +1747,12 @@ SubtypingResult Subtyping::isCovariantWith( SubtypingResult res{true}; if (superProp.isShared() && subProp.isShared()) - res.andAlso(isInvariantWith(env, subProp.type(), superProp.type(), scope).withBothComponent(TypePath::Property::read(name))); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + res.andAlso(isInvariantWith(env, *subProp.readTy, *superProp.readTy, scope).withBothComponent(TypePath::Property::read(name))); + else + res.andAlso(isInvariantWith(env, subProp.type_DEPRECATED(), superProp.type_DEPRECATED(), scope).withBothComponent(TypePath::Property::read(name))); + } else { if (superProp.readTy.has_value() && subProp.readTy.has_value()) diff --git a/Analysis/src/ToDot.cpp b/Analysis/src/ToDot.cpp index ecf89fbf..30ce2aec 100644 --- a/Analysis/src/ToDot.cpp +++ b/Analysis/src/ToDot.cpp @@ -10,7 +10,8 @@ #include #include -LUAU_FASTFLAG(LuauSolverV2); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -188,7 +189,29 @@ void StateDot::visitChildren(TypeId ty, int index) return visitChild(*t.boundTo, index, "boundTo"); for (const auto& [name, prop] : t.props) - visitChild(prop.type(), index, name.c_str()); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + if (prop.isShared()) + visitChild(*prop.readTy, index, name.c_str()); + else + { + if (prop.readTy) + { + std::string readName = "read " + name; + visitChild(*prop.readTy, index, readName.c_str()); + } + + if (prop.writeTy) + { + std::string writeName = "write " + name; + visitChild(*prop.writeTy, index, writeName.c_str()); + } + } + } + else + visitChild(prop.type_DEPRECATED(), index, name.c_str()); + } if (t.indexer) { visitChild(t.indexer->indexType, index, "[index]"); @@ -306,7 +329,29 @@ void StateDot::visitChildren(TypeId ty, int index) finishNode(); for (const auto& [name, prop] : t.props) - visitChild(prop.type(), index, name.c_str()); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + if (prop.isShared()) + visitChild(*prop.readTy, index, name.c_str()); + else + { + if (prop.readTy) + { + std::string readName = "read " + name; + visitChild(*prop.readTy, index, readName.c_str()); + } + + if (prop.writeTy) + { + std::string writeName = "write " + name; + visitChild(*prop.writeTy, index, writeName.c_str()); + } + } + } + else + visitChild(prop.type_DEPRECATED(), index, name.c_str()); + } if (t.parent) visitChild(*t.parent, index, "[parent]"); diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 2f121de4..1b14fb07 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -22,6 +22,7 @@ LUAU_FASTFLAGVARIABLE(LuauEnableDenseTableAlias) LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) /* * Enables increasing levels of verbosity for Luau type names when stringifying. @@ -409,7 +410,10 @@ struct TypeStringifier if (prop.isShared()) { emitKey(name); - stringify(prop.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + stringify(*prop.readTy); + else + stringify(prop.type_DEPRECATED()); return; } @@ -440,7 +444,7 @@ struct TypeStringifier return _newStringify(name, prop); emitKey(name); - stringify(prop.type()); + stringify(prop.type_DEPRECATED()); } void stringify(TypePackId tp); diff --git a/Analysis/src/TopoSortStatements.cpp b/Analysis/src/TopoSortStatements.cpp index a4203dab..0a3750db 100644 --- a/Analysis/src/TopoSortStatements.cpp +++ b/Analysis/src/TopoSortStatements.cpp @@ -41,8 +41,6 @@ #include #include -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - namespace Luau { @@ -302,7 +300,7 @@ struct ArcCollector : public AstVisitor bool visit(AstTypePack* node) override { - return FFlag::LuauStoreReturnTypesAsPackOnAst; + return true; } }; diff --git a/Analysis/src/Transpiler.cpp b/Analysis/src/Transpiler.cpp index 36b2247f..2ec5d920 100644 --- a/Analysis/src/Transpiler.cpp +++ b/Analysis/src/Transpiler.cpp @@ -10,8 +10,6 @@ #include #include -LUAU_FASTFLAG(LuauStoreCSTData2) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions) namespace @@ -165,17 +163,7 @@ struct StringWriter : Writer void symbol(std::string_view s) override { - if (FFlag::LuauStoreCSTData2) - { - write(s); - } - else - { - if (isDigit(lastChar) && s[0] == '.') - space(); - - write(s); - } + write(s); } void literal(std::string_view s) override @@ -255,7 +243,7 @@ public: first = !first; else { - if (FFlag::LuauStoreCSTData2 && commaPosition) + if (commaPosition) { writer.advance(*commaPosition); commaPosition++; @@ -307,1024 +295,6 @@ private: size_t idx = 0; }; -struct Printer_DEPRECATED -{ - explicit Printer_DEPRECATED(Writer& writer) - : writer(writer) - { - } - - bool writeTypes = false; - Writer& writer; - - void visualize(const AstLocal& local) - { - advance(local.location.begin); - - writer.identifier(local.name.value); - if (writeTypes && local.annotation) - { - writer.symbol(":"); - visualizeTypeAnnotation(*local.annotation); - } - } - - void visualizeTypePackAnnotation(const AstTypePack& annotation, bool forVarArg, bool unconditionallyParenthesize = true) - { - advance(annotation.location.begin); - if (const AstTypePackVariadic* variadicTp = annotation.as()) - { - if (!forVarArg) - writer.symbol("..."); - - visualizeTypeAnnotation(*variadicTp->variadicType); - } - else if (const AstTypePackGeneric* genericTp = annotation.as()) - { - writer.symbol(genericTp->genericName.value); - writer.symbol("..."); - } - else if (const AstTypePackExplicit* explicitTp = annotation.as()) - { - LUAU_ASSERT(!forVarArg); - visualizeTypeList(explicitTp->typeList, unconditionallyParenthesize); - } - else - { - LUAU_ASSERT(!"Unknown TypePackAnnotation kind"); - } - } - - void visualizeTypeList(const AstTypeList& list, bool unconditionallyParenthesize) - { - size_t typeCount = list.types.size + (list.tailType != nullptr ? 1 : 0); - if (typeCount == 0) - { - writer.symbol("("); - writer.symbol(")"); - } - else if (typeCount == 1) - { - bool shouldParenthesize = unconditionallyParenthesize && (list.types.size == 0 || !list.types.data[0]->is()); - if (shouldParenthesize) - writer.symbol("("); - - // Only variadic tail - if (list.types.size == 0) - { - visualizeTypePackAnnotation(*list.tailType, false); - } - else - { - visualizeTypeAnnotation(*list.types.data[0]); - } - - if (shouldParenthesize) - writer.symbol(")"); - } - else - { - writer.symbol("("); - - bool first = true; - for (const auto& el : list.types) - { - if (first) - first = false; - else - writer.symbol(","); - - visualizeTypeAnnotation(*el); - } - - if (list.tailType) - { - writer.symbol(","); - visualizeTypePackAnnotation(*list.tailType, false); - } - - writer.symbol(")"); - } - } - - bool isIntegerish(double d) - { - if (d <= std::numeric_limits::max() && d >= std::numeric_limits::min()) - return double(int(d)) == d && !(d == 0.0 && signbit(d)); - else - return false; - } - - void visualize(AstExpr& expr) - { - advance(expr.location.begin); - - if (const auto& a = expr.as()) - { - writer.symbol("("); - visualize(*a->expr); - writer.symbol(")"); - } - else if (expr.is()) - { - writer.keyword("nil"); - } - else if (const auto& a = expr.as()) - { - if (a->value) - writer.keyword("true"); - else - writer.keyword("false"); - } - else if (const auto& a = expr.as()) - { - if (isinf(a->value)) - { - if (a->value > 0) - writer.literal("1e500"); - else - writer.literal("-1e500"); - } - else if (isnan(a->value)) - writer.literal("0/0"); - else - { - if (isIntegerish(a->value)) - writer.literal(std::to_string(int(a->value))); - else - { - char buffer[100]; - size_t len = snprintf(buffer, sizeof(buffer), "%.17g", a->value); - writer.literal(std::string_view{buffer, len}); - } - } - } - else if (const auto& a = expr.as()) - { - writer.string(std::string_view(a->value.data, a->value.size)); - } - else if (const auto& a = expr.as()) - { - writer.identifier(a->local->name.value); - } - else if (const auto& a = expr.as()) - { - writer.identifier(a->name.value); - } - else if (expr.is()) - { - writer.symbol("..."); - } - else if (const auto& a = expr.as()) - { - visualize(*a->func); - writer.symbol("("); - - bool first = true; - for (const auto& arg : a->args) - { - if (first) - first = false; - else - writer.symbol(","); - - visualize(*arg); - } - - writer.symbol(")"); - } - else if (const auto& a = expr.as()) - { - visualize(*a->expr); - writer.symbol(std::string(1, a->op)); - writer.write(a->index.value); - } - else if (const auto& a = expr.as()) - { - visualize(*a->expr); - writer.symbol("["); - visualize(*a->index); - writer.symbol("]"); - } - else if (const auto& a = expr.as()) - { - writer.keyword("function"); - visualizeFunctionBody(*a); - } - else if (const auto& a = expr.as()) - { - writer.symbol("{"); - - bool first = true; - - for (const auto& item : a->items) - { - if (first) - first = false; - else - writer.symbol(","); - - switch (item.kind) - { - case AstExprTable::Item::List: - break; - - case AstExprTable::Item::Record: - { - const auto& value = item.key->as()->value; - advance(item.key->location.begin); - writer.identifier(std::string_view(value.data, value.size)); - writer.maybeSpace(item.value->location.begin, 1); - writer.symbol("="); - } - break; - - case AstExprTable::Item::General: - { - writer.symbol("["); - visualize(*item.key); - writer.symbol("]"); - writer.maybeSpace(item.value->location.begin, 1); - writer.symbol("="); - } - break; - - default: - LUAU_ASSERT(!"Unknown table item kind"); - } - - advance(item.value->location.begin); - visualize(*item.value); - } - - // Decrement endPos column so that we advance to before the closing `}` brace before writing, rather than after it - Position endPos = expr.location.end; - if (endPos.column > 0) - --endPos.column; - - advance(endPos); - - writer.symbol("}"); - advance(expr.location.end); - } - else if (const auto& a = expr.as()) - { - switch (a->op) - { - case AstExprUnary::Not: - writer.keyword("not"); - break; - case AstExprUnary::Minus: - writer.symbol("-"); - break; - case AstExprUnary::Len: - writer.symbol("#"); - break; - } - visualize(*a->expr); - } - else if (const auto& a = expr.as()) - { - visualize(*a->left); - - switch (a->op) - { - case AstExprBinary::Add: - case AstExprBinary::Sub: - case AstExprBinary::Mul: - case AstExprBinary::Div: - case AstExprBinary::FloorDiv: - case AstExprBinary::Mod: - case AstExprBinary::Pow: - case AstExprBinary::CompareLt: - case AstExprBinary::CompareGt: - writer.maybeSpace(a->right->location.begin, 2); - writer.symbol(toString(a->op)); - break; - case AstExprBinary::Concat: - case AstExprBinary::CompareNe: - case AstExprBinary::CompareEq: - case AstExprBinary::CompareLe: - case AstExprBinary::CompareGe: - case AstExprBinary::Or: - writer.maybeSpace(a->right->location.begin, 3); - writer.keyword(toString(a->op)); - break; - case AstExprBinary::And: - writer.maybeSpace(a->right->location.begin, 4); - writer.keyword(toString(a->op)); - break; - default: - LUAU_ASSERT(!"Unknown Op"); - } - - visualize(*a->right); - } - else if (const auto& a = expr.as()) - { - visualize(*a->expr); - - if (writeTypes) - { - writer.maybeSpace(a->annotation->location.begin, 2); - writer.symbol("::"); - visualizeTypeAnnotation(*a->annotation); - } - } - else if (const auto& a = expr.as()) - { - writer.keyword("if"); - visualize(*a->condition); - writer.keyword("then"); - visualize(*a->trueExpr); - writer.keyword("else"); - visualize(*a->falseExpr); - } - else if (const auto& a = expr.as()) - { - writer.symbol("`"); - - size_t index = 0; - - for (const auto& string : a->strings) - { - writer.write(escape(std::string_view(string.data, string.size), /* escapeForInterpString = */ true)); - - if (index < a->expressions.size) - { - writer.symbol("{"); - visualize(*a->expressions.data[index]); - writer.symbol("}"); - } - - index++; - } - - writer.symbol("`"); - } - else if (const auto& a = expr.as()) - { - writer.symbol("(error-expr"); - - for (size_t i = 0; i < a->expressions.size; i++) - { - writer.symbol(i == 0 ? ": " : ", "); - visualize(*a->expressions.data[i]); - } - - writer.symbol(")"); - } - else - { - LUAU_ASSERT(!"Unknown AstExpr"); - } - } - - void writeEnd(const Location& loc) - { - Position endPos = loc.end; - if (endPos.column >= 3) - endPos.column -= 3; - advance(endPos); - writer.keyword("end"); - } - - void advance(const Position& newPos) - { - writer.advance(newPos); - } - - void visualize(AstStat& program) - { - advance(program.location.begin); - - if (const auto& block = program.as()) - { - writer.keyword("do"); - for (const auto& s : block->body) - visualize(*s); - writeEnd(program.location); - } - else if (const auto& a = program.as()) - { - writer.keyword("if"); - visualizeElseIf(*a); - } - else if (const auto& a = program.as()) - { - writer.keyword("while"); - visualize(*a->condition); - writer.keyword("do"); - visualizeBlock(*a->body); - writeEnd(program.location); - } - else if (const auto& a = program.as()) - { - writer.keyword("repeat"); - visualizeBlock(*a->body); - if (a->condition->location.begin.column > 5) - writer.advance(Position{a->condition->location.begin.line, a->condition->location.begin.column - 6}); - writer.keyword("until"); - visualize(*a->condition); - } - else if (program.is()) - writer.keyword("break"); - else if (program.is()) - writer.keyword("continue"); - else if (const auto& a = program.as()) - { - writer.keyword("return"); - - bool first = true; - for (const auto& expr : a->list) - { - if (first) - first = false; - else - writer.symbol(","); - visualize(*expr); - } - } - else if (const auto& a = program.as()) - { - visualize(*a->expr); - } - else if (const auto& a = program.as()) - { - writer.keyword("local"); - - bool first = true; - for (const auto& local : a->vars) - { - if (first) - first = false; - else - writer.write(","); - - visualize(*local); - } - - first = true; - for (const auto& value : a->values) - { - if (first) - { - first = false; - writer.maybeSpace(value->location.begin, 2); - writer.symbol("="); - } - else - writer.symbol(","); - - visualize(*value); - } - } - else if (const auto& a = program.as()) - { - writer.keyword("for"); - - visualize(*a->var); - writer.symbol("="); - visualize(*a->from); - writer.symbol(","); - visualize(*a->to); - if (a->step) - { - writer.symbol(","); - visualize(*a->step); - } - writer.keyword("do"); - visualizeBlock(*a->body); - - writeEnd(program.location); - } - else if (const auto& a = program.as()) - { - writer.keyword("for"); - - bool first = true; - for (const auto& var : a->vars) - { - if (first) - first = false; - else - writer.symbol(","); - - visualize(*var); - } - - writer.keyword("in"); - - first = true; - for (const auto& val : a->values) - { - if (first) - first = false; - else - writer.symbol(","); - - visualize(*val); - } - - writer.keyword("do"); - - visualizeBlock(*a->body); - - writeEnd(program.location); - } - else if (const auto& a = program.as()) - { - bool first = true; - for (const auto& var : a->vars) - { - if (first) - first = false; - else - writer.symbol(","); - visualize(*var); - } - - first = true; - for (const auto& value : a->values) - { - if (first) - { - writer.maybeSpace(value->location.begin, 1); - writer.symbol("="); - first = false; - } - else - writer.symbol(","); - - visualize(*value); - } - } - else if (const auto& a = program.as()) - { - visualize(*a->var); - - switch (a->op) - { - case AstExprBinary::Add: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("+="); - break; - case AstExprBinary::Sub: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("-="); - break; - case AstExprBinary::Mul: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("*="); - break; - case AstExprBinary::Div: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("/="); - break; - case AstExprBinary::FloorDiv: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("//="); - break; - case AstExprBinary::Mod: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("%="); - break; - case AstExprBinary::Pow: - writer.maybeSpace(a->value->location.begin, 2); - writer.symbol("^="); - break; - case AstExprBinary::Concat: - writer.maybeSpace(a->value->location.begin, 3); - writer.symbol("..="); - break; - default: - LUAU_ASSERT(!"Unexpected compound assignment op"); - } - - visualize(*a->value); - } - else if (const auto& a = program.as()) - { - writer.keyword("function"); - visualize(*a->name); - visualizeFunctionBody(*a->func); - } - else if (const auto& a = program.as()) - { - writer.keyword("local function"); - advance(a->name->location.begin); - writer.identifier(a->name->name.value); - visualizeFunctionBody(*a->func); - } - else if (const auto& a = program.as()) - { - if (writeTypes) - { - if (a->exported) - writer.keyword("export"); - - writer.keyword("type"); - writer.identifier(a->name.value); - if (a->generics.size > 0 || a->genericPacks.size > 0) - { - writer.symbol("<"); - CommaSeparatorInserter comma(writer); - - for (auto o : a->generics) - { - comma(); - - writer.advance(o->location.begin); - writer.identifier(o->name.value); - - if (o->defaultValue) - { - writer.maybeSpace(o->defaultValue->location.begin, 2); - writer.symbol("="); - visualizeTypeAnnotation(*o->defaultValue); - } - } - - for (auto o : a->genericPacks) - { - comma(); - - writer.advance(o->location.begin); - writer.identifier(o->name.value); - writer.symbol("..."); - - if (o->defaultValue) - { - writer.maybeSpace(o->defaultValue->location.begin, 2); - writer.symbol("="); - visualizeTypePackAnnotation(*o->defaultValue, false); - } - } - - writer.symbol(">"); - } - writer.maybeSpace(a->type->location.begin, 2); - writer.symbol("="); - visualizeTypeAnnotation(*a->type); - } - } - else if (const auto& t = program.as()) - { - if (writeTypes) - { - writer.keyword("type function"); - writer.identifier(t->name.value); - visualizeFunctionBody(*t->body); - } - } - else if (const auto& a = program.as()) - { - writer.symbol("(error-stat"); - - for (size_t i = 0; i < a->expressions.size; i++) - { - writer.symbol(i == 0 ? ": " : ", "); - visualize(*a->expressions.data[i]); - } - - for (size_t i = 0; i < a->statements.size; i++) - { - writer.symbol(i == 0 && a->expressions.size == 0 ? ": " : ", "); - visualize(*a->statements.data[i]); - } - - writer.symbol(")"); - } - else - { - LUAU_ASSERT(!"Unknown AstStat"); - } - - if (program.hasSemicolon) - writer.symbol(";"); - } - - void visualizeFunctionBody(AstExprFunction& func) - { - if (func.generics.size > 0 || func.genericPacks.size > 0) - { - CommaSeparatorInserter comma(writer); - writer.symbol("<"); - for (const auto& o : func.generics) - { - comma(); - - writer.advance(o->location.begin); - writer.identifier(o->name.value); - } - for (const auto& o : func.genericPacks) - { - comma(); - - writer.advance(o->location.begin); - writer.identifier(o->name.value); - writer.symbol("..."); - } - writer.symbol(">"); - } - - writer.symbol("("); - CommaSeparatorInserter comma(writer); - - for (size_t i = 0; i < func.args.size; ++i) - { - AstLocal* local = func.args.data[i]; - - comma(); - - advance(local->location.begin); - writer.identifier(local->name.value); - if (writeTypes && local->annotation) - { - writer.symbol(":"); - visualizeTypeAnnotation(*local->annotation); - } - } - - if (func.vararg) - { - comma(); - advance(func.varargLocation.begin); - writer.symbol("..."); - - if (func.varargAnnotation) - { - writer.symbol(":"); - visualizeTypePackAnnotation(*func.varargAnnotation, true); - } - } - - writer.symbol(")"); - - if (writeTypes && (FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value())) - { - writer.symbol(":"); - writer.space(); - - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visualizeTypePackAnnotation(*func.returnAnnotation, false, false); - else - visualizeTypeList(*func.returnAnnotation_DEPRECATED, false); - } - - visualizeBlock(*func.body); - writeEnd(func.location); - } - - void visualizeBlock(AstStatBlock& block) - { - for (const auto& s : block.body) - visualize(*s); - writer.advance(block.location.end); - } - - void visualizeBlock(AstStat& stat) - { - if (AstStatBlock* block = stat.as()) - visualizeBlock(*block); - else - LUAU_ASSERT(!"visualizeBlock was expecting an AstStatBlock"); - } - - void visualizeElseIf(AstStatIf& elseif) - { - visualize(*elseif.condition); - writer.keyword("then"); - visualizeBlock(*elseif.thenbody); - - if (elseif.elsebody == nullptr) - { - writeEnd(elseif.location); - } - else if (auto elseifelseif = elseif.elsebody->as()) - { - writer.keyword("elseif"); - visualizeElseIf(*elseifelseif); - } - else - { - writer.keyword("else"); - - visualizeBlock(*elseif.elsebody); - writeEnd(elseif.location); - } - } - - void visualizeTypeAnnotation(const AstType& typeAnnotation) - { - advance(typeAnnotation.location.begin); - if (const auto& a = typeAnnotation.as()) - { - if (a->prefix) - { - writer.write(a->prefix->value); - writer.symbol("."); - } - - writer.write(a->name.value); - if (a->parameters.size > 0 || a->hasParameterList) - { - CommaSeparatorInserter comma(writer); - writer.symbol("<"); - for (auto o : a->parameters) - { - comma(); - - if (o.type) - visualizeTypeAnnotation(*o.type); - else - visualizeTypePackAnnotation(*o.typePack, false); - } - - writer.symbol(">"); - } - } - else if (const auto& a = typeAnnotation.as()) - { - if (a->generics.size > 0 || a->genericPacks.size > 0) - { - CommaSeparatorInserter comma(writer); - writer.symbol("<"); - for (const auto& o : a->generics) - { - comma(); - - writer.advance(o->location.begin); - writer.identifier(o->name.value); - } - for (const auto& o : a->genericPacks) - { - comma(); - - writer.advance(o->location.begin); - writer.identifier(o->name.value); - writer.symbol("..."); - } - writer.symbol(">"); - } - - { - visualizeTypeList(a->argTypes, true); - } - - writer.symbol("->"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visualizeTypePackAnnotation(*a->returnTypes, false); - else - visualizeTypeList(a->returnTypes_DEPRECATED, true); - } - else if (const auto& a = typeAnnotation.as()) - { - AstTypeReference* indexType = a->indexer ? a->indexer->indexType->as() : nullptr; - - if (a->props.size == 0 && indexType && indexType->name == "number") - { - writer.symbol("{"); - visualizeTypeAnnotation(*a->indexer->resultType); - writer.symbol("}"); - } - else - { - CommaSeparatorInserter comma(writer); - - writer.symbol("{"); - - for (std::size_t i = 0; i < a->props.size; ++i) - { - comma(); - advance(a->props.data[i].location.begin); - writer.identifier(a->props.data[i].name.value); - if (a->props.data[i].type) - { - writer.symbol(":"); - visualizeTypeAnnotation(*a->props.data[i].type); - } - } - if (a->indexer) - { - comma(); - writer.symbol("["); - visualizeTypeAnnotation(*a->indexer->indexType); - writer.symbol("]"); - writer.symbol(":"); - visualizeTypeAnnotation(*a->indexer->resultType); - } - writer.symbol("}"); - } - } - else if (auto a = typeAnnotation.as()) - { - writer.keyword("typeof"); - writer.symbol("("); - visualize(*a->expr); - writer.symbol(")"); - } - else if (const auto& a = typeAnnotation.as()) - { - if (a->types.size == 2) - { - AstType* l = a->types.data[0]; - AstType* r = a->types.data[1]; - - auto lta = l->as(); - if (lta && lta->name == "nil" && !r->is()) - std::swap(l, r); - - // it's still possible that we had a (T | U) or (T | nil) and not (nil | T) - auto rta = r->as(); - if (rta && rta->name == "nil") - { - bool wrap = l->as() || l->as(); - - if (wrap) - writer.symbol("("); - - visualizeTypeAnnotation(*l); - - if (wrap) - writer.symbol(")"); - - writer.symbol("?"); - return; - } - } - - for (size_t i = 0; i < a->types.size; ++i) - { - if (a->types.data[i]->is()) - { - writer.symbol("?"); - continue; - } - - if (i > 0) - { - writer.maybeSpace(a->types.data[i]->location.begin, 2); - writer.symbol("|"); - } - - bool wrap = a->types.data[i]->as() || a->types.data[i]->as(); - - if (wrap) - writer.symbol("("); - - visualizeTypeAnnotation(*a->types.data[i]); - - if (wrap) - writer.symbol(")"); - } - } - else if (const auto& a = typeAnnotation.as()) - { - for (size_t i = 0; i < a->types.size; ++i) - { - if (i > 0) - { - writer.maybeSpace(a->types.data[i]->location.begin, 2); - writer.symbol("&"); - } - - bool wrap = a->types.data[i]->as() || a->types.data[i]->as(); - - if (wrap) - writer.symbol("("); - - visualizeTypeAnnotation(*a->types.data[i]); - - if (wrap) - writer.symbol(")"); - } - } - else if (const auto& a = typeAnnotation.as()) - { - writer.symbol("("); - visualizeTypeAnnotation(*a->type); - writer.symbol(")"); - } - else if (const auto& a = typeAnnotation.as()) - { - writer.keyword(a->value ? "true" : "false"); - } - else if (const auto& a = typeAnnotation.as()) - { - writer.string(std::string_view(a->value.data, a->value.size)); - } - else if (typeAnnotation.is()) - { - writer.symbol("%error-type%"); - } - else - { - LUAU_ASSERT(!"Unknown AstType"); - } - } -}; - struct Printer { explicit Printer(Writer& writer, CstNodeMap cstNodeMap) @@ -1382,7 +352,7 @@ struct Printer if (const auto cstNode = lookupCstNode(explicitTp)) visualizeTypeList( explicitTp->typeList, - FFlag::LuauStoreReturnTypesAsPackOnAst ? cstNode->hasParentheses : true, + cstNode->hasParentheses, cstNode->openParenthesesPosition, cstNode->closeParenthesesPosition, cstNode->commaPositions @@ -2295,8 +1265,7 @@ struct Printer if (program.hasSemicolon) { - if (FFlag::LuauStoreCSTData2) - advanceBefore(program.location.end, 1); + advanceBefore(program.location.end, 1); writer.symbol(";"); } } @@ -2348,7 +1317,7 @@ struct Printer writer.identifier(local->name.value); if (writeTypes && local->annotation) { - if (FFlag::LuauStoreReturnTypesAsPackOnAst && FFlag::LuauStoreLocalAnnotationColonPositions && cstNode) + if (FFlag::LuauStoreLocalAnnotationColonPositions && cstNode) { LUAU_ASSERT(cstNode->argsAnnotationColonPositions.size > i); advance(cstNode->argsAnnotationColonPositions.data[i]); @@ -2366,7 +1335,7 @@ struct Printer if (func.varargAnnotation) { - if (FFlag::LuauStoreReturnTypesAsPackOnAst && FFlag::LuauStoreLocalAnnotationColonPositions && cstNode) + if (FFlag::LuauStoreLocalAnnotationColonPositions && cstNode) { LUAU_ASSERT(cstNode->varargAnnotationColonPosition != Position({0, 0})); advance(cstNode->varargAnnotationColonPosition); @@ -2380,23 +1349,15 @@ struct Printer advanceBefore(func.argLocation->end, 1); writer.symbol(")"); - if (writeTypes && FFlag::LuauStoreReturnTypesAsPackOnAst ? func.returnAnnotation != nullptr : func.returnAnnotation_DEPRECATED.has_value()) + if (writeTypes && func.returnAnnotation != nullptr) { if (cstNode) advance(cstNode->returnSpecifierPosition); writer.symbol(":"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - if (!cstNode) - writer.space(); - visualizeTypePackAnnotation(*func.returnAnnotation, false, false); - } - else - { + if (!cstNode) writer.space(); - visualizeTypeList(*func.returnAnnotation_DEPRECATED, false); - } + visualizeTypePackAnnotation(*func.returnAnnotation, false, false); } visualizeBlock(*func.body); @@ -2579,10 +1540,7 @@ struct Printer if (cstNode) advance(cstNode->returnArrowPosition); writer.symbol("->"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visualizeTypePackAnnotation(*a->returnTypes, false); - else - visualizeTypeList(a->returnTypes_DEPRECATED, true); + visualizeTypePackAnnotation(*a->returnTypes, false); } else if (const auto& a = typeAnnotation.as()) { @@ -2883,30 +1841,15 @@ std::string toString(AstNode* node) StringWriter writer; writer.pos = node->location.begin; - if (FFlag::LuauStoreCSTData2) - { - Printer printer(writer, CstNodeMap{nullptr}); - printer.writeTypes = true; + Printer printer(writer, CstNodeMap{nullptr}); + printer.writeTypes = true; - if (auto statNode = node->asStat()) - printer.visualize(*statNode); - else if (auto exprNode = node->asExpr()) - printer.visualize(*exprNode); - else if (auto typeNode = node->asType()) - printer.visualizeTypeAnnotation(*typeNode); - } - else - { - Printer_DEPRECATED printer(writer); - printer.writeTypes = true; - - if (auto statNode = node->asStat()) - printer.visualize(*statNode); - else if (auto exprNode = node->asExpr()) - printer.visualize(*exprNode); - else if (auto typeNode = node->asType()) - printer.visualizeTypeAnnotation(*typeNode); - } + if (auto statNode = node->asStat()) + printer.visualize(*statNode); + else if (auto exprNode = node->asExpr()) + printer.visualize(*exprNode); + else if (auto typeNode = node->asType()) + printer.visualizeTypeAnnotation(*typeNode); return writer.str(); } @@ -2919,38 +1862,21 @@ void dump(AstNode* node) std::string transpile(AstStatBlock& block, const CstNodeMap& cstNodeMap) { StringWriter writer; - if (FFlag::LuauStoreCSTData2) - { - Printer(writer, cstNodeMap).visualizeBlock(block); - } - else - { - Printer_DEPRECATED(writer).visualizeBlock(block); - } + Printer(writer, cstNodeMap).visualizeBlock(block); return writer.str(); } std::string transpileWithTypes(AstStatBlock& block, const CstNodeMap& cstNodeMap) { StringWriter writer; - if (FFlag::LuauStoreCSTData2) - { - Printer printer(writer, cstNodeMap); - printer.writeTypes = true; - printer.visualizeBlock(block); - } - else - { - Printer_DEPRECATED printer(writer); - printer.writeTypes = true; - printer.visualizeBlock(block); - } + Printer printer(writer, cstNodeMap); + printer.writeTypes = true; + printer.visualizeBlock(block); return writer.str(); } std::string transpileWithTypes(AstStatBlock& block) { - // TODO: remove this interface? return transpileWithTypes(block, CstNodeMap{nullptr}); } diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index d49eee09..07ae7ab1 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -30,6 +30,7 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -708,8 +709,11 @@ Property Property::create(std::optional read, std::optional writ } } -TypeId Property::type() const +TypeId Property::type_DEPRECATED() const { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + LUAU_ASSERT(!FFlag::LuauSolverV2); + LUAU_ASSERT(readTy); return *readTy; } @@ -834,7 +838,7 @@ bool areEqual(SeenSet& seen, const TableType& lhs, const TableType& rhs) if (l->first != r->first) return false; - if (FFlag::LuauSolverV2 && FFlag::LuauSubtypingCheckFunctionGenericCounts) + if (FFlag::LuauSolverV2 && (FFlag::LuauSubtypingCheckFunctionGenericCounts || FFlag::LuauRemoveTypeCallsForReadWriteProps)) { if (l->second.readTy && r->second.readTy) { @@ -852,7 +856,7 @@ bool areEqual(SeenSet& seen, const TableType& lhs, const TableType& rhs) else if (l->second.writeTy || r->second.writeTy) return false; } - else if (!areEqual(seen, *l->second.type(), *r->second.type())) + else if (!areEqual(seen, *l->second.type_DEPRECATED(), *r->second.type_DEPRECATED())) return false; ++l; ++r; @@ -1082,7 +1086,18 @@ void persist(TypeId ty) LUAU_ASSERT(ttv->state != TableState::Free && ttv->state != TableState::Unsealed); for (const auto& [_name, prop] : ttv->props) - queue.push_back(prop.type()); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + { + if (prop.readTy) + queue.push_back(*prop.readTy); + if (prop.writeTy) + queue.push_back(*prop.writeTy); + } + else + queue.push_back(prop.type_DEPRECATED()); + } + if (ttv->indexer) { @@ -1093,7 +1108,17 @@ void persist(TypeId ty) else if (auto etv = get(t)) { for (const auto& [_name, prop] : etv->props) - queue.push_back(prop.type()); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + { + if (prop.readTy) + queue.push_back(*prop.readTy); + if (prop.writeTy) + queue.push_back(*prop.writeTy); + } + else + queue.push_back(prop.type_DEPRECATED()); + } } else if (auto utv = get(t)) { diff --git a/Analysis/src/TypeArena.cpp b/Analysis/src/TypeArena.cpp index d4e6604f..cc0b63d5 100644 --- a/Analysis/src/TypeArena.cpp +++ b/Analysis/src/TypeArena.cpp @@ -3,6 +3,7 @@ #include "Luau/TypeArena.h" LUAU_FASTFLAGVARIABLE(DebugLuauFreezeArena); +LUAU_FASTFLAG(LuauTrackTypeAllocations) namespace Luau { @@ -114,6 +115,29 @@ TypePackId TypeArena::addTypePackFunction(const TypePackFunction& function, std: return addTypePack(TypeFunctionInstanceTypePack{NotNull{&function}, std::move(typeArguments), std::move(packArguments)}); } +void TypeArena::recordSingletonStats(const NotNull singleton) +{ + LUAU_ASSERT(FFlag::LuauTrackTypeAllocations); + + auto record = [this](auto&& s) + { + using T = std::decay_t; + + if constexpr (std::is_same_v) + boolSingletonsMinted += 1; + else if constexpr (std::is_same_v) + { + strSingletonsMinted += 1; + if (!s.value.empty()) + uniqueStrSingletonsMinted.insert(s.value); + } + else + static_assert(always_false_v, "Missing allocation count support for this kind of singleton"); + }; + + visit(record, singleton->variant); +} + void freeze(TypeArena& arena) { if (!FFlag::DebugLuauFreezeArena) diff --git a/Analysis/src/TypeAttach.cpp b/Analysis/src/TypeAttach.cpp index 176bdb17..9a31edd5 100644 --- a/Analysis/src/TypeAttach.cpp +++ b/Analysis/src/TypeAttach.cpp @@ -1,6 +1,7 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/TypeAttach.h" +#include "Luau/Ast.h" #include "Luau/Error.h" #include "Luau/Module.h" #include "Luau/RecursionCounter.h" @@ -13,8 +14,7 @@ #include -LUAU_FASTFLAG(LuauStoreCSTData2) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) static char* allocateString(Luau::Allocator& allocator, std::string_view contents) { @@ -197,10 +197,44 @@ public: char* name = allocateString(*allocator, propName); - props.data[idx].name = AstName(name); - props.data[idx].type = Luau::visit(*this, prop.type()->ty); - props.data[idx].location = Location(); - idx++; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + if (prop.isShared()) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::ReadWrite; + props.data[idx].location = Location(); + idx++; + } + else + { + if (prop.readTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::Read; + props.data[idx].location = Location(); + idx++; + } + + if (prop.writeTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty); + props.data[idx].access = AstTableAccess::Write; + props.data[idx].location = Location(); + idx++; + } + } + } + else + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty); + props.data[idx].location = Location(); + idx++; + } } AstTableIndexer* indexer = nullptr; @@ -238,10 +272,44 @@ public: { char* name = allocateString(*allocator, propName); - props.data[idx].name = AstName{name}; - props.data[idx].type = Luau::visit(*this, prop.type()->ty); - props.data[idx].location = Location(); - idx++; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + if (prop.isShared()) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::ReadWrite; + props.data[idx].location = Location(); + idx++; + } + else + { + if (prop.readTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.readTy)->ty); + props.data[idx].access = AstTableAccess::Read; + props.data[idx].location = Location(); + idx++; + } + + if (prop.writeTy) + { + props.data[idx].name = AstName(name); + props.data[idx].type = Luau::visit(*this, (*prop.writeTy)->ty); + props.data[idx].access = AstTableAccess::Write; + props.data[idx].location = Location(); + idx++; + } + } + } + else + { + props.data[idx].name = AstName{name}; + props.data[idx].type = Luau::visit(*this, prop.type_DEPRECATED()->ty); + props.data[idx].location = Location(); + idx++; + } } AstTableIndexer* indexer = nullptr; @@ -308,8 +376,7 @@ public: std::optional* arg = &argNames.data[i++]; if (el) - new (arg) - std::optional(AstArgumentName(AstName(el->name.c_str()), FFlag::LuauStoreCSTData2 ? Location() : el->location)); + new (arg) std::optional(AstArgumentName(AstName(el->name.c_str()), Location())); else new (arg) std::optional(); } @@ -329,19 +396,10 @@ public: if (retTail) retTailAnnotation = rehydrate(*retTail); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - auto returnAnnotation = allocator->alloc(Location(), AstTypeList{returnTypes, retTailAnnotation}); - return allocator->alloc( - Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, returnAnnotation - ); - } - else - { - return allocator->alloc( - Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, AstTypeList{returnTypes, retTailAnnotation} - ); - } + auto returnAnnotation = allocator->alloc(Location(), AstTypeList{returnTypes, retTailAnnotation}); + return allocator->alloc( + Location(), generics, genericPacks, AstTypeList{argTypes, argTailAnnotation}, argNames, returnAnnotation + ); } AstType* operator()(const ErrorType&) { @@ -596,40 +654,19 @@ public: visitLocal(arg); } - if (FFlag::LuauStoreReturnTypesAsPackOnAst) + if (!fn->returnAnnotation) { - if (!fn->returnAnnotation) + if (auto result = getScope(fn->body->location)) { - if (auto result = getScope(fn->body->location)) - { - TypePackId ret = result->returnType; + TypePackId ret = result->returnType; - AstTypePack* variadicAnnotation = nullptr; - const auto& [v, tail] = flatten(ret); + AstTypePack* variadicAnnotation = nullptr; + const auto& [v, tail] = flatten(ret); - if (tail) - variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail); + if (tail) + variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail); - fn->returnAnnotation = allocator->alloc(Location(), AstTypeList{typeAstPack(ret), variadicAnnotation}); - } - } - } - else - { - if (!fn->returnAnnotation_DEPRECATED) - { - if (auto result = getScope(fn->body->location)) - { - TypePackId ret = result->returnType; - - AstTypePack* variadicAnnotation = nullptr; - const auto& [v, tail] = flatten(ret); - - if (tail) - variadicAnnotation = TypeRehydrationVisitor(allocator, &syntheticNames).rehydrate(*tail); - - fn->returnAnnotation_DEPRECATED = AstTypeList{typeAstPack(ret), variadicAnnotation}; - } + fn->returnAnnotation = allocator->alloc(Location(), AstTypeList{typeAstPack(ret), variadicAnnotation}); } } diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 732d3525..08093856 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -31,13 +31,13 @@ LUAU_FASTFLAG(DebugLuauMagicTypes) LUAU_FASTFLAGVARIABLE(LuauTypeCheckerStricterIndexCheck) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauNewNonStrictFixGenericTypePacks) LUAU_FASTFLAGVARIABLE(LuauReportSubtypingErrors) LUAU_FASTFLAGVARIABLE(LuauSkipMalformedTypeAliases) LUAU_FASTFLAGVARIABLE(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) +LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) namespace Luau { @@ -1316,10 +1316,7 @@ void TypeChecker2::visit(AstStatDeclareFunction* stat) { visitGenerics(stat->generics, stat->genericPacks); visit(stat->params); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visit(stat->retTypes); - else - visit(stat->retTypes_DEPRECATED); + visit(stat->retTypes); } void TypeChecker2::visit(AstStatDeclareGlobal* stat) @@ -1573,26 +1570,104 @@ void TypeChecker2::visitCall(AstExprCall* call) argExprs.push_back(indexExpr->expr); } - for (size_t i = 0; i < call->args.size; ++i) + if (FFlag::LuauTableLiteralSubtypeCheckFunctionCalls) { - AstExpr* arg = call->args.data[i]; - argExprs.push_back(arg); - TypeId* argTy = module->astTypes.find(arg); - if (argTy) - args.head.push_back(*argTy); - else if (i == call->args.size - 1) + // FIXME: Similar to bidirectional inference prior, this does not support + // overloaded functions nor generic types (yet). + if (auto fty = get(fnTy); fty && fty->generics.empty() && fty->genericPacks.empty() && call->args.size > 0) { - if (auto argTail = module->astTypePacks.find(arg)) + size_t selfOffset = call->self ? 1 : 0; + + auto [paramsHead, _] = extendTypePack(module->internalTypes, builtinTypes, fty->argTypes, call->args.size + selfOffset); + + for (size_t idx = 0; idx < call->args.size - 1; ++idx) { - auto [head, tail] = flatten(*argTail); - args.head.insert(args.head.end(), head.begin(), head.end()); - args.tail = tail; + auto argExpr = call->args.data[idx]; + auto argExprType = lookupType(argExpr); + argExprs.push_back(argExpr); + if (idx + selfOffset >= paramsHead.size() || isErrorSuppressing(argExpr->location, argExprType)) + { + args.head.push_back(argExprType); + continue; + } + testLiteralOrAstTypeIsSubtype(argExpr, paramsHead[idx + selfOffset]); + args.head.push_back(paramsHead[idx + selfOffset]); + } + + auto lastExpr = call->args.data[call->args.size - 1]; + argExprs.push_back(lastExpr); + + if (auto argTail = module->astTypePacks.find(lastExpr)) + { + auto [lastExprHead, lastExprTail] = flatten(*argTail); + args.head.insert(args.head.end(), lastExprHead.begin(), lastExprHead.end()); + args.tail = lastExprTail; + } + else if (paramsHead.size() >= call->args.size + selfOffset) + { + auto lastType = paramsHead[call->args.size - 1 + selfOffset]; + auto lastExprType = lookupType(lastExpr); + if (isErrorSuppressing(lastExpr->location, lastExprType)) + { + args.head.push_back(lastExprType); + } + else + { + testLiteralOrAstTypeIsSubtype(lastExpr, lastType); + args.head.push_back(lastType); + } } else args.tail = builtinTypes->anyTypePack; } else - args.head.push_back(builtinTypes->anyType); + { + for (size_t i = 0; i < call->args.size; ++i) + { + AstExpr* arg = call->args.data[i]; + argExprs.push_back(arg); + TypeId* argTy = module->astTypes.find(arg); + if (argTy) + args.head.push_back(*argTy); + else if (i == call->args.size - 1) + { + if (auto argTail = module->astTypePacks.find(arg)) + { + auto [head, tail] = flatten(*argTail); + args.head.insert(args.head.end(), head.begin(), head.end()); + args.tail = tail; + } + else + args.tail = builtinTypes->anyTypePack; + } + else + args.head.push_back(builtinTypes->anyType); + } + } + } + else + { + for (size_t i = 0; i < call->args.size; ++i) + { + AstExpr* arg = call->args.data[i]; + argExprs.push_back(arg); + TypeId* argTy = module->astTypes.find(arg); + if (argTy) + args.head.push_back(*argTy); + else if (i == call->args.size - 1) + { + if (auto argTail = module->astTypePacks.find(arg)) + { + auto [head, tail] = flatten(*argTail); + args.head.insert(args.head.end(), head.begin(), head.end()); + args.tail = tail; + } + else + args.tail = builtinTypes->anyTypePack; + } + else + args.head.push_back(builtinTypes->anyType); + } } TypePackId argsTp = module->internalTypes.addTypePack(args); @@ -1964,16 +2039,8 @@ void TypeChecker2::visit(AstExprFunction* fn) visit(fn->body); // we need to typecheck the return annotation itself, if it exists. - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - if (fn->returnAnnotation) - visit(fn->returnAnnotation); - } - else - { - if (fn->returnAnnotation_DEPRECATED) - visit(*fn->returnAnnotation_DEPRECATED); - } + if (fn->returnAnnotation) + visit(fn->returnAnnotation); // If the function type has a function annotation, we need to see if we can suggest an annotation @@ -2751,10 +2818,7 @@ void TypeChecker2::visit(AstTypeFunction* ty) { visitGenerics(ty->generics, ty->genericPacks); visit(ty->argTypes); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - visit(ty->returnTypes); - else - visit(ty->returnTypes_DEPRECATED); + visit(ty->returnTypes); } void TypeChecker2::visit(AstTypeTypeof* ty) diff --git a/Analysis/src/TypeFunction.cpp b/Analysis/src/TypeFunction.cpp index b7b5f52f..f4646737 100644 --- a/Analysis/src/TypeFunction.cpp +++ b/Analysis/src/TypeFunction.cpp @@ -52,11 +52,13 @@ LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(DebugLuauLogTypeFamilies) -LUAU_FASTFLAGVARIABLE(LuauNarrowIntersectionNevers) LUAU_FASTFLAGVARIABLE(LuauNotAllBinaryTypeFunsHaveDefaults) LUAU_FASTFLAG(LuauUserTypeFunctionAliases) LUAU_FASTFLAG(LuauUpdateGetMetatableTypeSignature) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAGVARIABLE(LuauOccursCheckForRefinement) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) +LUAU_FASTFLAGVARIABLE(LuauEmptyStringInKeyOf) namespace Luau { @@ -370,7 +372,7 @@ struct TypeFunctionReducer } if (FFlag::DebugLuauLogTypeFamilies) - printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str()); + printf("%s => %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str()); asMutable(subject)->ty.template emplace>(replacement); @@ -2259,38 +2261,11 @@ struct ContainsRefinableType : TypeOnceVisitor namespace { -bool isApproximateFalsy(TypeId ty) -{ - ty = follow(ty); - bool seenNil = false; - bool seenFalse = false; - if (auto ut = get(ty)) - { - for (auto option : ut) - { - if (auto pt = get(option); pt && pt->type == PrimitiveType::NilType) - seenNil = true; - else if (auto st = get(option); st && st->variant == BooleanSingleton{false}) - seenFalse = true; - else - return false; - } - } - return seenFalse && seenNil; -} -bool isApproximateTruthy(TypeId ty) +bool isTruthyOrFalsyType(TypeId ty) { ty = follow(ty); - if (auto nt = get(ty)) - return isApproximateFalsy(nt->ty); - return false; -} - -bool isSimpleDiscriminant(TypeId ty) -{ - ty = follow(ty); - return isApproximateTruthy(ty) || isApproximateFalsy(ty); + return isApproximatelyTruthyType(ty) || isApproximatelyFalsyType(ty); } struct RefineTypeScrubber : public Substitution @@ -2486,6 +2461,13 @@ TypeFunctionReductionResult refineTypeFunction( if (!crt.found) return {target, {}}; + if (FFlag::LuauRefineTablesWithReadType) + { + if (auto ty = intersectWithSimpleDiscriminant(ctx->builtins, ctx->arena, target, discriminant)) + return {*ty, {}}; + } + + // NOTE: This block causes us to refine too early in some cases. if (auto negation = get(discriminant)) { if (auto primitive = get(follow(negation->ty)); primitive && primitive->type == PrimitiveType::NilType) @@ -2497,12 +2479,8 @@ TypeFunctionReductionResult refineTypeFunction( // If the target type is a table, then simplification already implements the logic to deal with refinements properly since the // type of the discriminant is guaranteed to only ever be an (arbitrarily-nested) table of a single property type. - // We also fire for simple discriminants such as false? and ~(false?): the falsy and truthy types respectively - // NOTE: It would be nice to be able to do a simple intersection for something like: - // - // { a: A, b: B, ... } & { x: X } - // - if (is(target) || isSimpleDiscriminant(discriminant)) + // We also fire for simple discriminants such as false? and ~(false?): the falsy and truthy types respectively. + if (is(target) || isTruthyOrFalsyType(discriminant)) { SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, target, discriminant); if (FFlag::LuauEagerGeneralization4) @@ -2551,6 +2529,7 @@ TypeFunctionReductionResult refineTypeFunction( return {resultTy, {}}; } + }; // refine target with each discriminant type in sequence (reverse of insertion order) @@ -2749,19 +2728,27 @@ TypeFunctionReductionResult intersectTypeFunction( if (get(ty)) continue; - SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty); - - if (FFlag::LuauNarrowIntersectionNevers) + if (FFlag::LuauRefineTablesWithReadType) { - // If simplifying the intersection returned never, note the type we tried to intersect it with, and continue trying to intersect with the - // rest - if (get(result.result)) + if (auto simpleResult = intersectWithSimpleDiscriminant(ctx->builtins, ctx->arena, resultTy, ty)) { - unintersectableTypes.insert(follow(ty)); + if (get(*simpleResult)) + unintersectableTypes.insert(follow(ty)); + else + resultTy = *simpleResult; continue; } } + SimplifyResult result = simplifyIntersection(ctx->builtins, ctx->arena, resultTy, ty); + + // If simplifying the intersection returned never, note the type we tried to intersect it with, and continue trying to intersect with the + // rest + if (get(result.result)) + { + unintersectableTypes.insert(follow(ty)); + continue; + } for (TypeId blockedType : result.blockedTypes) { if (!get(blockedType)) @@ -2771,24 +2758,20 @@ TypeFunctionReductionResult intersectTypeFunction( resultTy = result.result; } - if (FFlag::LuauNarrowIntersectionNevers) + if (!unintersectableTypes.empty()) { - if (!unintersectableTypes.empty()) + unintersectableTypes.insert(resultTy); + if (unintersectableTypes.size() > 1) { - unintersectableTypes.insert(resultTy); - if (unintersectableTypes.size() > 1) - { - TypeId intersection = - ctx->arena->addType(IntersectionType{std::vector(unintersectableTypes.begin(), unintersectableTypes.end())}); - return {intersection, Reduction::MaybeOk, {}, {}}; - } - else - { - return {*unintersectableTypes.begin(), Reduction::MaybeOk, {}, {}}; - } + TypeId intersection = + ctx->arena->addType(IntersectionType{std::vector(unintersectableTypes.begin(), unintersectableTypes.end())}); + return {intersection, Reduction::MaybeOk, {}, {}}; + } + else + { + return {*unintersectableTypes.begin(), Reduction::MaybeOk, {}, {}}; } } - // if the intersection simplifies to `never`, this gives us bad autocomplete. // we'll just produce the intersection plainly instead, but this might be revisitable // if we ever give `never` some kind of "explanation" trail. @@ -2804,7 +2787,7 @@ TypeFunctionReductionResult intersectTypeFunction( // computes the keys of `ty` into `result` // `isRaw` parameter indicates whether or not we should follow __index metamethods // returns `false` if `result` should be ignored because the answer is "all strings" -bool computeKeysOf(TypeId ty, Set& result, DenseHashSet& seen, bool isRaw, NotNull ctx) +bool computeKeysOf_DEPRECATED(TypeId ty, Set& result, DenseHashSet& seen, bool isRaw, NotNull ctx) { // if the type is the top table type, the answer is just "all strings" if (get(ty)) @@ -2830,6 +2813,89 @@ bool computeKeysOf(TypeId ty, Set& result, DenseHashSet& se return true; } + // otherwise, we have a metatable to deal with + if (auto metatableTy = get(ty)) + { + bool res = true; + + if (!isRaw) + { + // findMetatableEntry demands the ability to emit errors, so we must give it + // the necessary state to do that, even if we intend to just eat the errors. + ErrorVec dummy; + + std::optional mmType = findMetatableEntry(ctx->builtins, dummy, ty, "__index", Location{}); + if (mmType) + res = res && computeKeysOf_DEPRECATED(*mmType, result, seen, isRaw, ctx); + } + + res = res && computeKeysOf_DEPRECATED(metatableTy->table, result, seen, isRaw, ctx); + + return res; + } + + if (auto classTy = get(ty)) + { + for (auto [key, _] : classTy->props) // NOLINT(performance-for-range-copy) + result.insert(key); + + bool res = true; + if (classTy->metatable && !isRaw) + { + // findMetatableEntry demands the ability to emit errors, so we must give it + // the necessary state to do that, even if we intend to just eat the errors. + ErrorVec dummy; + + std::optional mmType = findMetatableEntry(ctx->builtins, dummy, ty, "__index", Location{}); + if (mmType) + res = res && computeKeysOf_DEPRECATED(*mmType, result, seen, isRaw, ctx); + } + + if (classTy->parent) + res = res && computeKeysOf_DEPRECATED(follow(*classTy->parent), result, seen, isRaw, ctx); + + return res; + } + + // this should not be reachable since the type should be a valid tables or extern types part from normalization. + LUAU_ASSERT(false); + return false; +} + +namespace { + +/** + * Computes the keys of `ty` into `result` + * `isRaw` parameter indicates whether or not we should follow __index metamethods + * returns `false` if `result` should be ignored because the answer is "all strings" + */ +bool computeKeysOf(TypeId ty, Set>& result, DenseHashSet& seen, bool isRaw, NotNull ctx) +{ + + // if the type is the top table type, the answer is just "all strings" + if (get(ty)) + return false; + + // if we've already seen this type, we can do nothing + if (seen.contains(ty)) + return true; + seen.insert(ty); + + // if we have a particular table type, we can insert the keys + if (auto tableTy = get(ty)) + { + if (tableTy->indexer) + { + // if we have a string indexer, the answer is, again, "all strings" + if (isString(tableTy->indexer->indexType)) + return false; + } + + for (const auto& [key, _] : tableTy->props) + result.insert(key); + return true; + } + // otherwise, we have a metatable to deal with if (auto metatableTy = get(ty)) { @@ -2853,7 +2919,7 @@ bool computeKeysOf(TypeId ty, Set& result, DenseHashSet& se if (auto classTy = get(ty)) { - for (auto [key, _] : classTy->props) + for (const auto& [key, _] : classTy->props) result.insert(key); bool res = true; @@ -2879,6 +2945,8 @@ bool computeKeysOf(TypeId ty, Set& result, DenseHashSet& se return false; } +} + TypeFunctionReductionResult keyofFunctionImpl( const std::vector& typeParams, const std::vector& packParams, @@ -2910,98 +2978,201 @@ TypeFunctionReductionResult keyofFunctionImpl( normTy->hasThreads() || normTy->hasBuffers() || normTy->hasFunctions() || normTy->hasTyvars()) return {std::nullopt, Reduction::Erroneous, {}, {}}; - // we're going to collect the keys in here - Set keys{{}}; - - // computing the keys for extern types - if (normTy->hasExternTypes()) + if (FFlag::LuauEmptyStringInKeyOf) { - LUAU_ASSERT(!normTy->hasTables()); + // We're going to collect the keys in here, and we use optional strings + // so that we can differentiate between the empty string and _no_ string. + Set> keys{std::nullopt}; - // seen set for key computation for extern types - DenseHashSet seen{{}}; - - auto externTypeIter = normTy->externTypes.ordering.begin(); - auto externTypeIterEnd = normTy->externTypes.ordering.end(); - LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier - - // collect all the properties from the first class type - if (!computeKeysOf(*externTypeIter, keys, seen, isRaw, ctx)) - return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type! - - // we need to look at each class to remove any keys that are not common amongst them all - while (++externTypeIter != externTypeIterEnd) + // computing the keys for extern types + if (normTy->hasExternTypes()) { - seen.clear(); // we'll reuse the same seen set + LUAU_ASSERT(!normTy->hasTables()); - Set localKeys{{}}; + // seen set for key computation for extern types + DenseHashSet seen{{}}; - // we can skip to the next class if this one is a top type - if (!computeKeysOf(*externTypeIter, localKeys, seen, isRaw, ctx)) - continue; + auto externTypeIter = normTy->externTypes.ordering.begin(); + auto externTypeIterEnd = normTy->externTypes.ordering.end(); + LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier - for (auto& key : keys) + // collect all the properties from the first class type + if (!computeKeysOf(*externTypeIter, keys, seen, isRaw, ctx)) + return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type! + + // we need to look at each class to remove any keys that are not common amongst them all + while (++externTypeIter != externTypeIterEnd) { - // remove any keys that are not present in each class - if (!localKeys.contains(key)) - keys.erase(key); + seen.clear(); // we'll reuse the same seen set + + Set> localKeys{std::nullopt}; + + // we can skip to the next class if this one is a top type + if (!computeKeysOf(*externTypeIter, localKeys, seen, isRaw, ctx)) + continue; + + for (auto& key : keys) + { + // remove any keys that are not present in each class + if (!localKeys.contains(key)) + keys.erase(key); + } } } - } - // computing the keys for tables - if (normTy->hasTables()) - { - LUAU_ASSERT(!normTy->hasExternTypes()); - - // seen set for key computation for tables - DenseHashSet seen{{}}; - - auto tablesIter = normTy->tables.begin(); - LUAU_ASSERT(tablesIter != normTy->tables.end()); // should be guaranteed by the `hasTables` check earlier - - // collect all the properties from the first table type - if (!computeKeysOf(*tablesIter, keys, seen, isRaw, ctx)) - return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have the top table type! - - // we need to look at each tables to remove any keys that are not common amongst them all - while (++tablesIter != normTy->tables.end()) + // computing the keys for tables + if (normTy->hasTables()) { - seen.clear(); // we'll reuse the same seen set + LUAU_ASSERT(!normTy->hasExternTypes()); - Set localKeys{{}}; + // seen set for key computation for tables + DenseHashSet seen{{}}; - // we can skip to the next table if this one is the top table type - if (!computeKeysOf(*tablesIter, localKeys, seen, isRaw, ctx)) - continue; + auto tablesIter = normTy->tables.begin(); + LUAU_ASSERT(tablesIter != normTy->tables.end()); // should be guaranteed by the `hasTables` check earlier - for (auto& key : keys) + // collect all the properties from the first table type + if (!computeKeysOf(*tablesIter, keys, seen, isRaw, ctx)) + return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have the top table type! + + // we need to look at each tables to remove any keys that are not common amongst them all + while (++tablesIter != normTy->tables.end()) { - // remove any keys that are not present in each table - if (!localKeys.contains(key)) - keys.erase(key); + seen.clear(); // we'll reuse the same seen set + + Set> localKeys{std::nullopt}; + + // we can skip to the next table if this one is the top table type + if (!computeKeysOf(*tablesIter, localKeys, seen, isRaw, ctx)) + continue; + + for (auto& key : keys) + { + // remove any keys that are not present in each table + if (!localKeys.contains(key)) + keys.erase(key); + } } } + + // if the set of keys is empty, `keyof` is `never` + if (keys.empty()) + return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; + + // everything is validated, we need only construct our big union of singletons now! + std::vector singletons; + singletons.reserve(keys.size()); + + for (const auto& key : keys) + { + if (key) + singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{*key}})); + } + + // If there's only one entry, we don't need a UnionType. + // We can take straight take it from the first entry + // because it was added into the type arena already. + if (singletons.size() == 1) + return {singletons.front(), Reduction::MaybeOk, {}, {}}; + + return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}}; } + else + { - // if the set of keys is empty, `keyof` is `never` - if (keys.empty()) - return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; + // we're going to collect the keys in here + Set keys{{}}; - // everything is validated, we need only construct our big union of singletons now! - std::vector singletons; - singletons.reserve(keys.size()); + // computing the keys for extern types + if (normTy->hasExternTypes()) + { + LUAU_ASSERT(!normTy->hasTables()); - for (const std::string& key : keys) - singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}})); + // seen set for key computation for extern types + DenseHashSet seen{{}}; - // If there's only one entry, we don't need a UnionType. - // We can take straight take it from the first entry - // because it was added into the type arena already. - if (singletons.size() == 1) - return {singletons.front(), Reduction::MaybeOk, {}, {}}; + auto externTypeIter = normTy->externTypes.ordering.begin(); + auto externTypeIterEnd = normTy->externTypes.ordering.end(); + LUAU_ASSERT(externTypeIter != externTypeIterEnd); // should be guaranteed by the `hasExternTypes` check earlier - return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}}; + // collect all the properties from the first class type + if (!computeKeysOf_DEPRECATED(*externTypeIter, keys, seen, isRaw, ctx)) + return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have a top type! + + // we need to look at each class to remove any keys that are not common amongst them all + while (++externTypeIter != externTypeIterEnd) + { + seen.clear(); // we'll reuse the same seen set + + Set localKeys{{}}; + + // we can skip to the next class if this one is a top type + if (!computeKeysOf_DEPRECATED(*externTypeIter, localKeys, seen, isRaw, ctx)) + continue; + + for (auto& key : keys) + { + // remove any keys that are not present in each class + if (!localKeys.contains(key)) + keys.erase(key); + } + } + } + + // computing the keys for tables + if (normTy->hasTables()) + { + LUAU_ASSERT(!normTy->hasExternTypes()); + + // seen set for key computation for tables + DenseHashSet seen{{}}; + + auto tablesIter = normTy->tables.begin(); + LUAU_ASSERT(tablesIter != normTy->tables.end()); // should be guaranteed by the `hasTables` check earlier + + // collect all the properties from the first table type + if (!computeKeysOf_DEPRECATED(*tablesIter, keys, seen, isRaw, ctx)) + return {ctx->builtins->stringType, Reduction::MaybeOk, {}, {}}; // if it failed, we have the top table type! + + // we need to look at each tables to remove any keys that are not common amongst them all + while (++tablesIter != normTy->tables.end()) + { + seen.clear(); // we'll reuse the same seen set + + Set localKeys{{}}; + + // we can skip to the next table if this one is the top table type + if (!computeKeysOf_DEPRECATED(*tablesIter, localKeys, seen, isRaw, ctx)) + continue; + + for (auto& key : keys) + { + // remove any keys that are not present in each table + if (!localKeys.contains(key)) + keys.erase(key); + } + } + } + + // if the set of keys is empty, `keyof` is `never` + if (keys.empty()) + return {ctx->builtins->neverType, Reduction::MaybeOk, {}, {}}; + + // everything is validated, we need only construct our big union of singletons now! + std::vector singletons; + singletons.reserve(keys.size()); + + for (const std::string& key : keys) + singletons.push_back(ctx->arena->addType(SingletonType{StringSingleton{key}})); + + // If there's only one entry, we don't need a UnionType. + // We can take straight take it from the first entry + // because it was added into the type arena already. + if (singletons.size() == 1) + return {singletons.front(), Reduction::MaybeOk, {}, {}}; + + return {ctx->arena->addType(UnionType{std::move(singletons)}), Reduction::MaybeOk, {}, {}}; + } } TypeFunctionReductionResult keyofTypeFunction( @@ -3054,7 +3225,21 @@ bool searchPropsAndIndexer( { if (tblProps.find(stringSingleton->value) != tblProps.end()) { - TypeId propTy = follow(tblProps.at(stringSingleton->value).type()); + + TypeId propTy; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + Property& prop = tblProps.at(stringSingleton->value); + + if (prop.readTy) + propTy = follow(*prop.readTy); + else if (prop.writeTy) + propTy = follow(*prop.writeTy); + else // found the property, but there was no type associated with it + return false; + } + else + propTy = follow(tblProps.at(stringSingleton->value).type_DEPRECATED()); // property is a union type -> we need to extend our reduction type if (auto propUnionTy = get(propTy)) diff --git a/Analysis/src/TypeFunctionRuntimeBuilder.cpp b/Analysis/src/TypeFunctionRuntimeBuilder.cpp index 9334e63b..e8f0a93b 100644 --- a/Analysis/src/TypeFunctionRuntimeBuilder.cpp +++ b/Analysis/src/TypeFunctionRuntimeBuilder.cpp @@ -20,6 +20,8 @@ // currently, controls serialization, deserialization, and `type.copy` LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000); +LUAU_FASTFLAGVARIABLE(LuauTypeFunctionSerializeFollowMetatable) + namespace Luau { @@ -388,7 +390,7 @@ private: void serializeChildren(const MetatableType* m1, TypeFunctionTableType* m2) { // Serialize main part of the metatable immediately - if (auto tableTy = get(m1->table)) + if (auto tableTy = get(FFlag::LuauTypeFunctionSerializeFollowMetatable ? follow(m1->table) : m1->table)) serializeChildren(tableTy, m2); m2->metatable = shallowSerialize(m1->metatable); diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 544a6df5..ba09720d 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -32,7 +32,6 @@ LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500) LUAU_FASTFLAG(LuauKnowsTheDataModel3) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification) LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauReduceCheckBinaryExprStackPressure) @@ -1773,7 +1772,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareExtern else { Luau::Property& prop = assignTo[propName]; - TypeId currentTy = prop.type(); + TypeId currentTy = prop.type_DEPRECATED(); // We special-case this logic to keep the intersection flat; otherwise we // would create a ton of nested intersection types. @@ -1834,8 +1833,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti ); TypePackId argPack = resolveTypePack(funScope, global.params); - TypePackId retPack = - FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funScope, *global.retTypes) : resolveTypePack(funScope, global.retTypes_DEPRECATED); + TypePackId retPack = resolveTypePack(funScope, *global.retTypes); FunctionDefinition defn; @@ -2096,7 +2094,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( if (TableType* tableType = getMutableTableType(type)) { if (auto it = tableType->props.find(name); it != tableType->props.end()) - return it->second.type(); + return it->second.type_DEPRECATED(); else if (auto indexer = tableType->indexer) { // TODO: Property lookup should work with string singletons or unions thereof as the indexer key type. @@ -2124,7 +2122,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( { const Property* prop = lookupExternTypeProp(cls, name); if (prop) - return prop->type(); + return prop->type_DEPRECATED(); if (auto indexer = cls->indexer) { @@ -2332,9 +2330,9 @@ TypeId TypeChecker::checkExprTable( if (it != expectedTable->props.end()) { Property expectedProp = it->second; - ErrorVec errors = tryUnify(exprType, expectedProp.type(), scope, k->location); + ErrorVec errors = tryUnify(exprType, expectedProp.type_DEPRECATED(), scope, k->location); if (errors.empty()) - exprType = expectedProp.type(); + exprType = expectedProp.type_DEPRECATED(); } else if (expectedTable->indexer && maybeString(expectedTable->indexer->indexType)) { @@ -2428,7 +2426,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp if (expectedTable) { if (auto prop = expectedTable->props.find(key->value.data); prop != expectedTable->props.end()) - expectedResultType = prop->second.type(); + expectedResultType = prop->second.type_DEPRECATED(); else if (expectedIndexType && maybeString(*expectedIndexType)) expectedResultType = expectedIndexResultType; } @@ -2440,7 +2438,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp if (const TableType* ttv = get(follow(expectedOption))) { if (auto prop = ttv->props.find(key->value.data); prop != ttv->props.end()) - expectedResultTypes.push_back(prop->second.type()); + expectedResultTypes.push_back(prop->second.type_DEPRECATED()); else if (ttv->indexer && maybeString(ttv->indexer->indexType)) expectedResultTypes.push_back(ttv->indexer->indexResultType); } @@ -3408,7 +3406,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex const auto& it = lhsTable->props.find(name); if (it != lhsTable->props.end()) { - return it->second.type(); + return it->second.type_DEPRECATED(); } else if ((ctx == ValueContext::LValue && lhsTable->state == TableState::Unsealed) || lhsTable->state == TableState::Free) { @@ -3449,7 +3447,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex { if (const Property* prop = lookupExternTypeProp(lhsExternType, name)) { - return prop->type(); + return prop->type_DEPRECATED(); } if (auto indexer = lhsExternType->indexer) @@ -3508,7 +3506,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex { if (const Property* prop = lookupExternTypeProp(exprExternType, value->value.data)) { - return prop->type(); + return prop->type_DEPRECATED(); } if (auto indexer = exprExternType->indexer) @@ -3618,7 +3616,7 @@ TypeId TypeChecker::checkLValueBinding(const ScopePtr& scope, const AstExprIndex const auto& it = table->props.find(value->value.data); if (it != table->props.end()) { - propTypes.insert(it->second.type()); + propTypes.insert(it->second.type_DEPRECATED()); } else if ((ctx == ValueContext::LValue && table->state == TableState::Unsealed) || table->state == TableState::Free) { @@ -3759,12 +3757,12 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T Name name = indexName->index.value; if (ttv->props.count(name)) - return ttv->props[name].type(); + return ttv->props[name].type_DEPRECATED(); Property& property = ttv->props[name]; property.setType(freshTy()); property.location = indexName->indexLocation; - return property.type(); + return property.type_DEPRECATED(); } else if (funName.is()) return errorRecoveryType(scope); @@ -3831,10 +3829,8 @@ std::pair TypeChecker::checkFunctionSignature( auto [generics, genericPacks] = createGenericTypes(funScope, std::nullopt, expr, expr.generics, expr.genericPacks); TypePackId retPack; - if (FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation) + if (expr.returnAnnotation) retPack = resolveTypePack(funScope, *expr.returnAnnotation); - else if (!FFlag::LuauStoreReturnTypesAsPackOnAst && expr.returnAnnotation_DEPRECATED) - retPack = resolveTypePack(funScope, *expr.returnAnnotation_DEPRECATED); else if (isNonstrictMode()) retPack = anyTypePack; else if (expectedFunctionType && expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty()) @@ -4041,8 +4037,7 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE // If we're in nonstrict mode we want to only report this missing return // statement if there are type annotations on the function. In strict mode // we report it regardless. - if (!isNonstrictMode() || - (FFlag::LuauStoreReturnTypesAsPackOnAst ? function.returnAnnotation != nullptr : function.returnAnnotation_DEPRECATED.has_value())) + if (!isNonstrictMode() || function.returnAnnotation != nullptr) { reportError(getEndLocation(function), FunctionExitsWithoutReturning{funTy->retTypes}); } @@ -5785,8 +5780,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno auto [generics, genericPacks] = createGenericTypes(funcScope, std::nullopt, annotation, func->generics, func->genericPacks); TypePackId argTypes = resolveTypePack(funcScope, func->argTypes); - TypePackId retTypes = FFlag::LuauStoreReturnTypesAsPackOnAst ? resolveTypePack(funcScope, *func->returnTypes) - : resolveTypePack(funcScope, func->returnTypes_DEPRECATED); + TypePackId retTypes = resolveTypePack(funcScope, *func->returnTypes); std::vector genericTys; genericTys.reserve(generics.size()); diff --git a/Analysis/src/TypePath.cpp b/Analysis/src/TypePath.cpp index 95b8da05..bc268535 100644 --- a/Analysis/src/TypePath.cpp +++ b/Analysis/src/TypePath.cpp @@ -344,7 +344,7 @@ struct TraversalState if (FFlag::LuauSolverV2) maybeType = property.isRead ? prop->readTy : prop->writeTy; else - maybeType = prop->type(); + maybeType = prop->type_DEPRECATED(); if (maybeType) { diff --git a/Analysis/src/TypeUtils.cpp b/Analysis/src/TypeUtils.cpp index 1fcca83b..1ca0971d 100644 --- a/Analysis/src/TypeUtils.cpp +++ b/Analysis/src/TypeUtils.cpp @@ -14,6 +14,7 @@ LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAGVARIABLE(LuauErrorSuppressionTypeFunctionArgs) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) namespace Luau { @@ -75,7 +76,17 @@ std::optional findTableProperty(NotNull builtinTypes, Er { const auto& fit = itt->props.find(name); if (fit != itt->props.end()) - return fit->second.type(); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + { + if (fit->second.readTy) + return fit->second.readTy; + else + return fit->second.writeTy; + } + else + return fit->second.type_DEPRECATED(); + } } else if (const auto& itf = get(index)) { @@ -124,7 +135,17 @@ std::optional findMetatableEntry( auto it = mtt->props.find(entry); if (it != mtt->props.end()) - return it->second.type(); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + { + if (it->second.readTy) + return it->second.readTy; + else + return it->second.writeTy; + } + else + return it->second.type_DEPRECATED(); + } else return std::nullopt; } @@ -168,7 +189,7 @@ std::optional findTablePropertyRespectingMeta( } } else - return it->second.type(); + return it->second.type_DEPRECATED(); } } @@ -187,7 +208,20 @@ std::optional findTablePropertyRespectingMeta( { const auto& fit = itt->props.find(name); if (fit != itt->props.end()) - return fit->second.type(); + { + if (FFlag::LuauRemoveTypeCallsForReadWriteProps && FFlag::LuauSolverV2) + { + switch (context) + { + case ValueContext::RValue: + return fit->second.readTy; + case ValueContext::LValue: + return fit->second.writeTy; + } + } + else + return fit->second.type_DEPRECATED(); + } } else if (const auto& itf = get(index)) { @@ -703,6 +737,34 @@ AstExpr* unwrapGroup(AstExpr* expr) return expr; } +bool isApproximatelyFalsyType(TypeId ty) +{ + ty = follow(ty); + bool seenNil = false; + bool seenFalse = false; + if (auto ut = get(ty)) + { + for (auto option : ut) + { + if (auto pt = get(option); pt && pt->type == PrimitiveType::NilType) + seenNil = true; + else if (auto st = get(option); st && st->variant == BooleanSingleton{false}) + seenFalse = true; + else + return false; + } + } + return seenFalse && seenNil; +} + +bool isApproximatelyTruthyType(TypeId ty) +{ + ty = follow(ty); + if (auto nt = get(ty)) + return isApproximatelyFalsyType(nt->ty); + return false; +} + } // namespace Luau diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 36d5765c..096974ec 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -342,7 +342,7 @@ static std::optional> getTableMatchT { for (auto&& [name, prop] : ttv->props) { - if (auto sing = get(follow(prop.type()))) + if (auto sing = get(follow(prop.type_DEPRECATED()))) return {{name, sing}}; } } @@ -1938,7 +1938,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, { auto subIter = subTable->props.find(propName); - if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type())) + if (subIter == subTable->props.end() && subTable->state == TableState::Unsealed && !isOptional(superProp.type_DEPRECATED())) missingProperties.push_back(propName); } @@ -1980,7 +1980,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, variance = Invariant; std::unique_ptr innerState = makeChildUnifier(); - innerState->tryUnify_(r->second.type(), prop.type()); + innerState->tryUnify_(r->second.type_DEPRECATED(), prop.type_DEPRECATED()); checkChildUnifierTypeMismatch(innerState->errors, name, superTy, subTy); @@ -1997,7 +1997,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, variance = Invariant; std::unique_ptr innerState = makeChildUnifier(); - innerState->tryUnify_(subTable->indexer->indexResultType, prop.type()); + innerState->tryUnify_(subTable->indexer->indexResultType, prop.type_DEPRECATED()); checkChildUnifierTypeMismatch(innerState->errors, name, superTy, subTy); @@ -2005,7 +2005,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, log.concat(std::move(innerState->log)); failure |= innerState->failure; } - else if (subTable->state == TableState::Unsealed && isOptional(prop.type())) + else if (subTable->state == TableState::Unsealed && isOptional(prop.type_DEPRECATED())) // This is sound because unsealed table types are precise, so `{ p : T } <: { p : T, q : U? }` // since if `t : { p : T }` then we are guaranteed that `t.q` is `nil`. // TODO: if the supertype is written to, the subtype may no longer be precise (alias analysis?) @@ -2076,11 +2076,11 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, std::unique_ptr innerState = makeChildUnifier(); if (FFlag::LuauFixIndexerSubtypingOrdering) - innerState->tryUnify_(prop.type(), superTable->indexer->indexResultType); + innerState->tryUnify_(prop.type_DEPRECATED(), superTable->indexer->indexResultType); else { // Incredibly, the old solver depends on this bug somehow. - innerState->tryUnify_(superTable->indexer->indexResultType, prop.type()); + innerState->tryUnify_(superTable->indexer->indexResultType, prop.type_DEPRECATED()); } checkChildUnifierTypeMismatch(innerState->errors, name, superTy, subTy); @@ -2095,7 +2095,7 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, // TODO: file a JIRA // TODO: hopefully readonly/writeonly properties will fix this. Property clone = prop; - clone.setType(deeplyOptional(clone.type())); + clone.setType(deeplyOptional(clone.type_DEPRECATED())); PendingType* pendingSuper = log.queue(superTy); TableType* pendingSuperTtv = getMutable(pendingSuper); @@ -2271,7 +2271,7 @@ void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed) if (auto it = mttv->props.find("__index"); it != mttv->props.end()) { - TypeId ty = it->second.type(); + TypeId ty = it->second.type_DEPRECATED(); std::unique_ptr child = makeChildUnifier(); child->tryUnify_(ty, superTy); @@ -2323,7 +2323,7 @@ TypeId Unifier::deeplyOptional(TypeId ty, std::unordered_map see result = types->addType(*ttv); TableType* resultTtv = getMutable(result); for (auto& [name, prop] : resultTtv->props) - prop.setType(deeplyOptional(prop.type(), seen)); + prop.setType(deeplyOptional(prop.type_DEPRECATED(), seen)); return types->addType(UnionType{{builtinTypes->nilType, result}}); } else @@ -2442,7 +2442,7 @@ void Unifier::tryUnifyWithExternType(TypeId subTy, TypeId superTy, bool reversed else { std::unique_ptr innerState = makeChildUnifier(); - innerState->tryUnify_(classProp->type(), prop.type()); + innerState->tryUnify_(classProp->type_DEPRECATED(), prop.type_DEPRECATED()); checkChildUnifierTypeMismatch(innerState->errors, propName, reversed ? subTy : superTy, reversed ? superTy : subTy); @@ -2621,7 +2621,7 @@ static void tryUnifyWithAny( else if (auto table = state.log.getMutable(ty)) { for (const auto& [_name, prop] : table->props) - queue.push_back(prop.type()); + queue.push_back(prop.type_DEPRECATED()); if (table->indexer) { diff --git a/Analysis/src/Unifier2.cpp b/Analysis/src/Unifier2.cpp index af1098a2..0d9391c9 100644 --- a/Analysis/src/Unifier2.cpp +++ b/Analysis/src/Unifier2.cpp @@ -20,10 +20,18 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) namespace Luau { +static bool isOptionalOrFree(TypeId ty) +{ + ty = follow(ty); + return isOptional(ty) || (get(ty) != nullptr); +} + static bool areCompatible(TypeId left, TypeId right) { auto p = get2(follow(left), follow(right)); @@ -43,14 +51,32 @@ static bool areCompatible(TypeId left, TypeId right) // the right table is free (and therefore potentially has an indexer or // a compatible property) - LUAU_ASSERT(leftProp.isReadOnly() || leftProp.isShared()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps || FFlag::LuauRefineTablesWithReadType) + { + if (rightTable->state == TableState::Free || rightTable->indexer.has_value()) + return true; - const TypeId leftType = follow(leftProp.isReadOnly() ? *leftProp.readTy : leftProp.type()); + if (leftProp.isReadOnly() || leftProp.isShared()) + { + if (isOptionalOrFree(*leftProp.readTy)) + return true; + } - if (isOptional(leftType) || get(leftType) || rightTable->state == TableState::Free || rightTable->indexer.has_value()) - return true; + // FIXME: Could this create an issue for write only / divergent properties? + return false; + } + else + { + LUAU_ASSERT(leftProp.isReadOnly() || leftProp.isShared()); + + const TypeId leftType = follow(leftProp.isReadOnly() ? *leftProp.readTy : leftProp.type_DEPRECATED()); + + if (isOptional(leftType) || get(leftType) || rightTable->state == TableState::Free || rightTable->indexer.has_value()) + return true; + + return false; + } - return false; }; for (const auto& [name, leftProp] : leftTable->props) @@ -426,13 +452,13 @@ bool Unifier2::unify(TableType* subTable, const TableType* superTable) if (subProp.isReadOnly() && superProp.isReadOnly()) result &= unify(*subProp.readTy, *superPropOpt->second.readTy); else if (subProp.isReadOnly()) - result &= unify(*subProp.readTy, superProp.type()); + result &= unify(*subProp.readTy, superProp.type_DEPRECATED()); else if (superProp.isReadOnly()) - result &= unify(subProp.type(), *superProp.readTy); + result &= unify(subProp.type_DEPRECATED(), *superProp.readTy); else { - result &= unify(subProp.type(), superProp.type()); - result &= unify(superProp.type(), subProp.type()); + result &= unify(subProp.type_DEPRECATED(), superProp.type_DEPRECATED()); + result &= unify(superProp.type_DEPRECATED(), subProp.type_DEPRECATED()); } } } diff --git a/Ast/include/Luau/Ast.h b/Ast/include/Luau/Ast.h index 2e7ca6b7..0b6f12de 100644 --- a/Ast/include/Luau/Ast.h +++ b/Ast/include/Luau/Ast.h @@ -451,24 +451,6 @@ public: const std::optional& argLocation = std::nullopt ); - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstExprFunction( - const Location& location, - const AstArray& attributes, - const AstArray& generics, - const AstArray& genericPacks, - AstLocal* self, - const AstArray& args, - bool vararg, - const Location& varargLocation, - AstStatBlock* body, - size_t functionDepth, - const AstName& debugname, - const std::optional& returnAnnotation, - AstTypePack* varargAnnotation = nullptr, - const std::optional& argLocation = std::nullopt - ); - void visit(AstVisitor* visitor) override; bool hasNativeAttribute() const; @@ -479,8 +461,6 @@ public: AstArray genericPacks; AstLocal* self; AstArray args; - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - std::optional returnAnnotation_DEPRECATED; AstTypePack* returnAnnotation = nullptr; bool vararg = false; Location varargLocation; @@ -949,7 +929,6 @@ class AstStatDeclareFunction : public AstStat public: LUAU_RTTI(AstStatDeclareFunction) - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst AstStatDeclareFunction( const Location& location, const AstName& name, @@ -963,7 +942,6 @@ public: AstTypePack* retTypes ); - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst AstStatDeclareFunction( const Location& location, const AstArray& attributes, @@ -978,36 +956,6 @@ public: AstTypePack* retTypes ); - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstStatDeclareFunction( - const Location& location, - const AstName& name, - const Location& nameLocation, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& params, - const AstArray& paramNames, - bool vararg, - const Location& varargLocation, - const AstTypeList& retTypes - ); - - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstStatDeclareFunction( - const Location& location, - const AstArray& attributes, - const AstName& name, - const Location& nameLocation, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& params, - const AstArray& paramNames, - bool vararg, - const Location& varargLocation, - const AstTypeList& retTypes - ); - - void visit(AstVisitor* visitor) override; bool isCheckedFunction() const; @@ -1023,8 +971,6 @@ public: bool vararg = false; Location varargLocation; AstTypePack* retTypes; - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstTypeList retTypes_DEPRECATED; }; struct AstDeclaredExternTypeProperty @@ -1167,27 +1113,6 @@ public: AstTypePack* returnTypes ); - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstTypeFunction( - const Location& location, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& argTypes, - const AstArray>& argNames, - const AstTypeList& returnTypes - ); - - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstTypeFunction( - const Location& location, - const AstArray& attributes, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& argTypes, - const AstArray>& argNames, - const AstTypeList& returnTypes - ); - void visit(AstVisitor* visitor) override; bool isCheckedFunction() const; @@ -1198,8 +1123,6 @@ public: AstArray genericPacks; AstTypeList argTypes; AstArray> argNames; - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - AstTypeList returnTypes_DEPRECATED; AstTypePack* returnTypes; }; diff --git a/Ast/include/Luau/ParseResult.h b/Ast/include/Luau/ParseResult.h index 3b9993d6..40855124 100644 --- a/Ast/include/Luau/ParseResult.h +++ b/Ast/include/Luau/ParseResult.h @@ -15,7 +15,7 @@ class CstNode; class ParseError : public std::exception { public: - ParseError(const Location& location, const std::string& message); + ParseError(const Location& location, std::string message); virtual const char* what() const throw(); diff --git a/Ast/include/Luau/Parser.h b/Ast/include/Luau/Parser.h index cffb304a..1c277cf8 100644 --- a/Ast/include/Luau/Parser.h +++ b/Ast/include/Luau/Parser.h @@ -183,14 +183,6 @@ private: const Name* localName, const AstArray& attributes ); - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - std::pair parseFunctionBody_DEPRECATED( - bool hasself, - const Lexeme& matchFunction, - const AstName& debugname, - const Name* localName, - const AstArray& attributes - ); // explist ::= {exp `,'} exp void parseExprList(TempVector& result, TempVector* commaPositions = nullptr); @@ -233,10 +225,6 @@ private: AstTypePack* parseOptionalReturnType(Position* returnSpecifierPosition = nullptr); AstTypePack* parseReturnType(); - // Clip with FFlagLuauStoreReturnTypesAsPackOnAst - std::optional parseOptionalReturnType_DEPRECATED(Position* returnSpecifierPosition = nullptr); - std::pair parseReturnType_DEPRECATED(); - struct TableIndexerResult { AstTableIndexer* node; @@ -246,8 +234,6 @@ private: }; TableIndexerResult parseTableIndexer(AstTableAccess access, std::optional accessLocation, Lexeme begin); - // Remove with FFlagLuauStoreCSTData2 - AstTableIndexer* parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional accessLocation, Lexeme begin); AstTypeOrPack parseFunctionType(bool allowPack, const AstArray& attributes); AstType* parseFunctionTypeTail( diff --git a/Ast/src/Ast.cpp b/Ast/src/Ast.cpp index 9b081dcc..da825d02 100644 --- a/Ast/src/Ast.cpp +++ b/Ast/src/Ast.cpp @@ -3,8 +3,6 @@ #include "Luau/Common.h" -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - namespace Luau { @@ -258,41 +256,6 @@ AstExprFunction::AstExprFunction( , debugname(debugname) , argLocation(argLocation) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); -} - -AstExprFunction::AstExprFunction( - const Location& location, - const AstArray& attributes, - const AstArray& generics, - const AstArray& genericPacks, - AstLocal* self, - const AstArray& args, - bool vararg, - const Location& varargLocation, - AstStatBlock* body, - size_t functionDepth, - const AstName& debugname, - const std::optional& returnAnnotation, - AstTypePack* varargAnnotation, - const std::optional& argLocation -) - : AstExpr(ClassIndex(), location) - , attributes(attributes) - , generics(generics) - , genericPacks(genericPacks) - , self(self) - , args(args) - , returnAnnotation_DEPRECATED(returnAnnotation) - , vararg(vararg) - , varargLocation(varargLocation) - , varargAnnotation(varargAnnotation) - , body(body) - , functionDepth(functionDepth) - , debugname(debugname) - , argLocation(argLocation) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); } void AstExprFunction::visit(AstVisitor* visitor) @@ -308,16 +271,8 @@ void AstExprFunction::visit(AstVisitor* visitor) if (varargAnnotation) varargAnnotation->visit(visitor); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - if (returnAnnotation) - returnAnnotation->visit(visitor); - } - else - { - if (returnAnnotation_DEPRECATED) - visitTypeList(visitor, *returnAnnotation_DEPRECATED); - } + if (returnAnnotation) + returnAnnotation->visit(visitor); body->visit(visitor); } @@ -908,7 +863,6 @@ AstStatDeclareFunction::AstStatDeclareFunction( , varargLocation(varargLocation) , retTypes(retTypes) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); } AstStatDeclareFunction::AstStatDeclareFunction( @@ -936,64 +890,6 @@ AstStatDeclareFunction::AstStatDeclareFunction( , varargLocation(varargLocation) , retTypes(retTypes) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); -} - -// Clip with FFlagLuauStoreReturnTypesAsPackOnAst -AstStatDeclareFunction::AstStatDeclareFunction( - const Location& location, - const AstName& name, - const Location& nameLocation, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& params, - const AstArray& paramNames, - bool vararg, - const Location& varargLocation, - const AstTypeList& retTypes -) - : AstStat(ClassIndex(), location) - , attributes() - , name(name) - , nameLocation(nameLocation) - , generics(generics) - , genericPacks(genericPacks) - , params(params) - , paramNames(paramNames) - , vararg(vararg) - , varargLocation(varargLocation) - , retTypes_DEPRECATED(retTypes) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); -} - -// Clip with FFlagLuauStoreReturnTypesAsPackOnAst -AstStatDeclareFunction::AstStatDeclareFunction( - const Location& location, - const AstArray& attributes, - const AstName& name, - const Location& nameLocation, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& params, - const AstArray& paramNames, - bool vararg, - const Location& varargLocation, - const AstTypeList& retTypes -) - : AstStat(ClassIndex(), location) - , attributes(attributes) - , name(name) - , nameLocation(nameLocation) - , generics(generics) - , genericPacks(genericPacks) - , params(params) - , paramNames(paramNames) - , vararg(vararg) - , varargLocation(varargLocation) - , retTypes_DEPRECATED(retTypes) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); } void AstStatDeclareFunction::visit(AstVisitor* visitor) @@ -1001,10 +897,7 @@ void AstStatDeclareFunction::visit(AstVisitor* visitor) if (visitor->visit(this)) { visitTypeList(visitor, params); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - retTypes->visit(visitor); - else - visitTypeList(visitor, retTypes_DEPRECATED); + retTypes->visit(visitor); } } @@ -1144,7 +1037,6 @@ AstTypeFunction::AstTypeFunction( , argNames(argNames) , returnTypes(returnTypes) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size); } @@ -1165,50 +1057,6 @@ AstTypeFunction::AstTypeFunction( , argNames(argNames) , returnTypes(returnTypes) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); - LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size); -} - -// Clip with FFlagLuauStoreReturnTypesAsPackOnAst -AstTypeFunction::AstTypeFunction( - const Location& location, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& argTypes, - const AstArray>& argNames, - const AstTypeList& returnTypes -) - : AstType(ClassIndex(), location) - , attributes() - , generics(generics) - , genericPacks(genericPacks) - , argTypes(argTypes) - , argNames(argNames) - , returnTypes_DEPRECATED(returnTypes) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); - LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size); -} - -// Clip with FFlagLuauStoreReturnTypesAsPackOnAst -AstTypeFunction::AstTypeFunction( - const Location& location, - const AstArray& attributes, - const AstArray& generics, - const AstArray& genericPacks, - const AstTypeList& argTypes, - const AstArray>& argNames, - const AstTypeList& returnTypes -) - : AstType(ClassIndex(), location) - , attributes(attributes) - , generics(generics) - , genericPacks(genericPacks) - , argTypes(argTypes) - , argNames(argNames) - , returnTypes_DEPRECATED(returnTypes) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); LUAU_ASSERT(argNames.size == 0 || argNames.size == argTypes.types.size); } @@ -1217,10 +1065,7 @@ void AstTypeFunction::visit(AstVisitor* visitor) if (visitor->visit(this)) { visitTypeList(visitor, argTypes); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - returnTypes->visit(visitor); - else - visitTypeList(visitor, returnTypes_DEPRECATED); + returnTypes->visit(visitor); } } diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index fcc4d496..70388d99 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -18,10 +18,8 @@ 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(LuauStoreCSTData2) LUAU_FASTFLAGVARIABLE(LuauDeclareExternType) LUAU_FASTFLAGVARIABLE(LuauParseStringIndexer) -LUAU_FASTFLAGVARIABLE(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauStoreLocalAnnotationColonPositions) LUAU_FASTFLAGVARIABLE(LuauCSTForReturnTypeFunctionTail) LUAU_FASTFLAGVARIABLE(LuauParseAttributeFixUninit) @@ -46,9 +44,9 @@ AttributeEntry kAttributeEntries[] = { {nullptr, AstAttr::Type::Checked} }; -ParseError::ParseError(const Location& location, const std::string& message) +ParseError::ParseError(const Location& location, std::string message) : location(location) - , message(message) + , message(std::move(message)) { } @@ -525,17 +523,10 @@ AstStat* Parser::parseRepeat() restoreLocals(localsBegin); - if (FFlag::LuauStoreCSTData2) - { - AstStatRepeat* node = allocator.alloc(Location(start, cond->location), cond, body, hasUntil); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(untilPosition); - return node; - } - else - { - return allocator.alloc(Location(start, cond->location), cond, body, hasUntil); - } + AstStatRepeat* node = allocator.alloc(Location(start, cond->location), cond, body, hasUntil); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(untilPosition); + return node; } // do block end @@ -555,7 +546,7 @@ AstStat* Parser::parseDo() if (body->hasEnd) body->location.end = endLocation.end; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstNodeMap[body] = allocator.alloc(endLocation.begin); return body; @@ -638,18 +629,11 @@ AstStat* Parser::parseFor() bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo); body->hasEnd = hasEnd; - if (FFlag::LuauStoreCSTData2) - { - AstStatFor* node = allocator.alloc(Location(start, end), var, from, to, step, body, hasDo, matchDo.location); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(varname.colonPosition, equalsPosition, endCommaPosition, stepCommaPosition); + AstStatFor* node = allocator.alloc(Location(start, end), var, from, to, step, body, hasDo, matchDo.location); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(varname.colonPosition, equalsPosition, endCommaPosition, stepCommaPosition); - return node; - } - else - { - return allocator.alloc(Location(start, end), var, from, to, step, body, hasDo, matchDo.location); - } + return node; } else { @@ -659,7 +643,7 @@ AstStat* Parser::parseFor() if (lexer.current().type == ',') { - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) { Position initialCommaPosition = lexer.current().location.begin; nextLexeme(); @@ -678,7 +662,7 @@ AstStat* Parser::parseFor() TempVector values(scratchExpr); TempVector valuesCommaPositions(scratchPosition); - parseExprList(values, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &valuesCommaPositions : nullptr); + parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr); Lexeme matchDo = lexer.current(); bool hasDo = expectAndConsume(Lexeme::ReservedDo, "for loop"); @@ -703,24 +687,17 @@ AstStat* Parser::parseFor() bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchDo); body->hasEnd = hasEnd; - if (FFlag::LuauStoreCSTData2) + AstStatForIn* node = + allocator.alloc(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location); + if (options.storeCstData) { - AstStatForIn* node = - allocator.alloc(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location); - if (options.storeCstData) - { - if (FFlag::LuauStoreLocalAnnotationColonPositions) - cstNodeMap[node] = - allocator.alloc(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions)); - else - cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPosition, copy(valuesCommaPositions)); - } - return node; - } - else - { - return allocator.alloc(Location(start, end), copy(vars), copy(values), body, hasIn, inLocation, hasDo, matchDo.location); + if (FFlag::LuauStoreLocalAnnotationColonPositions) + cstNodeMap[node] = + allocator.alloc(extractAnnotationColonPositions(names), varsCommaPosition, copy(valuesCommaPositions)); + else + cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPosition, copy(valuesCommaPositions)); } + return node; } } @@ -789,23 +766,14 @@ AstStat* Parser::parseFunctionStat(const AstArray& attributes) matchRecoveryStopOnToken[Lexeme::ReservedEnd]++; - AstExprFunction* body = FFlag::LuauStoreReturnTypesAsPackOnAst - ? parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first - : parseFunctionBody_DEPRECATED(hasself, matchFunction, debugname, nullptr, attributes).first; + AstExprFunction* body = parseFunctionBody(hasself, matchFunction, debugname, nullptr, attributes).first; matchRecoveryStopOnToken[Lexeme::ReservedEnd]--; - if (FFlag::LuauStoreCSTData2) - { - AstStatFunction* node = allocator.alloc(Location(start, body->location), expr, body); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(matchFunction.location.begin); - return node; - } - else - { - return allocator.alloc(Location(start, body->location), expr, body); - } + AstStatFunction* node = allocator.alloc(Location(start, body->location), expr, body); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(matchFunction.location.begin); + return node; } @@ -992,24 +960,16 @@ AstStat* Parser::parseLocal(const AstArray& attributes) matchRecoveryStopOnToken[Lexeme::ReservedEnd]++; - auto [body, var] = FFlag::LuauStoreReturnTypesAsPackOnAst ? parseFunctionBody(false, matchFunction, name.name, &name, attributes) - : parseFunctionBody_DEPRECATED(false, matchFunction, name.name, &name, attributes); + auto [body, var] = parseFunctionBody(false, matchFunction, name.name, &name, attributes); matchRecoveryStopOnToken[Lexeme::ReservedEnd]--; Location location{start.begin, body->location.end}; - if (FFlag::LuauStoreCSTData2) - { - AstStatLocalFunction* node = allocator.alloc(location, var, body); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(localKeywordPosition, functionKeywordPosition); - return node; - } - else - { - return allocator.alloc(location, var, body); - } + AstStatLocalFunction* node = allocator.alloc(location, var, body); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(localKeywordPosition, functionKeywordPosition); + return node; } else { @@ -1028,7 +988,7 @@ AstStat* Parser::parseLocal(const AstArray& attributes) TempVector names(scratchBinding); AstArray varsCommaPositions; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) parseBindingList(names, false, &varsCommaPositions); else parseBindingList(names); @@ -1048,7 +1008,7 @@ AstStat* Parser::parseLocal(const AstArray& attributes) nextLexeme(); - parseExprList(values, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &valuesCommaPositions : nullptr); + parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr); } for (size_t i = 0; i < names.size(); ++i) @@ -1056,24 +1016,17 @@ AstStat* Parser::parseLocal(const AstArray& attributes) Location end = values.empty() ? lexer.previousLocation() : values.back()->location; - if (FFlag::LuauStoreCSTData2) + AstStatLocal* node = allocator.alloc(Location(start, end), copy(vars), copy(values), equalsSignLocation); + if (options.storeCstData) { - AstStatLocal* node = allocator.alloc(Location(start, end), copy(vars), copy(values), equalsSignLocation); - if (options.storeCstData) - { - if (FFlag::LuauStoreLocalAnnotationColonPositions) - cstNodeMap[node] = - allocator.alloc(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions)); - else - cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPositions, copy(valuesCommaPositions)); - } + if (FFlag::LuauStoreLocalAnnotationColonPositions) + cstNodeMap[node] = + allocator.alloc(extractAnnotationColonPositions(names), varsCommaPositions, copy(valuesCommaPositions)); + else + cstNodeMap[node] = allocator.alloc(AstArray{}, varsCommaPositions, copy(valuesCommaPositions)); + } - return node; - } - else - { - return allocator.alloc(Location(start, end), copy(vars), copy(values), equalsSignLocation); - } + return node; } } @@ -1088,21 +1041,14 @@ AstStat* Parser::parseReturn() TempVector commaPositions(scratchPosition); if (!blockFollow(lexer.current()) && lexer.current().type != ';') - parseExprList(list, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &commaPositions : nullptr); + parseExprList(list, options.storeCstData ? &commaPositions : nullptr); Location end = list.empty() ? start : list.back()->location; - if (FFlag::LuauStoreCSTData2) - { - AstStatReturn* node = allocator.alloc(Location(start, end), copy(list)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(commaPositions)); - return node; - } - else - { - return allocator.alloc(Location(start, end), copy(list)); - } + AstStatReturn* node = allocator.alloc(Location(start, end), copy(list)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(commaPositions)); + return node; } // type Name [`<' varlist `>'] `=' Type @@ -1125,7 +1071,7 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported, Position t Position genericsOpenPosition{0, 0}; AstArray genericsCommaPositions; Position genericsClosePosition{0, 0}; - auto [generics, genericPacks] = FFlag::LuauStoreCSTData2 && options.storeCstData + auto [generics, genericPacks] = options.storeCstData ? parseGenericTypeList( /* withDefaultValues= */ true, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition ) @@ -1136,20 +1082,13 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported, Position t AstType* type = parseType(); - if (FFlag::LuauStoreCSTData2) - { - AstStatTypeAlias* node = - allocator.alloc(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc( - typeKeywordPosition, genericsOpenPosition, genericsCommaPositions, genericsClosePosition, equalsPosition - ); - return node; - } - else - { - return allocator.alloc(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported); - } + AstStatTypeAlias* node = + allocator.alloc(Location(start, type->location), name->name, name->location, generics, genericPacks, type, exported); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc( + typeKeywordPosition, genericsOpenPosition, genericsCommaPositions, genericsClosePosition, equalsPosition + ); + return node; } // type function Name `(' arglist `)' `=' funcbody `end' @@ -1170,10 +1109,7 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio size_t oldTypeFunctionDepth = typeFunctionDepth; typeFunctionDepth = functionStack.size(); - AstExprFunction* body = - FFlag::LuauStoreReturnTypesAsPackOnAst - ? parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray({nullptr, 0})).first - : parseFunctionBody_DEPRECATED(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray({nullptr, 0})).first; + AstExprFunction* body = parseFunctionBody(/* hasself */ false, matchFn, fnName->name, nullptr, AstArray({nullptr, 0})).first; typeFunctionDepth = oldTypeFunctionDepth; @@ -1181,18 +1117,11 @@ AstStat* Parser::parseTypeFunction(const Location& start, bool exported, Positio bool hasErrors = parseErrors.size() > errorsAtStart; - if (FFlag::LuauStoreCSTData2) - { - AstStatTypeFunction* node = - allocator.alloc(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(typeKeywordPosition, matchFn.location.begin); - return node; - } - else - { - return allocator.alloc(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors); - } + AstStatTypeFunction* node = + allocator.alloc(Location(start, body->location), fnName->name, fnName->location, body, exported, hasErrors); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(typeKeywordPosition, matchFn.location.begin); + return node; } AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArray& attributes) @@ -1224,18 +1153,9 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArr expectMatchAndConsume(')', matchParen); - AstTypePack* retTypes; - AstTypeList retTypes_DEPRECATED; - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - retTypes = parseOptionalReturnType(); - if (!retTypes) - retTypes = allocator.alloc(lexer.current().location, AstTypeList{copy(nullptr, 0), nullptr}); - } - else - { - retTypes_DEPRECATED = parseOptionalReturnType_DEPRECATED().value_or(AstTypeList{copy(nullptr, 0), nullptr}); - } + AstTypePack* retTypes = parseOptionalReturnType(); + if (!retTypes) + retTypes = allocator.alloc(lexer.current().location, AstTypeList{copy(nullptr, 0), nullptr}); Location end = lexer.previousLocation(); TempVector vars(scratchType); @@ -1262,20 +1182,9 @@ AstDeclaredExternTypeProperty Parser::parseDeclaredExternTypeMethod(const AstArr if (vararg && !varargAnnotation) report(start, "All declaration parameters aside from 'self' must be annotated"); - AstType* fnType = - FFlag::LuauStoreReturnTypesAsPackOnAst - ? allocator.alloc( - Location(start, end), attributes, generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes - ) - : allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - AstTypeList{copy(vars), varargAnnotation}, - copy(varNames), - retTypes_DEPRECATED - ); + AstType* fnType = allocator.alloc( + Location(start, end), attributes, generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes + ); return AstDeclaredExternTypeProperty{fnName.name, fnName.location, fnType, true, Location(start, end)}; } @@ -1316,17 +1225,9 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray(lexer.current().location, AstTypeList{copy(nullptr, 0), nullptr}); - } - else - { - retTypes_DEPRECATED = parseOptionalReturnType_DEPRECATED().value_or(AstTypeList{copy(nullptr, 0)}); - } + retTypes = parseOptionalReturnType(); + if (!retTypes) + retTypes = allocator.alloc(lexer.current().location, AstTypeList{copy(nullptr, 0), nullptr}); Location end = lexer.current().location; TempVector vars(scratchType); @@ -1344,38 +1245,19 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray( - Location(start, end), - attributes, - globalName.name, - globalName.location, - generics, - genericPacks, - AstTypeList{copy(vars), varargAnnotation}, - copy(varNames), - vararg, - varargLocation, - retTypes - ); - } - else - { - return allocator.alloc( - Location(start, end), - attributes, - globalName.name, - globalName.location, - generics, - genericPacks, - AstTypeList{copy(vars), varargAnnotation}, - copy(varNames), - vararg, - varargLocation, - retTypes_DEPRECATED - ); - } + return allocator.alloc( + Location(start, end), + attributes, + globalName.name, + globalName.location, + generics, + genericPacks, + AstTypeList{copy(vars), varargAnnotation}, + copy(varNames), + vararg, + varargLocation, + retTypes + ); } else if (AstName(lexer.current().name) == "class" || (FFlag::LuauDeclareExternType && AstName(lexer.current().name) == "extern")) { @@ -1482,11 +1364,7 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray values(scratchExprAux); TempVector valuesCommaPositions(scratchPosition); - parseExprList(values, FFlag::LuauStoreCSTData2 && options.storeCstData ? &valuesCommaPositions : nullptr); + parseExprList(values, options.storeCstData ? &valuesCommaPositions : nullptr); - if (FFlag::LuauStoreCSTData2) - { - AstStatAssign* node = allocator.alloc(Location(initial->location, values.back()->location), copy(vars), copy(values)); - cstNodeMap[node] = allocator.alloc(copy(varsCommaPositions), equalsPosition, copy(valuesCommaPositions)); - return node; - } - else - { - return allocator.alloc(Location(initial->location, values.back()->location), copy(vars), copy(values)); - } + AstStatAssign* node = allocator.alloc(Location(initial->location, values.back()->location), copy(vars), copy(values)); + cstNodeMap[node] = allocator.alloc(copy(varsCommaPositions), equalsPosition, copy(valuesCommaPositions)); + return node; } // var [`+=' | `-=' | `*=' | `/=' | `%=' | `^=' | `..='] exp @@ -1683,17 +1540,10 @@ AstStat* Parser::parseCompoundAssignment(AstExpr* initial, AstExprBinary::Op op) AstExpr* value = parseExpr(); - if (FFlag::LuauStoreCSTData2) - { - AstStatCompoundAssign* node = allocator.alloc(Location(initial->location, value->location), op, initial, value); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(opPosition); - return node; - } - else - { - return allocator.alloc(Location(initial->location, value->location), op, initial, value); - } + AstStatCompoundAssign* node = allocator.alloc(Location(initial->location, value->location), op, initial, value); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(opPosition); + return node; } std::pair> Parser::prepareFunctionArguments(const Location& start, bool hasself, const TempVector& args) @@ -1721,17 +1571,15 @@ std::pair Parser::parseFunctionBody( const AstArray& attributes ) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); - Location start = matchFunction.location; if (attributes.size > 0) start = attributes.data[0]->location; - auto* cstNode = FFlag::LuauStoreCSTData2 && options.storeCstData ? allocator.alloc() : nullptr; + auto* cstNode = options.storeCstData ? allocator.alloc() : nullptr; auto [generics, genericPacks] = - FFlag::LuauStoreCSTData2 && cstNode + cstNode ? parseGenericTypeList( /* withDefaultValues= */ false, &cstNode->openGenericsPosition, &cstNode->genericsCommaPositions, &cstNode->closeGenericsPosition ) @@ -1809,191 +1657,31 @@ std::pair Parser::parseFunctionBody( bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchFunction); body->hasEnd = hasEnd; - if (FFlag::LuauStoreCSTData2) + AstExprFunction* node = allocator.alloc( + Location(start, end), + attributes, + generics, + genericPacks, + self, + vars, + vararg, + varargLocation, + body, + functionStack.size(), + debugname, + typelist, + varargAnnotation, + argLocation + ); + if (options.storeCstData) { - AstExprFunction* node = allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - self, - vars, - vararg, - varargLocation, - body, - functionStack.size(), - debugname, - typelist, - varargAnnotation, - argLocation - ); - if (options.storeCstData) - { - cstNode->functionKeywordPosition = matchFunction.location.begin; - if (FFlag::LuauStoreLocalAnnotationColonPositions) - cstNode->argsAnnotationColonPositions = extractAnnotationColonPositions(args); - cstNodeMap[node] = cstNode; - } - - return {node, funLocal}; + cstNode->functionKeywordPosition = matchFunction.location.begin; + if (FFlag::LuauStoreLocalAnnotationColonPositions) + cstNode->argsAnnotationColonPositions = extractAnnotationColonPositions(args); + cstNodeMap[node] = cstNode; } - else - { - return { - allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - self, - vars, - vararg, - varargLocation, - body, - functionStack.size(), - debugname, - typelist, - varargAnnotation, - argLocation - ), - funLocal - }; - } -} -std::pair Parser::parseFunctionBody_DEPRECATED( - bool hasself, - const Lexeme& matchFunction, - const AstName& debugname, - const Name* localName, - const AstArray& attributes -) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); - - Location start = matchFunction.location; - - if (attributes.size > 0) - start = attributes.data[0]->location; - - auto* cstNode = FFlag::LuauStoreCSTData2 && options.storeCstData ? allocator.alloc() : nullptr; - - auto [generics, genericPacks] = - FFlag::LuauStoreCSTData2 && cstNode - ? parseGenericTypeList( - /* withDefaultValues= */ false, &cstNode->openGenericsPosition, &cstNode->genericsCommaPositions, &cstNode->closeGenericsPosition - ) - : parseGenericTypeList(/* withDefaultValues= */ false); - - 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 - // - matchRecoveryStopOnToken[')']++; - - TempVector args(scratchBinding); - - bool vararg = false; - Location varargLocation; - AstTypePack* varargAnnotation = nullptr; - - if (lexer.current().type != ')') - std::tie(vararg, varargLocation, varargAnnotation) = - parseBindingList(args, /* allowDot3= */ true, cstNode ? &cstNode->argsCommaPositions : nullptr); - - std::optional argLocation; - - if (matchParen.type == Lexeme::Type('(') && lexer.current().type == Lexeme::Type(')')) - argLocation = Location(matchParen.position, lexer.current().location.end); - - expectMatchAndConsume(')', matchParen, true); - - matchRecoveryStopOnToken[')']--; - - std::optional typelist = parseOptionalReturnType_DEPRECATED(cstNode ? &cstNode->returnSpecifierPosition : nullptr); - - AstLocal* funLocal = nullptr; - - if (localName) - funLocal = pushLocal(Binding(*localName, nullptr)); - - unsigned int localsBegin = saveLocals(); - - Function fun; - fun.vararg = vararg; - - functionStack.emplace_back(fun); - - auto [self, vars] = prepareFunctionArguments(start, hasself, args); - - AstStatBlock* body = parseBlock(); - - functionStack.pop_back(); - - restoreLocals(localsBegin); - - Location end = lexer.current().location; - - bool hasEnd = expectMatchEndAndConsume(Lexeme::ReservedEnd, matchFunction); - body->hasEnd = hasEnd; - - if (FFlag::LuauStoreCSTData2) - { - AstExprFunction* node = allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - self, - vars, - vararg, - varargLocation, - body, - functionStack.size(), - debugname, - typelist, - varargAnnotation, - argLocation - ); - if (options.storeCstData) - { - cstNode->functionKeywordPosition = matchFunction.location.begin; - cstNodeMap[node] = cstNode; - } - - return {node, funLocal}; - } - else - { - return { - allocator.alloc( - Location(start, end), - attributes, - generics, - genericPacks, - self, - vars, - vararg, - varargLocation, - body, - functionStack.size(), - debugname, - typelist, - varargAnnotation, - argLocation - ), - funLocal - }; - } + return {node, funLocal}; } // explist ::= {exp `,'} exp @@ -2003,7 +1691,7 @@ void Parser::parseExprList(TempVector& result, TempVector* c while (lexer.current().type == ',') { - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) commaPositions->push_back(lexer.current().location.begin); nextLexeme(); @@ -2054,7 +1742,7 @@ std::tuple Parser::parseBindingList( { TempVector localCommaPositions(scratchPosition); - if (FFlag::LuauStoreCSTData2 && commaPositions && initialCommaPosition) + if (commaPositions && initialCommaPosition) localCommaPositions.push_back(*initialCommaPosition); while (true) @@ -2074,7 +1762,7 @@ std::tuple Parser::parseBindingList( tailAnnotation = parseVariadicArgumentTypePack(); } - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) *commaPositions = copy(localCommaPositions); return {true, varargLocation, tailAnnotation}; @@ -2084,12 +1772,12 @@ std::tuple Parser::parseBindingList( if (lexer.current().type != ',') break; - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) localCommaPositions.push_back(lexer.current().location.begin); nextLexeme(); } - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) *commaPositions = copy(localCommaPositions); return {false, Location(), nullptr}; @@ -2124,7 +1812,7 @@ AstTypePack* Parser::parseTypeList( // Fill in previous argument names with empty slots while (resultNames.size() < result.size()) resultNames.push_back({}); - if (FFlag::LuauStoreCSTData2 && nameColonPositions) + if (nameColonPositions) { while (nameColonPositions->size() < result.size()) nameColonPositions->push_back({}); @@ -2133,7 +1821,7 @@ AstTypePack* Parser::parseTypeList( resultNames.push_back(AstArgumentName{AstName(lexer.current().name), lexer.current().location}); nextLexeme(); - if (FFlag::LuauStoreCSTData2 && nameColonPositions) + if (nameColonPositions) nameColonPositions->push_back(lexer.current().location.begin); expectAndConsume(':'); } @@ -2141,7 +1829,7 @@ AstTypePack* Parser::parseTypeList( { // If we have a type with named arguments, provide elements for all types resultNames.push_back({}); - if (FFlag::LuauStoreCSTData2 && nameColonPositions) + if (nameColonPositions) nameColonPositions->push_back({}); } @@ -2149,7 +1837,7 @@ AstTypePack* Parser::parseTypeList( if (lexer.current().type != ',') break; - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) commaPositions->push_back(lexer.current().location.begin); nextLexeme(); @@ -2165,13 +1853,12 @@ AstTypePack* Parser::parseTypeList( AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition) { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow) { if (lexer.current().type == Lexeme::SkinnyArrow) report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'"); - if (FFlag::LuauStoreCSTData2 && returnSpecifierPosition) + if (returnSpecifierPosition) *returnSpecifierPosition = lexer.current().location.begin; nextLexeme(); @@ -2197,43 +1884,9 @@ AstTypePack* Parser::parseOptionalReturnType(Position* returnSpecifierPosition) return nullptr; } -std::optional Parser::parseOptionalReturnType_DEPRECATED(Luau::Position* returnSpecifierPosition) -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); - if (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow) - { - if (lexer.current().type == Lexeme::SkinnyArrow) - report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'"); - - if (FFlag::LuauStoreCSTData2 && returnSpecifierPosition) - *returnSpecifierPosition = lexer.current().location.begin; - nextLexeme(); - - unsigned int oldRecursionCount = recursionCounter; - - auto [_location, result] = parseReturnType_DEPRECATED(); - - // At this point, if we find a , character, it indicates that there are multiple return types - // in this type annotation, but the list wasn't wrapped in parentheses. - if (lexer.current().type == ',') - { - report(lexer.current().location, "Expected a statement, got ','; did you forget to wrap the list of return types in parentheses?"); - - nextLexeme(); - } - - recursionCounter = oldRecursionCount; - - return result; - } - - return std::nullopt; -} - // ReturnType ::= Type | `(' TypeList `)' AstTypePack* Parser::parseReturnType() { - LUAU_ASSERT(FFlag::LuauStoreReturnTypesAsPackOnAst); incrementRecursionCounter("type annotation"); Lexeme begin = lexer.current(); @@ -2334,81 +1987,8 @@ AstTypePack* Parser::parseReturnType() return node; } -// ReturnType ::= Type | `(' TypeList `)' -std::pair Parser::parseReturnType_DEPRECATED() -{ - LUAU_ASSERT(!FFlag::LuauStoreReturnTypesAsPackOnAst); - incrementRecursionCounter("type annotation"); - - Lexeme begin = lexer.current(); - - if (lexer.current().type != '(') - { - if (shouldParseTypePack(lexer)) - { - AstTypePack* typePack = parseTypePack(); - - return {typePack->location, AstTypeList{{}, typePack}}; - } - else - { - AstType* type = parseType(); - - return {type->location, AstTypeList{copy(&type, 1), nullptr}}; - } - } - - nextLexeme(); - - matchRecoveryStopOnToken[Lexeme::SkinnyArrow]++; - - TempVector result(scratchType); - TempVector> resultNames(scratchOptArgName); - AstTypePack* varargAnnotation = nullptr; - - // possibly () -> ReturnType - if (lexer.current().type != ')') - varargAnnotation = parseTypeList(result, resultNames); - - const Location location{begin.location, lexer.current().location}; - - expectMatchAndConsume(')', begin, true); - - matchRecoveryStopOnToken[Lexeme::SkinnyArrow]--; - - if (lexer.current().type != Lexeme::SkinnyArrow && resultNames.empty()) - { - // If it turns out that it's just '(A)', it's possible that there are unions/intersections to follow, so fold over it. - if (result.size() == 1) - { - // TODO(CLI-140667): stop parsing type suffix when varargAnnotation != nullptr - this should be a parse error - AstType* inner = varargAnnotation == nullptr ? allocator.alloc(location, result[0]) : result[0]; - AstType* returnType = parseTypeSuffix(inner, begin.location); - - if (DFFlag::DebugLuauReportReturnTypeVariadicWithTypeSuffix && varargAnnotation != nullptr && - (returnType->is() || returnType->is())) - luau_telemetry_parsed_return_type_variadic_with_type_suffix = true; - - // If parseType parses nothing, then returnType->location.end only points at the last non-type-pack - // type to successfully parse. We need the span of the whole annotation. - Position endPos = result.size() == 1 ? location.end : returnType->location.end; - - return {Location{location.begin, endPos}, AstTypeList{copy(&returnType, 1), varargAnnotation}}; - } - - - return {location, AstTypeList{copy(result), varargAnnotation}}; - } - - AstType* tail = parseFunctionTypeTail(begin, {nullptr, 0}, {}, {}, copy(result), copy(resultNames), varargAnnotation); - - return {Location{location, tail->location}, AstTypeList{copy(&tail, 1), varargAnnotation}}; -} - std::pair Parser::extractStringDetails() { - LUAU_ASSERT(FFlag::LuauStoreCSTData2); - CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; @@ -2461,26 +2041,6 @@ Parser::TableIndexerResult Parser::parseTableIndexer(AstTableAccess access, std: }; } -// Remove with FFlagLuauStoreCSTData2 -AstTableIndexer* Parser::parseTableIndexer_DEPRECATED(AstTableAccess access, std::optional accessLocation, Lexeme begin) -{ - if (!FFlag::LuauParseStringIndexer) - { - begin = lexer.current(); - nextLexeme(); // [ - } - - AstType* index = parseType(); - - expectMatchAndConsume(']', begin); - - expectAndConsume(':', "table field"); - - AstType* result = parseType(); - - return allocator.alloc(AstTableIndexer{index, result, Location(begin.location, result->location), access, accessLocation}); -} - // TableProp ::= Name `:' Type // TablePropOrIndexer ::= TableProp | TableIndexer // PropList ::= TablePropOrIndexer {fieldsep TablePropOrIndexer} [fieldsep] @@ -2532,7 +2092,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) { CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) std::tie(style, blockDepth) = extractStringDetails(); Position stringPosition = lexer.current().location.begin; @@ -2552,7 +2112,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) if (chars && !containsNull) { props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::StringProperty, begin.location.begin, @@ -2573,35 +2133,24 @@ AstType* Parser::parseTableType(bool inDeclarationContext) { // maybe we don't need to parse the entire badIndexer... // however, we either have { or [ to lint, not the entire table type or the bad indexer. - AstTableIndexer* badIndexer; - if (FFlag::LuauStoreCSTData2) - badIndexer = parseTableIndexer(access, accessLocation, begin).node; - else - badIndexer = parseTableIndexer_DEPRECATED(access, accessLocation, begin); + AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, begin).node; // we lose all additional indexer expressions from the AST after error recovery here report(badIndexer->location, "Cannot have more than one table indexer"); } else { - if (FFlag::LuauStoreCSTData2) - { - auto tableIndexerResult = parseTableIndexer(access, accessLocation, begin); - indexer = tableIndexerResult.node; - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::Indexer, - tableIndexerResult.indexerOpenPosition, - tableIndexerResult.indexerClosePosition, - tableIndexerResult.colonPosition, - tableSeparator(), - lexer.current().location.begin, - }); - } - else - { - indexer = parseTableIndexer_DEPRECATED(access, accessLocation, begin); - } + auto tableIndexerResult = parseTableIndexer(access, accessLocation, begin); + indexer = tableIndexerResult.node; + if (options.storeCstData) + cstItems.push_back(CstTypeTable::Item{ + CstTypeTable::Item::Kind::Indexer, + tableIndexerResult.indexerOpenPosition, + tableIndexerResult.indexerClosePosition, + tableIndexerResult.colonPosition, + tableSeparator(), + lexer.current().location.begin, + }); } } } @@ -2629,7 +2178,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) AstType* type = parseType(inDeclarationContext); props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::Property, Position{0, 0}, @@ -2649,7 +2198,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) CstExprConstantString::QuoteStyle style; unsigned int blockDepth = 0; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) std::tie(style, blockDepth) = extractStringDetails(); Position stringPosition = lexer.current().location.begin; @@ -2669,7 +2218,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) if (chars && !containsNull) { props.push_back(AstTableProp{AstName(chars->data), begin.location, type, access, accessLocation}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::StringProperty, begin.location.begin, @@ -2690,39 +2239,25 @@ AstType* Parser::parseTableType(bool inDeclarationContext) { // maybe we don't need to parse the entire badIndexer... // however, we either have { or [ to lint, not the entire table type or the bad indexer. - AstTableIndexer* badIndexer; - if (FFlag::LuauStoreCSTData2) - // the last param in the parseTableIndexer is ignored - badIndexer = parseTableIndexer(access, accessLocation, lexer.current()).node; - else - // the last param in the parseTableIndexer is ignored - badIndexer = parseTableIndexer_DEPRECATED(access, accessLocation, lexer.current()); + AstTableIndexer* badIndexer = parseTableIndexer(access, accessLocation, lexer.current()).node; // we lose all additional indexer expressions from the AST after error recovery here report(badIndexer->location, "Cannot have more than one table indexer"); } else { - if (FFlag::LuauStoreCSTData2) - { - // the last param in the parseTableIndexer is ignored - auto tableIndexerResult = parseTableIndexer(access, accessLocation, lexer.current()); - indexer = tableIndexerResult.node; - if (options.storeCstData) - cstItems.push_back(CstTypeTable::Item{ - CstTypeTable::Item::Kind::Indexer, - tableIndexerResult.indexerOpenPosition, - tableIndexerResult.indexerClosePosition, - tableIndexerResult.colonPosition, - tableSeparator(), - lexer.current().location.begin, - }); - } - else - { - // the last param in the parseTableIndexer is ignored - indexer = parseTableIndexer_DEPRECATED(access, accessLocation, lexer.current()); - } + // the last param in the parseTableIndexer is ignored + auto tableIndexerResult = parseTableIndexer(access, accessLocation, lexer.current()); + indexer = tableIndexerResult.node; + if (options.storeCstData) + cstItems.push_back(CstTypeTable::Item{ + CstTypeTable::Item::Kind::Indexer, + tableIndexerResult.indexerOpenPosition, + tableIndexerResult.indexerClosePosition, + tableIndexerResult.colonPosition, + tableSeparator(), + lexer.current().location.begin, + }); } } else if (props.empty() && !indexer && !(lexer.current().type == Lexeme::Name && lexer.lookahead().type == ':')) @@ -2749,7 +2284,7 @@ AstType* Parser::parseTableType(bool inDeclarationContext) AstType* type = parseType(inDeclarationContext); props.push_back(AstTableProp{name->name, name->location, type, access, accessLocation}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back(CstTypeTable::Item{ CstTypeTable::Item::Kind::Property, Position{0, 0}, @@ -2777,17 +2312,10 @@ AstType* Parser::parseTableType(bool inDeclarationContext) if (!expectMatchAndConsume('}', matchBrace, /* searchForMissing = */ true)) end = lexer.previousLocation(); - if (FFlag::LuauStoreCSTData2) - { - AstTypeTable* node = allocator.alloc(Location(start, end), copy(props), indexer); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(cstItems), isArray); - return node; - } - else - { - return allocator.alloc(Location(start, end), copy(props), indexer); - } + AstTypeTable* node = allocator.alloc(Location(start, end), copy(props), indexer); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(cstItems), isArray); + return node; } // ReturnType ::= Type | `(' TypeList `)' @@ -2803,7 +2331,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray Position genericsOpenPosition{0, 0}; AstArray genericsCommaPositions; Position genericsClosePosition{0, 0}; - auto [generics, genericPacks] = FFlag::LuauStoreCSTData2 && options.storeCstData + auto [generics, genericPacks] = options.storeCstData ? parseGenericTypeList( /* withDefaultValues= */ false, &genericsOpenPosition, &genericsCommaPositions, &genericsClosePosition ) @@ -2823,7 +2351,7 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray if (lexer.current().type != ')') { - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) varargAnnotation = parseTypeList(params, names, &argCommaPositions, &nameColonPositions); else varargAnnotation = parseTypeList(params, names); @@ -2846,18 +2374,11 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray { if (allowPack) { - if (FFlag::LuauStoreCSTData2) - { - AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr}); - if (options.storeCstData) - cstNodeMap[node] = - allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); - return {{}, node}; - } - else - { - return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr})}; - } + AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, nullptr}); + if (options.storeCstData) + cstNodeMap[node] = + allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); + return {{}, node}; } else { @@ -2867,45 +2388,30 @@ AstTypeOrPack Parser::parseFunctionType(bool allowPack, const AstArray if (!forceFunctionType && !returnTypeIntroducer && allowPack) { - if (FFlag::LuauStoreCSTData2) - { - AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation}); - if (options.storeCstData) - cstNodeMap[node] = - allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); - return {{}, node}; - } - else - { - return {{}, allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation})}; - } + AstTypePackExplicit* node = allocator.alloc(begin.location, AstTypeList{paramTypes, varargAnnotation}); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(parameterStart.location.begin, closeArgsLocation.begin, copy(argCommaPositions)); + return {{}, node}; } AstArray> paramNames = copy(names); - if (FFlag::LuauStoreCSTData2) + Position returnArrowPosition = lexer.current().location.begin; + AstType* node = parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation); + if (options.storeCstData && node->is()) { - Position returnArrowPosition = lexer.current().location.begin; - AstType* node = parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation); - if (options.storeCstData && node->is()) - { - cstNodeMap[node] = allocator.alloc( - genericsOpenPosition, - genericsCommaPositions, - genericsClosePosition, - parameterStart.location.begin, - copy(nameColonPositions), - copy(argCommaPositions), - closeArgsLocation.begin, - returnArrowPosition - ); - } - return {node, {}}; - } - else - { - return {parseFunctionTypeTail(begin, attributes, generics, genericPacks, paramTypes, paramNames, varargAnnotation), {}}; + cstNodeMap[node] = allocator.alloc( + genericsOpenPosition, + genericsCommaPositions, + genericsClosePosition, + parameterStart.location.begin, + copy(nameColonPositions), + copy(argCommaPositions), + closeArgsLocation.begin, + returnArrowPosition + ); } + return {node, {}}; } AstType* Parser::parseFunctionTypeTail( @@ -2937,25 +2443,13 @@ AstType* Parser::parseFunctionTypeTail( expectAndConsume(Lexeme::SkinnyArrow, "function type"); } - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - auto returnType = parseReturnType(); - LUAU_ASSERT(returnType); + auto returnType = parseReturnType(); + LUAU_ASSERT(returnType); - AstTypeList paramTypes = AstTypeList{params, varargAnnotation}; - return allocator.alloc( - Location(begin.location, returnType->location), attributes, generics, genericPacks, paramTypes, paramNames, returnType - ); - } - else - { - auto [endLocation, returnTypeList] = parseReturnType_DEPRECATED(); - - AstTypeList paramTypes = AstTypeList{params, varargAnnotation}; - return allocator.alloc( - Location(begin.location, endLocation), attributes, generics, genericPacks, paramTypes, paramNames, returnTypeList - ); - } + AstTypeList paramTypes = AstTypeList{params, varargAnnotation}; + return allocator.alloc( + Location(begin.location, returnType->location), attributes, generics, genericPacks, paramTypes, paramNames, returnType + ); } static bool isTypeFollow(Lexeme::Type c) @@ -3000,7 +2494,7 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin) isUnion = true; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) { if (type == nullptr && !leadingPosition.has_value()) leadingPosition = separatorPosition; @@ -3030,7 +2524,7 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin) isIntersection = true; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) { if (type == nullptr && !leadingPosition.has_value()) leadingPosition = separatorPosition; @@ -3063,31 +2557,20 @@ AstType* Parser::parseTypeSuffix(AstType* type, const Location& begin) location.end = parts.back()->location.end; - if (FFlag::LuauStoreCSTData2) + if (isUnion) { - if (isUnion) - { - AstTypeUnion* node = allocator.alloc(location, copy(parts)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); - return node; - } - - if (isIntersection) - { - AstTypeIntersection* node = allocator.alloc(location, copy(parts)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); - return node; - } + AstTypeUnion* node = allocator.alloc(location, copy(parts)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); + return node; } - else - { - if (isUnion) - return allocator.alloc(location, copy(parts)); - if (isIntersection) - return allocator.alloc(location, copy(parts)); + if (isIntersection) + { + AstTypeIntersection* node = allocator.alloc(location, copy(parts)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(leadingPosition, copy(separatorPositions)); + return node; } LUAU_ASSERT(false); @@ -3175,35 +2658,22 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) } else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) { - if (FFlag::LuauStoreCSTData2) - { - CstExprConstantString::QuoteStyle style; - unsigned int blockDepth = 0; - if (options.storeCstData) - std::tie(style, blockDepth) = extractStringDetails(); + CstExprConstantString::QuoteStyle style; + unsigned int blockDepth = 0; + if (options.storeCstData) + std::tie(style, blockDepth) = extractStringDetails(); - AstArray originalString; - if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) - { - AstArray svalue = *value; - auto node = allocator.alloc(start, svalue); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(originalString, style, blockDepth); - return {node}; - } - else - return {reportTypeError(start, {}, "String literal contains malformed escape sequence")}; + AstArray originalString; + if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) + { + AstArray svalue = *value; + auto node = allocator.alloc(start, svalue); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(originalString, style, blockDepth); + return {node}; } else - { - if (std::optional> value = parseCharArray()) - { - AstArray svalue = *value; - return {allocator.alloc(start, svalue)}; - } - else - return {reportTypeError(start, {}, "String literal contains malformed escape sequence")}; - } + return {reportTypeError(start, {}, "String literal contains malformed escape sequence")}; } else if (lexer.current().type == Lexeme::InterpStringBegin || lexer.current().type == Lexeme::InterpStringSimple) { @@ -3225,24 +2695,12 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) if (lexer.current().type == '.') { - if (FFlag::LuauStoreCSTData2) - { - prefixPointPosition = lexer.current().location.begin; - nextLexeme(); + prefixPointPosition = lexer.current().location.begin; + nextLexeme(); - prefix = name.name; - prefixLocation = name.location; - name = parseIndexName("field name", *prefixPointPosition); - } - else - { - Position pointPosition = lexer.current().location.begin; - nextLexeme(); - - prefix = name.name; - prefixLocation = name.location; - name = parseIndexName("field name", pointPosition); - } + prefix = name.name; + prefixLocation = name.location; + name = parseIndexName("field name", *prefixPointPosition); } else if (lexer.current().type == Lexeme::Dot3) { @@ -3260,17 +2718,10 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) expectMatchAndConsume(')', typeofBegin); - if (FFlag::LuauStoreCSTData2) - { - AstTypeTypeof* node = allocator.alloc(Location(start, end), expr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(typeofBegin.location.begin, end.begin); - return {node, {}}; - } - else - { - return {allocator.alloc(Location(start, end), expr), {}}; - } + AstTypeTypeof* node = allocator.alloc(Location(start, end), expr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(typeofBegin.location.begin, end.begin); + return {node, {}}; } bool hasParameters = false; @@ -3282,7 +2733,7 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) if (lexer.current().type == '<') { hasParameters = true; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) parameters = parseTypeParams(¶metersOpeningPosition, ¶metersCommaPositions, ¶metersClosingPosition); else parameters = parseTypeParams(); @@ -3290,23 +2741,13 @@ AstTypeOrPack Parser::parseSimpleType(bool allowPack, bool inDeclarationContext) Location end = lexer.previousLocation(); - if (FFlag::LuauStoreCSTData2) - { - AstTypeReference* node = - allocator.alloc(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc( - prefixPointPosition, parametersOpeningPosition, copy(parametersCommaPositions), parametersClosingPosition - ); - return {node, {}}; - } - else - { - return { - allocator.alloc(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters), - {} - }; - } + AstTypeReference* node = + allocator.alloc(Location(start, end), prefix, name.name, prefixLocation, name.location, hasParameters, parameters); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc( + prefixPointPosition, parametersOpeningPosition, copy(parametersCommaPositions), parametersClosingPosition + ); + return {node, {}}; } else if (lexer.current().type == '{') { @@ -3351,17 +2792,10 @@ AstTypePack* Parser::parseVariadicArgumentTypePack() // This will not fail because of the lookahead guard. expectAndConsume(Lexeme::Dot3, "generic type pack annotation"); - if (FFlag::LuauStoreCSTData2) - { - AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(end.begin); - return node; - } - else - { - return allocator.alloc(Location(name.location, end), name.name); - } + AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(end.begin); + return node; } // Variadic: T else @@ -3389,17 +2823,10 @@ AstTypePack* Parser::parseTypePack() // This will not fail because of the lookahead guard. expectAndConsume(Lexeme::Dot3, "generic type pack annotation"); - if (FFlag::LuauStoreCSTData2) - { - AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(end.begin); - return node; - } - else - { - return allocator.alloc(Location(name.location, end), name.name); - } + AstTypePackGeneric* node = allocator.alloc(Location(name.location, end), name.name); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(end.begin); + return node; } // TODO: shouldParseTypePack can be removed and parseTypePack can be called unconditionally instead @@ -3583,7 +3010,7 @@ AstExpr* Parser::parseExpr(unsigned int limit) AstExpr* subexpr = parseExpr(unaryPriority); expr = allocator.alloc(Location(start, subexpr->location), *uop, subexpr); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstNodeMap[expr] = allocator.alloc(opPosition); } else @@ -3606,7 +3033,7 @@ AstExpr* Parser::parseExpr(unsigned int limit) AstExpr* next = parseExpr(binaryPriority[*op].right); expr = allocator.alloc(Location(start, next->location), *op, expr, next); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstNodeMap[expr] = allocator.alloc(opPosition); op = parseBinaryOp(lexer.current()); @@ -3713,7 +3140,7 @@ AstExpr* Parser::parsePrimaryExpr(bool asStatement) expectMatchAndConsume(']', matchBracket); expr = allocator.alloc(Location(start, end), expr, index); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstNodeMap[expr] = allocator.alloc(matchBracket.position, closeBracketPosition); } else if (lexer.current().type == ':') @@ -3764,7 +3191,7 @@ AstExpr* Parser::parseAssertionExpr() if (lexer.current().type == Lexeme::DoubleColon) { CstExprTypeAssertion* cstNode = nullptr; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) { Position opPosition = lexer.current().location.begin; cstNode = allocator.alloc(opPosition); @@ -3772,7 +3199,7 @@ AstExpr* Parser::parseAssertionExpr() nextLexeme(); AstType* annotation = parseType(); AstExprTypeAssertion* node = allocator.alloc(Location(start, annotation->location), expr, annotation); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstNodeMap[node] = cstNode; return node; } @@ -3884,10 +3311,7 @@ AstExpr* Parser::parseSimpleExpr() Lexeme matchFunction = lexer.current(); nextLexeme(); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - return parseFunctionBody(false, matchFunction, AstName(), nullptr, attributes).first; - else - return parseFunctionBody_DEPRECATED(false, matchFunction, AstName(), nullptr, attributes).first; + return parseFunctionBody(false, matchFunction, AstName(), nullptr, attributes).first; } else if (lexer.current().type == Lexeme::Number) { @@ -3957,24 +3381,17 @@ AstExpr* Parser::parseFunctionArgs(AstExpr* func, bool self) TempVector commaPositions(scratchPosition); if (lexer.current().type != ')') - parseExprList(args, (FFlag::LuauStoreCSTData2 && options.storeCstData) ? &commaPositions : nullptr); + parseExprList(args, options.storeCstData ? &commaPositions : nullptr); Location end = lexer.current().location; Position argEnd = end.end; expectMatchAndConsume(')', matchParen); - if (FFlag::LuauStoreCSTData2) - { - AstExprCall* node = allocator.alloc(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(matchParen.position, lexer.previousLocation().begin, copy(commaPositions)); - return node; - } - else - { - return allocator.alloc(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd)); - } + AstExprCall* node = allocator.alloc(Location(func->location, end), func, copy(args), self, Location(argStart, argEnd)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(matchParen.position, lexer.previousLocation().begin, copy(commaPositions)); + return node; } else if (lexer.current().type == '{') { @@ -3982,35 +3399,21 @@ AstExpr* Parser::parseFunctionArgs(AstExpr* func, bool self) AstExpr* expr = parseTableConstructor(); Position argEnd = lexer.previousLocation().end; - if (FFlag::LuauStoreCSTData2) - { - AstExprCall* node = - allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, Location(argStart, argEnd)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); - return node; - } - else - { - return allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, Location(argStart, argEnd)); - } + AstExprCall* node = + allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, Location(argStart, argEnd)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); + return node; } else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString) { Location argLocation = lexer.current().location; AstExpr* expr = parseString(); - if (FFlag::LuauStoreCSTData2) - { - AstExprCall* node = allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, argLocation); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); - return node; - } - else - { - return allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, argLocation); - } + AstExprCall* node = allocator.alloc(Location(func->location, expr->location), func, copy(&expr, 1), self, argLocation); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(std::nullopt, std::nullopt, AstArray{nullptr, 0}); + return node; } else { @@ -4046,7 +3449,6 @@ LUAU_NOINLINE void Parser::reportAmbiguousCallError() std::optional Parser::tableSeparator() { - LUAU_ASSERT(FFlag::LuauStoreCSTData2); if (lexer.current().type == ',') return CstExprTable::Comma; else if (lexer.current().type == ';') @@ -4091,7 +3493,7 @@ AstExpr* Parser::parseTableConstructor() AstExpr* value = parseExpr(); items.push_back({AstExprTable::Item::General, key, value}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back({indexerOpenPosition, indexerClosePosition, equalsPosition, tableSeparator(), lexer.current().location.begin}); } else if (lexer.current().type == Lexeme::Name && lexer.lookahead().type == '=') @@ -4112,7 +3514,7 @@ AstExpr* Parser::parseTableConstructor() func->debugname = name.name; items.push_back({AstExprTable::Item::Record, key, value}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back({std::nullopt, std::nullopt, equalsPosition, tableSeparator(), lexer.current().location.begin}); } else @@ -4120,7 +3522,7 @@ AstExpr* Parser::parseTableConstructor() AstExpr* expr = parseExpr(); items.push_back({AstExprTable::Item::List, nullptr, expr}); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) cstItems.push_back({std::nullopt, std::nullopt, std::nullopt, tableSeparator(), lexer.current().location.begin}); } @@ -4143,17 +3545,10 @@ AstExpr* Parser::parseTableConstructor() if (!expectMatchAndConsume('}', matchBrace)) end = lexer.previousLocation(); - if (FFlag::LuauStoreCSTData2) - { - AstExprTable* node = allocator.alloc(Location(start, end), copy(items)); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(cstItems)); - return node; - } - else - { - return allocator.alloc(Location(start, end), copy(items)); - } + AstExprTable* node = allocator.alloc(Location(start, end), copy(items)); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(cstItems)); + return node; } AstExpr* Parser::parseIfElseExpr() @@ -4180,8 +3575,7 @@ AstExpr* Parser::parseIfElseExpr() hasElse = true; falseExpr = parseIfElseExpr(); recursionCounter = oldRecursionCount; - if (FFlag::LuauStoreCSTData2) - isElseIf = true; + isElseIf = true; } else { @@ -4191,17 +3585,10 @@ AstExpr* Parser::parseIfElseExpr() Location end = falseExpr->location; - if (FFlag::LuauStoreCSTData2) - { - AstExprIfElse* node = allocator.alloc(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(thenPosition, elsePosition, isElseIf); - return node; - } - else - { - return allocator.alloc(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr); - } + AstExprIfElse* node = allocator.alloc(Location(start, end), condition, hasThen, trueExpr, hasElse, falseExpr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(thenPosition, elsePosition, isElseIf); + return node; } // Name @@ -4268,7 +3655,7 @@ std::pair, AstArray> Parser::pars if (lexer.current().type == '<') { Lexeme begin = lexer.current(); - if (FFlag::LuauStoreCSTData2 && openPosition) + if (openPosition) *openPosition = begin.location.begin; nextLexeme(); @@ -4299,17 +3686,10 @@ std::pair, AstArray> Parser::pars { AstTypePack* typePack = parseTypePack(); - if (FFlag::LuauStoreCSTData2) - { - AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); - namePacks.push_back(node); - } - else - { - namePacks.push_back(allocator.alloc(nameLocation, name, typePack)); - } + AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); + namePacks.push_back(node); } else { @@ -4318,17 +3698,10 @@ std::pair, AstArray> Parser::pars if (type) report(type->location, "Expected type pack after '=', got type"); - if (FFlag::LuauStoreCSTData2) - { - AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); - namePacks.push_back(node); - } - else - { - namePacks.push_back(allocator.alloc(nameLocation, name, typePack)); - } + AstGenericTypePack* node = allocator.alloc(nameLocation, name, typePack); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(ellipsisPosition, equalsPosition); + namePacks.push_back(node); } } else @@ -4336,17 +3709,10 @@ std::pair, AstArray> Parser::pars if (seenDefault) report(lexer.current().location, "Expected default type pack after type pack name"); - if (FFlag::LuauStoreCSTData2) - { - AstGenericTypePack* node = allocator.alloc(nameLocation, name, nullptr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(ellipsisPosition, std::nullopt); - namePacks.push_back(node); - } - else - { - namePacks.push_back(allocator.alloc(nameLocation, name, nullptr)); - } + AstGenericTypePack* node = allocator.alloc(nameLocation, name, nullptr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(ellipsisPosition, std::nullopt); + namePacks.push_back(node); } } else @@ -4359,40 +3725,26 @@ std::pair, AstArray> Parser::pars AstType* defaultType = parseType(); - if (FFlag::LuauStoreCSTData2) - { - AstGenericType* node = allocator.alloc(nameLocation, name, defaultType); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(equalsPosition); - names.push_back(node); - } - else - { - names.push_back(allocator.alloc(nameLocation, name, defaultType)); - } + AstGenericType* node = allocator.alloc(nameLocation, name, defaultType); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(equalsPosition); + names.push_back(node); } else { if (seenDefault) report(lexer.current().location, "Expected default type after type name"); - if (FFlag::LuauStoreCSTData2) - { - AstGenericType* node = allocator.alloc(nameLocation, name, nullptr); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(std::nullopt); - names.push_back(node); - } - else - { - names.push_back(allocator.alloc(nameLocation, name, nullptr)); - } + AstGenericType* node = allocator.alloc(nameLocation, name, nullptr); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(std::nullopt); + names.push_back(node); } } if (lexer.current().type == ',') { - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) localCommaPositions.push_back(lexer.current().location.begin); nextLexeme(); @@ -4406,12 +3758,12 @@ std::pair, AstArray> Parser::pars break; } - if (FFlag::LuauStoreCSTData2 && closePosition) + if (closePosition) *closePosition = lexer.current().location.begin; expectMatchAndConsume('>', begin); } - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) *commaPositions = copy(localCommaPositions); AstArray generics = copy(names); @@ -4426,7 +3778,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV if (lexer.current().type == '<') { Lexeme begin = lexer.current(); - if (FFlag::LuauStoreCSTData2 && openingPosition) + if (openingPosition) *openingPosition = begin.location.begin; nextLexeme(); @@ -4507,7 +3859,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV if (lexer.current().type == ',') { - if (FFlag::LuauStoreCSTData2 && commaPositions) + if (commaPositions) commaPositions->push_back(lexer.current().location.begin); nextLexeme(); } @@ -4515,7 +3867,7 @@ AstArray Parser::parseTypeParams(Position* openingPosition, TempV break; } - if (FFlag::LuauStoreCSTData2 && closingPosition) + if (closingPosition) *closingPosition = lexer.current().location.begin; expectMatchAndConsume('>', begin); } @@ -4531,11 +3883,8 @@ std::optional> Parser::parseCharArray(AstArray* originalStr ); scratchData.assign(lexer.current().data, lexer.current().getLength()); - if (FFlag::LuauStoreCSTData2) - { - if (originalString) - *originalString = copy(scratchData); - } + if (originalString) + *originalString = copy(scratchData); if (lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::InterpStringSimple) { @@ -4573,31 +3922,21 @@ AstExpr* Parser::parseString() LUAU_ASSERT(false && "Invalid string type"); } - if (FFlag::LuauStoreCSTData2) - { - CstExprConstantString::QuoteStyle fullStyle; - unsigned int blockDepth; - if (options.storeCstData) - std::tie(fullStyle, blockDepth) = extractStringDetails(); + CstExprConstantString::QuoteStyle fullStyle; + unsigned int blockDepth; + if (options.storeCstData) + std::tie(fullStyle, blockDepth) = extractStringDetails(); - AstArray originalString; - if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) - { - AstExprConstantString* node = allocator.alloc(location, *value, style); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(originalString, fullStyle, blockDepth); - return node; - } - else - return reportExprError(location, {}, "String literal contains malformed escape sequence"); + AstArray originalString; + if (std::optional> value = parseCharArray(options.storeCstData ? &originalString : nullptr)) + { + AstExprConstantString* node = allocator.alloc(location, *value, style); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(originalString, fullStyle, blockDepth); + return node; } else - { - if (std::optional> value = parseCharArray()) - return allocator.alloc(location, *value, style); - else - return reportExprError(location, {}, "String literal contains malformed escape sequence"); - } + return reportExprError(location, {}, "String literal contains malformed escape sequence"); } AstExpr* Parser::parseInterpString() @@ -4622,7 +3961,7 @@ AstExpr* Parser::parseInterpString() scratchData.assign(currentLexeme.data, currentLexeme.getLength()); - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) { sourceStrings.push_back(copy(scratchData)); stringPositions.push_back(currentLexeme.location.begin); @@ -4692,15 +4031,10 @@ AstExpr* Parser::parseInterpString() AstArray> stringsArray = copy(strings); AstArray expressionsArray = copy(expressions); - if (FFlag::LuauStoreCSTData2) - { - AstExprInterpString* node = allocator.alloc(Location{startLocation, endLocation}, stringsArray, expressionsArray); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(copy(sourceStrings), copy(stringPositions)); - return node; - } - else - return allocator.alloc(Location{startLocation, endLocation}, stringsArray, expressionsArray); + AstExprInterpString* node = allocator.alloc(Location{startLocation, endLocation}, stringsArray, expressionsArray); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(copy(sourceStrings), copy(stringPositions)); + return node; } AstExpr* Parser::parseNumber() @@ -4709,7 +4043,7 @@ AstExpr* Parser::parseNumber() scratchData.assign(lexer.current().data, lexer.current().getLength()); AstArray sourceData; - if (FFlag::LuauStoreCSTData2 && options.storeCstData) + if (options.storeCstData) sourceData = copy(scratchData); // Remove all internal _ - they don't hold any meaning and this allows parsing code to just pass the string pointer to strtod et al @@ -4725,17 +4059,10 @@ AstExpr* Parser::parseNumber() if (result == ConstantNumberParseResult::Malformed) return reportExprError(start, {}, "Malformed number"); - if (FFlag::LuauStoreCSTData2) - { - AstExprConstantNumber* node = allocator.alloc(start, value, result); - if (options.storeCstData) - cstNodeMap[node] = allocator.alloc(sourceData); - return node; - } - else - { - return allocator.alloc(start, value, result); - } + AstExprConstantNumber* node = allocator.alloc(start, value, result); + if (options.storeCstData) + cstNodeMap[node] = allocator.alloc(sourceData); + return node; } AstLocal* Parser::pushLocal(const Binding& binding) diff --git a/Common/include/Luau/ExperimentalFlags.h b/Common/include/Luau/ExperimentalFlags.h index 64d52d0f..a9835216 100644 --- a/Common/include/Luau/ExperimentalFlags.h +++ b/Common/include/Luau/ExperimentalFlags.h @@ -18,6 +18,7 @@ inline bool isAnalysisFlagExperimental(const char* flag) "LuauTableCloneClonesType3", // requires fixes in lua-apps code, terrifyingly "LuauNormalizationReorderFreeTypeIntersect", // requires fixes in lua-apps code, also terrifyingly "LuauSolverV2", + "UseNewLuauTypeSolverDefaultEnabled", // This can change the default solver used in cli applications, so it also needs to be disabled. Will require fixes in lua-apps code // makes sure we always have at least one entry nullptr, }; diff --git a/Compiler/include/Luau/Compiler.h b/Compiler/include/Luau/Compiler.h index 2c82116d..970eea92 100644 --- a/Compiler/include/Luau/Compiler.h +++ b/Compiler/include/Luau/Compiler.h @@ -73,7 +73,7 @@ struct CompileOptions class CompileError : public std::exception { public: - CompileError(const Location& location, const std::string& message); + CompileError(const Location& location, std::string message); virtual ~CompileError() throw(); diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 447f944a..dec886e0 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -26,9 +26,9 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25) LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300) LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) +LUAU_FASTFLAGVARIABLE(LuauCompileInlineNonConstInit) LUAU_FASTFLAGVARIABLE(LuauSeparateCompilerTypeInfo) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAGVARIABLE(LuauCompileFixTypeFunctionSkip) namespace Luau @@ -45,9 +45,9 @@ static const uint8_t kInvalidReg = 255; static const uint32_t kDefaultAllocPc = ~0u; -CompileError::CompileError(const Location& location, const std::string& message) +CompileError::CompileError(const Location& location, std::string message) : location(location) - , message(message) + , message(std::move(message)) { } @@ -302,7 +302,7 @@ struct Compiler { f.canInline = true; f.stackSize = stackSize; - f.costModel = modelCost(func->body, func->args.data, func->args.size, builtins); + f.costModel = modelCost(func->body, func->args.data, func->args.size, builtins, constants); // track functions that only ever return a single value so that we can convert multret calls to fixedret calls if (alwaysTerminates(func->body)) @@ -696,7 +696,10 @@ struct Compiler // if the argument is a local that isn't mutated, we will simply reuse the existing register if (int reg = le ? getExprLocalReg(le) : -1; reg >= 0 && (!lv || !lv->written)) { - args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc}); + if (FFlag::LuauCompileInlineNonConstInit) + args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc, lv ? lv->init : nullptr}); + else + args.push_back({var, uint8_t(reg), {Constant::Type_Unknown}, kDefaultAllocPc}); } else { @@ -719,9 +722,19 @@ struct Compiler for (InlineArg& arg : args) { if (arg.value.type == Constant::Type_Unknown) + { pushLocal(arg.local, arg.reg, arg.allocpc); + + if (FFlag::LuauCompileInlineNonConstInit && arg.init) + { + if (Variable* lv = variables.find(arg.local)) + lv->init = arg.init; + } + } else + { locstants[arg.local] = arg.value; + } } // the inline frame will be used to compile return statements as well as to reject recursive inlining attempts @@ -771,6 +784,12 @@ struct Compiler if (Constant* var = locstants.find(local)) var->type = Constant::Type_Unknown; + + if (FFlag::LuauCompileInlineNonConstInit) + { + if (Variable* lv = variables.find(local)) + lv->init = nullptr; + } } foldConstants(constants, variables, locstants, builtinsFold, builtinsFoldLibraryK, options.libraryMemberConstantCb, func->body); @@ -3016,7 +3035,7 @@ struct Compiler } AstLocal* var = stat->var; - uint64_t costModel = modelCost(stat->body, &var, 1, builtins); + uint64_t costModel = modelCost(stat->body, &var, 1, builtins, constants); // we use a dynamic cost threshold that's based on the fixed limit boosted by the cost advantage we gain due to unrolling bool varc = true; @@ -4109,6 +4128,8 @@ struct Compiler uint8_t reg; Constant value; uint32_t allocpc; + + AstExpr* init; }; struct InlineFrame @@ -4325,54 +4346,27 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c mainFlags |= LPF_NATIVE_FUNCTION; } - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - AstExprFunction main( - root->location, - /* attributes= */ AstArray({nullptr, 0}), - /* generics= */ AstArray(), - /* genericPacks= */ AstArray(), - /* self= */ nullptr, - AstArray(), - /* vararg= */ true, - /* varargLocation= */ Luau::Location(), - root, - /* functionDepth= */ 0, - /* debugname= */ AstName(), - /* returnAnnotation= */ nullptr - ); - uint32_t mainid = compiler.compileFunction(&main, mainFlags); + AstExprFunction main( + root->location, + /* attributes= */ AstArray({nullptr, 0}), + /* generics= */ AstArray(), + /* genericPacks= */ AstArray(), + /* self= */ nullptr, + AstArray(), + /* vararg= */ true, + /* varargLocation= */ Luau::Location(), + root, + /* functionDepth= */ 0, + /* debugname= */ AstName(), + /* returnAnnotation= */ nullptr + ); + uint32_t mainid = compiler.compileFunction(&main, mainFlags); - const Compiler::Function* mainf = compiler.functions.find(&main); - LUAU_ASSERT(mainf && mainf->upvals.empty()); + const Compiler::Function* mainf = compiler.functions.find(&main); + LUAU_ASSERT(mainf && mainf->upvals.empty()); - bytecode.setMainFunction(mainid); - bytecode.finalize(); - } - else - { - AstExprFunction main( - root->location, - /* attributes= */ AstArray({nullptr, 0}), - /* generics= */ AstArray(), - /* genericPacks= */ AstArray(), - /* self= */ nullptr, - AstArray(), - /* vararg= */ true, - /* varargLocation= */ Luau::Location(), - root, - /* functionDepth= */ 0, - /* debugname= */ AstName(), - /* returnAnnotation= */ std::nullopt - ); - uint32_t mainid = compiler.compileFunction(&main, mainFlags); - - const Compiler::Function* mainf = compiler.functions.find(&main); - LUAU_ASSERT(mainf && mainf->upvals.empty()); - - bytecode.setMainFunction(mainid); - bytecode.finalize(); - } + bytecode.setMainFunction(mainid); + bytecode.finalize(); } void compileOrThrow(BytecodeBuilder& bytecode, const std::string& source, const CompileOptions& options, const ParseOptions& parseOptions) diff --git a/Compiler/src/CostModel.cpp b/Compiler/src/CostModel.cpp index 04adf3e3..4dc6580b 100644 --- a/Compiler/src/CostModel.cpp +++ b/Compiler/src/CostModel.cpp @@ -4,8 +4,12 @@ #include "Luau/Common.h" #include "Luau/DenseHash.h" +#include "ConstantFolding.h" + #include +LUAU_FASTFLAGVARIABLE(LuauCompileCostModelConstants) + namespace Luau { namespace Compile @@ -39,7 +43,7 @@ static uint64_t parallelMulSat(uint64_t a, int b) return r | (s - (s >> 7)); } -inline bool getNumber(AstExpr* node, double& result) +inline bool getNumber_DEPRECATED(AstExpr* node, double& result) { // since constant model doesn't use constant folding atm, we perform the basic extraction that's sufficient to handle positive/negative literals if (AstExprConstantNumber* ne = node->as()) @@ -114,18 +118,23 @@ struct Cost struct CostVisitor : AstVisitor { const DenseHashMap& builtins; + const DenseHashMap& constants; DenseHashMap vars; Cost result; - CostVisitor(const DenseHashMap& builtins) + CostVisitor(const DenseHashMap& builtins, const DenseHashMap& constants) : builtins(builtins) + , constants(constants) , vars(nullptr) { } Cost model(AstExpr* node) { + if (FFlag::LuauCompileCostModelConstants && constants.contains(node)) + return Cost(0, Cost::kLiteral); + if (AstExprGroup* expr = node->as()) { return model(expr->expr); @@ -270,8 +279,18 @@ struct CostVisitor : AstVisitor int tripCount = -1; double from, to, step = 1; - if (getNumber(node->from, from) && getNumber(node->to, to) && (!node->step || getNumber(node->step, step))) - tripCount = getTripCount(from, to, step); + + if (FFlag::LuauCompileCostModelConstants) + { + if (getNumber(node->from, from) && getNumber(node->to, to) && (!node->step || getNumber(node->step, step))) + tripCount = getTripCount(from, to, step); + } + else + { + if (getNumber_DEPRECATED(node->from, from) && getNumber_DEPRECATED(node->to, to) && + (!node->step || getNumber_DEPRECATED(node->step, step))) + tripCount = getTripCount(from, to, step); + } loop(node->body, 1, tripCount < 0 ? 3 : tripCount); return false; @@ -369,11 +388,31 @@ struct CostVisitor : AstVisitor return false; } + + bool getNumber(AstExpr* node, double& result) + { + if (const Constant* constant = constants.find(node)) + { + if (constant->type == Constant::Type_Number) + { + result = constant->valueNumber; + return true; + } + } + + return false; + } }; -uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap& builtins) +uint64_t modelCost( + AstNode* root, + AstLocal* const* vars, + size_t varCount, + const DenseHashMap& builtins, + const DenseHashMap& constants +) { - CostVisitor visitor{builtins}; + CostVisitor visitor{builtins, constants}; for (size_t i = 0; i < varCount && i < 7; ++i) visitor.vars[vars[i]] = 0xffull << (i * 8 + 8); @@ -382,6 +421,14 @@ uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const return visitor.result.model; } +uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount) +{ + DenseHashMap builtins{nullptr}; + DenseHashMap constants{nullptr}; + + return modelCost(root, vars, varCount, builtins, constants); +} + int computeCost(uint64_t model, const bool* varsConst, size_t varCount) { int cost = int(model & 0x7f); diff --git a/Compiler/src/CostModel.h b/Compiler/src/CostModel.h index e8f3e166..1021f152 100644 --- a/Compiler/src/CostModel.h +++ b/Compiler/src/CostModel.h @@ -9,8 +9,18 @@ namespace Luau namespace Compile { +struct Constant; + // cost model: 8 bytes, where first byte is the baseline cost, and the next 7 bytes are discounts for when variable #i is constant -uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap& builtins); +uint64_t modelCost( + AstNode* root, + AstLocal* const* vars, + size_t varCount, + const DenseHashMap& builtins, + const DenseHashMap& constants +); +// when additional data is not available, used to test the cost model +uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount); // cost is computed as B - sum(Di * Ci), where B is baseline cost, Di is the discount for each variable and Ci is 1 when variable #i is constant int computeCost(uint64_t model, const bool* varsConst, size_t varCount); diff --git a/Compiler/src/ValueTracking.cpp b/Compiler/src/ValueTracking.cpp index 616fca99..fde9beb8 100644 --- a/Compiler/src/ValueTracking.cpp +++ b/Compiler/src/ValueTracking.cpp @@ -3,6 +3,8 @@ #include "Luau/Lexer.h" +LUAU_FASTFLAG(LuauCompileInlineNonConstInit) + namespace Luau { namespace Compile @@ -80,6 +82,17 @@ struct ValueVisitor : AstVisitor return false; } + + bool visit(AstExprFunction* node) override + { + if (FFlag::LuauCompileInlineNonConstInit) + { + for (AstLocal* arg : node->args) + variables[arg].init = nullptr; + } + + return true; + } }; void assignMutable(DenseHashMap& globals, const AstNameTable& names, const char* const* mutableGlobals) diff --git a/VM/src/ldebug.cpp b/VM/src/ldebug.cpp index a9b26930..44da57c2 100644 --- a/VM/src/ldebug.cpp +++ b/VM/src/ldebug.cpp @@ -12,16 +12,11 @@ #include #include -LUAU_FASTFLAGVARIABLE(LuauCurrentLineBounds) - static const char* getfuncname(Closure* f); static int currentpc(lua_State* L, CallInfo* ci) { - if (FFlag::LuauCurrentLineBounds) - return pcRel(ci->savedpc, ci_func(ci)->l.p); - else - return pcRel_DEPRECATED(ci->savedpc, ci_func(ci)->l.p); + return pcRel(ci->savedpc, ci_func(ci)->l.p); } static int currentline(lua_State* L, CallInfo* ci) diff --git a/VM/src/ldebug.h b/VM/src/ldebug.h index 3e3006b0..3ff4d736 100644 --- a/VM/src/ldebug.h +++ b/VM/src/ldebug.h @@ -5,8 +5,6 @@ #include "lstate.h" #define pcRel(pc, p) ((pc) && (pc) != (p)->code ? cast_to(int, (pc) - (p)->code) - 1 : 0) -// TODO: remove with FFlagLuauCurrentLineBounds -#define pcRel_DEPRECATED(pc, p) ((pc) ? cast_to(int, (pc) - (p)->code) - 1 : 0) #define luaG_typeerror(L, o, opname) luaG_typeerrorL(L, o, opname) #define luaG_forerror(L, o, what) luaG_forerrorL(L, o, what) diff --git a/VM/src/lgcdebug.cpp b/VM/src/lgcdebug.cpp index 30800171..609d3ec3 100644 --- a/VM/src/lgcdebug.cpp +++ b/VM/src/lgcdebug.cpp @@ -14,7 +14,6 @@ #include #include -LUAU_FASTFLAG(LuauCurrentLineBounds) LUAU_FASTFLAGVARIABLE(LuauHeapNameDetails) static void validateobjref(global_State* g, GCObject* f, GCObject* t) @@ -465,7 +464,7 @@ static void dumpthread(FILE* f, lua_State* th) else if (isLua(ci)) { Proto* p = ci_func(ci)->l.p; - int pc = FFlag::LuauCurrentLineBounds ? pcRel(ci->savedpc, p) : pcRel_DEPRECATED(ci->savedpc, p); + int pc = pcRel(ci->savedpc, p); const LocVar* var = luaF_findlocal(p, int(v - ci->base), pc); if (var && var->varname) diff --git a/VM/src/lvmexecute.cpp b/VM/src/lvmexecute.cpp index ea409127..67765678 100644 --- a/VM/src/lvmexecute.cpp +++ b/VM/src/lvmexecute.cpp @@ -16,8 +16,6 @@ #include -LUAU_FASTFLAG(LuauCurrentLineBounds) - // Disable c99-designator to avoid the warning in CGOTO dispatch table #ifdef __clang__ #if __has_warning("-Wc99-designator") @@ -149,53 +147,27 @@ LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata) L->base = L->ci->base; } - if (FFlag::LuauCurrentLineBounds) - { - Closure* cl = clvalue(L->ci->func); + Closure* cl = clvalue(L->ci->func); - // note: the pc expectations of the hook are matching the general "pc points to next instruction" - // however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction - // this needs to be called before luaD_checkstack in case it fails to reallocate stack - const Instruction* oldsavedpc = L->ci->savedpc; + // note: the pc expectations of the hook are matching the general "pc points to next instruction" + // however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction + // this needs to be called before luaD_checkstack in case it fails to reallocate stack + const Instruction* oldsavedpc = L->ci->savedpc; - if (L->ci->savedpc && L->ci->savedpc != cl->l.p->code + cl->l.p->sizecode) - L->ci->savedpc++; + if (L->ci->savedpc && L->ci->savedpc != cl->l.p->code + cl->l.p->sizecode) + L->ci->savedpc++; - luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size - L->ci->top = L->top + LUA_MINSTACK; - LUAU_ASSERT(L->ci->top <= L->stack_last); + luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size + L->ci->top = L->top + LUA_MINSTACK; + LUAU_ASSERT(L->ci->top <= L->stack_last); - lua_Debug ar; - ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p)); - ar.userdata = userdata; + lua_Debug ar; + ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p)); + ar.userdata = userdata; - hook(L, &ar); + hook(L, &ar); - L->ci->savedpc = oldsavedpc; - } - else - { - // note: the pc expectations of the hook are matching the general "pc points to next instruction" - // however, for the hook to be able to continue execution from the same point, this is called with savedpc at the *current* instruction - // this needs to be called before luaD_checkstack in case it fails to reallocate stack - if (L->ci->savedpc) - L->ci->savedpc++; - - luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size - L->ci->top = L->top + LUA_MINSTACK; - LUAU_ASSERT(L->ci->top <= L->stack_last); - - Closure* cl = clvalue(L->ci->func); - - lua_Debug ar; - ar.currentline = cl->isC ? -1 : luaG_getline(cl->l.p, pcRel(L->ci->savedpc, cl->l.p)); - ar.userdata = userdata; - - hook(L, &ar); - - if (L->ci->savedpc) - L->ci->savedpc--; - } + L->ci->savedpc = oldsavedpc; L->ci->top = restorestack(L, ci_top); L->top = restorestack(L, top); diff --git a/VM/src/lvmload.cpp b/VM/src/lvmload.cpp index 10096798..fccccfee 100644 --- a/VM/src/lvmload.cpp +++ b/VM/src/lvmload.cpp @@ -13,8 +13,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauLoadNoOomThrow) - template struct TempBuffer { @@ -27,15 +25,6 @@ struct TempBuffer , data(NULL) , count(0) { - LUAU_ASSERT(FFlag::LuauLoadNoOomThrow); - } - - TempBuffer(lua_State* L, size_t count) - : L(L) - , data(luaM_newarray(L, count, T, 0)) - , count(count) - { - LUAU_ASSERT(!FFlag::LuauLoadNoOomThrow); } TempBuffer(const TempBuffer&) = delete; @@ -614,362 +603,8 @@ static int loadsafe( return 0; } -int luau_load_DEPRECATED(lua_State* L, const char* chunkname, const char* data, size_t size, int env) -{ - size_t offset = 0; - - uint8_t version = read(data, size, offset); - - - // 0 means the rest of the bytecode is the error message - if (version == 0) - { - char chunkbuf[LUA_IDSIZE]; - const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname)); - lua_pushfstring(L, "%s%.*s", chunkid, int(size - offset), data + offset); - return 1; - } - - if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX) - { - char chunkbuf[LUA_IDSIZE]; - const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname)); - lua_pushfstring(L, "%s: bytecode version mismatch (expected [%d..%d], got %d)", chunkid, LBC_VERSION_MIN, LBC_VERSION_MAX, version); - return 1; - } - - // we will allocate a fair amount of memory so check GC before we do - luaC_checkGC(L); - - // pause GC for the duration of deserialization - some objects we're creating aren't rooted - const ScopedSetGCThreshold pauseGC{L->global, SIZE_MAX}; - - // env is 0 for current environment and a stack index otherwise - LuaTable* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env)); - - TString* source = luaS_new(L, chunkname); - - uint8_t typesversion = 0; - - if (version >= 4) - { - typesversion = read(data, size, offset); - - if (typesversion < LBC_TYPE_VERSION_MIN || typesversion > LBC_TYPE_VERSION_MAX) - { - char chunkbuf[LUA_IDSIZE]; - const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname)); - lua_pushfstring( - L, "%s: bytecode type version mismatch (expected [%d..%d], got %d)", chunkid, LBC_TYPE_VERSION_MIN, LBC_TYPE_VERSION_MAX, typesversion - ); - return 1; - } - } - - // string table - unsigned int stringCount = readVarInt(data, size, offset); - TempBuffer strings(L, stringCount); - - for (unsigned int i = 0; i < stringCount; ++i) - { - unsigned int length = readVarInt(data, size, offset); - - strings[i] = luaS_newlstr(L, data + offset, length); - offset += length; - } - - // userdata type remapping table - // for unknown userdata types, the entry will remap to common 'userdata' type - const uint32_t userdataTypeLimit = LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE; - uint8_t userdataRemapping[userdataTypeLimit]; - - if (typesversion == 3) - { - memset(userdataRemapping, LBC_TYPE_USERDATA, userdataTypeLimit); - - uint8_t index = read(data, size, offset); - - while (index != 0) - { - TString* name = readString(strings, data, size, offset); - - if (uint32_t(index - 1) < userdataTypeLimit) - { - if (auto cb = L->global->ecb.gettypemapping) - userdataRemapping[index - 1] = cb(L, getstr(name), name->len); - } - - index = read(data, size, offset); - } - } - - // proto table - unsigned int protoCount = readVarInt(data, size, offset); - TempBuffer protos(L, protoCount); - - for (unsigned int i = 0; i < protoCount; ++i) - { - Proto* p = luaF_newproto(L); - p->source = source; - p->bytecodeid = int(i); - - p->maxstacksize = read(data, size, offset); - p->numparams = read(data, size, offset); - p->nups = read(data, size, offset); - p->is_vararg = read(data, size, offset); - - if (version >= 4) - { - p->flags = read(data, size, offset); - - if (typesversion == 1) - { - uint32_t typesize = readVarInt(data, size, offset); - - if (typesize) - { - uint8_t* types = (uint8_t*)data + offset; - - LUAU_ASSERT(typesize == unsigned(2 + p->numparams)); - LUAU_ASSERT(types[0] == LBC_TYPE_FUNCTION); - LUAU_ASSERT(types[1] == p->numparams); - - // transform v1 into v2 format - int headersize = typesize > 127 ? 4 : 3; - - p->typeinfo = luaM_newarray(L, headersize + typesize, uint8_t, p->memcat); - p->sizetypeinfo = headersize + typesize; - - if (headersize == 4) - { - p->typeinfo[0] = (typesize & 127) | (1 << 7); - p->typeinfo[1] = typesize >> 7; - p->typeinfo[2] = 0; - p->typeinfo[3] = 0; - } - else - { - p->typeinfo[0] = uint8_t(typesize); - p->typeinfo[1] = 0; - p->typeinfo[2] = 0; - } - - memcpy(p->typeinfo + headersize, types, typesize); - } - - offset += typesize; - } - else if (typesversion == 2 || typesversion == 3) - { - uint32_t typesize = readVarInt(data, size, offset); - - if (typesize) - { - uint8_t* types = (uint8_t*)data + offset; - - p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat); - p->sizetypeinfo = typesize; - memcpy(p->typeinfo, types, typesize); - offset += typesize; - - if (typesversion == 3) - { - remapUserdataTypes((char*)(uint8_t*)p->typeinfo, p->sizetypeinfo, userdataRemapping, userdataTypeLimit); - } - } - } - } - - const int sizecode = readVarInt(data, size, offset); - p->code = luaM_newarray(L, sizecode, Instruction, p->memcat); - p->sizecode = sizecode; - - for (int j = 0; j < p->sizecode; ++j) - p->code[j] = read(data, size, offset); - - p->codeentry = p->code; - - const int sizek = readVarInt(data, size, offset); - p->k = luaM_newarray(L, sizek, TValue, p->memcat); - p->sizek = sizek; - - // Initialize the constants to nil to ensure they have a valid state - // in the event that some operation in the following loop fails with - // an exception. - for (int j = 0; j < p->sizek; ++j) - { - setnilvalue(&p->k[j]); - } - - for (int j = 0; j < p->sizek; ++j) - { - switch (read(data, size, offset)) - { - case LBC_CONSTANT_NIL: - // All constants have already been pre-initialized to nil - break; - - case LBC_CONSTANT_BOOLEAN: - { - uint8_t v = read(data, size, offset); - setbvalue(&p->k[j], v); - break; - } - - case LBC_CONSTANT_NUMBER: - { - double v = read(data, size, offset); - setnvalue(&p->k[j], v); - break; - } - - case LBC_CONSTANT_VECTOR: - { - float x = read(data, size, offset); - float y = read(data, size, offset); - float z = read(data, size, offset); - float w = read(data, size, offset); - (void)w; - setvvalue(&p->k[j], x, y, z, w); - break; - } - - case LBC_CONSTANT_STRING: - { - TString* v = readString(strings, data, size, offset); - setsvalue(L, &p->k[j], v); - break; - } - - case LBC_CONSTANT_IMPORT: - { - uint32_t iid = read(data, size, offset); - resolveImportSafe(L, envt, p->k, iid); - setobj(L, &p->k[j], L->top - 1); - L->top--; - break; - } - - case LBC_CONSTANT_TABLE: - { - int keys = readVarInt(data, size, offset); - LuaTable* h = luaH_new(L, 0, keys); - for (int i = 0; i < keys; ++i) - { - int key = readVarInt(data, size, offset); - TValue* val = luaH_set(L, h, &p->k[key]); - setnvalue(val, 0.0); - } - sethvalue(L, &p->k[j], h); - break; - } - - case LBC_CONSTANT_CLOSURE: - { - uint32_t fid = readVarInt(data, size, offset); - Closure* cl = luaF_newLclosure(L, protos[fid]->nups, envt, protos[fid]); - cl->preload = (cl->nupvalues > 0); - setclvalue(L, &p->k[j], cl); - break; - } - - default: - LUAU_ASSERT(!"Unexpected constant kind"); - } - } - - const int sizep = readVarInt(data, size, offset); - p->p = luaM_newarray(L, sizep, Proto*, p->memcat); - p->sizep = sizep; - - for (int j = 0; j < p->sizep; ++j) - { - uint32_t fid = readVarInt(data, size, offset); - p->p[j] = protos[fid]; - } - - p->linedefined = readVarInt(data, size, offset); - p->debugname = readString(strings, data, size, offset); - - uint8_t lineinfo = read(data, size, offset); - - if (lineinfo) - { - p->linegaplog2 = read(data, size, offset); - - int intervals = ((p->sizecode - 1) >> p->linegaplog2) + 1; - int absoffset = (p->sizecode + 3) & ~3; - - const int sizelineinfo = absoffset + intervals * sizeof(int); - p->lineinfo = luaM_newarray(L, sizelineinfo, uint8_t, p->memcat); - p->sizelineinfo = sizelineinfo; - - p->abslineinfo = (int*)(p->lineinfo + absoffset); - - uint8_t lastoffset = 0; - for (int j = 0; j < p->sizecode; ++j) - { - lastoffset += read(data, size, offset); - p->lineinfo[j] = lastoffset; - } - - int lastline = 0; - for (int j = 0; j < intervals; ++j) - { - lastline += read(data, size, offset); - p->abslineinfo[j] = lastline; - } - } - - uint8_t debuginfo = read(data, size, offset); - - if (debuginfo) - { - const int sizelocvars = readVarInt(data, size, offset); - p->locvars = luaM_newarray(L, sizelocvars, LocVar, p->memcat); - p->sizelocvars = sizelocvars; - - for (int j = 0; j < p->sizelocvars; ++j) - { - p->locvars[j].varname = readString(strings, data, size, offset); - p->locvars[j].startpc = readVarInt(data, size, offset); - p->locvars[j].endpc = readVarInt(data, size, offset); - p->locvars[j].reg = read(data, size, offset); - } - - const int sizeupvalues = readVarInt(data, size, offset); - LUAU_ASSERT(sizeupvalues == p->nups); - - p->upvalues = luaM_newarray(L, sizeupvalues, TString*, p->memcat); - p->sizeupvalues = sizeupvalues; - - for (int j = 0; j < p->sizeupvalues; ++j) - { - p->upvalues[j] = readString(strings, data, size, offset); - } - } - - protos[i] = p; - } - - // "main" proto is pushed to Lua stack - uint32_t mainid = readVarInt(data, size, offset); - Proto* main = protos[mainid]; - - luaC_threadbarrier(L); - - Closure* cl = luaF_newLclosure(L, 0, envt, main); - setclvalue(L, L->top, cl); - incr_top(L); - - return 0; -} - int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size, int env) { - if (!FFlag::LuauLoadNoOomThrow) - return luau_load_DEPRECATED(L, chunkname, data, size, env); - // we will allocate a fair amount of memory so check GC before we do luaC_checkGC(L); diff --git a/fuzz/proto.cpp b/fuzz/proto.cpp index 787bd950..48ff2cdb 100644 --- a/fuzz/proto.cpp +++ b/fuzz/proto.cpp @@ -58,6 +58,8 @@ LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTFLAG(LuauSolverV2) +const double kTypecheckTimeoutSec = 4.0; + std::chrono::milliseconds kInterruptTimeout(10); std::chrono::time_point interruptDeadline; @@ -175,6 +177,19 @@ static void setupFrontend(Luau::Frontend& frontend) }; } +static Luau::FrontendOptions getFrontendOptions() +{ + Luau::FrontendOptions options; + + options.retainFullTypeGraphs = true; + options.forAutocomplete = false; + options.runLintChecks = kFuzzLinter; + + options.moduleTimeLimitSec = kTypecheckTimeoutSec; + + return options; +} + struct FuzzFileResolver : Luau::FileResolver { std::optional readSource(const Luau::ModuleName& name) override @@ -286,7 +301,7 @@ DEFINE_PROTO_FUZZER(const luau::ModuleSet& message) { static FuzzFileResolver fileResolver; static FuzzConfigResolver configResolver; - static Luau::FrontendOptions defaultOptions{/*retainFullTypeGraphs*/ true, /*forAutocomplete*/ false, /*runLintChecks*/ kFuzzLinter}; + static Luau::FrontendOptions defaultOptions = getFrontendOptions(); static Luau::Frontend frontend(&fileResolver, &configResolver, defaultOptions); static int once = (setupFrontend(frontend), 0); diff --git a/tests/AstJsonEncoder.test.cpp b/tests/AstJsonEncoder.test.cpp index 687b94c7..4c0de902 100644 --- a/tests/AstJsonEncoder.test.cpp +++ b/tests/AstJsonEncoder.test.cpp @@ -11,8 +11,6 @@ using namespace Luau; -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - struct JsonEncoderFixture { Allocator allocator; @@ -419,39 +417,19 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction") { AstStat* statement = expectParseStatement("declare function foo(x: number): string"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - std::string_view expected = - R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypePackExplicit","location":"0,33 - 0,39","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]}},"generics":[],"genericPacks":[]})"; - CHECK(toJson(statement) == expected); - } - else - { - std::string_view expected = - R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]},"generics":[],"genericPacks":[]})"; - - CHECK(toJson(statement) == expected); - } + std::string_view expected = + R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":false,"varargLocation":"0,0 - 0,0","retTypes":{"type":"AstTypePackExplicit","location":"0,33 - 0,39","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]}},"generics":[],"genericPacks":[]})"; + CHECK(toJson(statement) == expected); } TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2") { AstStat* statement = expectParseStatement("declare function foo(x: number, ...: string): string"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - std::string_view expected = - R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypePackExplicit","location":"0,46 - 0,52","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]}},"generics":[],"genericPacks":[]})"; + std::string_view expected = + R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypePackExplicit","location":"0,46 - 0,52","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]}},"generics":[],"genericPacks":[]})"; - CHECK(toJson(statement) == expected); - } - else - { - std::string_view expected = - R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","attributes":[],"name":"foo","nameLocation":"0,17 - 0,20","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}],"tailType":{"type":"AstTypePackVariadic","location":"0,37 - 0,43","variadicType":{"type":"AstTypeReference","location":"0,37 - 0,43","name":"string","nameLocation":"0,37 - 0,43","parameters":[]}}},"paramNames":[{"type":"AstArgumentName","name":"x","location":"0,21 - 0,22"}],"vararg":true,"varargLocation":"0,32 - 0,35","retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,46 - 0,52","name":"string","nameLocation":"0,46 - 0,52","parameters":[]}]},"generics":[],"genericPacks":[]})"; - - CHECK(toJson(statement) == expected); - } + CHECK(toJson(statement) == expected); } TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstAttr") @@ -479,18 +457,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass") REQUIRE(2 == root->body.size); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - std::string_view expected1 = - R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypePackExplicit","location":"3,48 - 3,54","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}},"location":"3,12 - 3,54"}],"indexer":null})"; - CHECK(toJson(root->body.data[0]) == expected1); - } - else - { - std::string_view expected1 = - R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}},"location":"3,12 - 3,54"}],"indexer":null})"; - CHECK(toJson(root->body.data[0]) == expected1); - } + std::string_view expected1 = + R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","nameLocation":"2,12 - 2,16","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]},"location":"2,12 - 2,24"},{"name":"method","nameLocation":"3,21 - 3,27","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,12 - 3,54","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,39 - 3,45","name":"number","nameLocation":"3,39 - 3,45","parameters":[]}]},"argNames":[{"type":"AstArgumentName","name":"foo","location":"3,34 - 3,37"}],"returnTypes":{"type":"AstTypePackExplicit","location":"3,48 - 3,54","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"3,48 - 3,54","name":"string","nameLocation":"3,48 - 3,54","parameters":[]}]}}},"location":"3,12 - 3,54"}],"indexer":null})"; + CHECK(toJson(root->body.data[0]) == expected1); std::string_view expected2 = R"({"type":"AstStatDeclareClass","location":"6,22 - 8,11","name":"Bar","superName":"Foo","props":[{"name":"prop2","nameLocation":"7,12 - 7,17","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"7,19 - 7,25","name":"string","nameLocation":"7,19 - 7,25","parameters":[]},"location":"7,12 - 7,25"}],"indexer":null})"; @@ -501,18 +470,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_annotation") { AstStat* statement = expectParseStatement("type T = ((number) -> (string | nil)) & ((string) -> ())"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - std::string_view expected = - R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,22 - 0,36","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,53 - 0,55","typeList":{"type":"AstTypeList","types":[]}}}}]},"exported":false})"; - CHECK(toJson(statement) == expected); - } - else - { - std::string_view expected = - R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypeList","types":[]}}}]},"exported":false})"; - CHECK(toJson(statement) == expected); - } + std::string_view expected = + R"({"type":"AstStatTypeAlias","location":"0,0 - 0,56","name":"T","generics":[],"genericPacks":[],"value":{"type":"AstTypeIntersection","location":"0,9 - 0,56","types":[{"type":"AstTypeGroup","location":"0,9 - 0,37","inner":{"type":"AstTypeFunction","location":"0,10 - 0,36","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,11 - 0,17","name":"number","nameLocation":"0,11 - 0,17","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,22 - 0,36","typeList":{"type":"AstTypeList","types":[{"type":"AstTypeGroup","location":"0,22 - 0,36","inner":{"type":"AstTypeUnion","location":"0,23 - 0,35","types":[{"type":"AstTypeReference","location":"0,23 - 0,29","name":"string","nameLocation":"0,23 - 0,29","parameters":[]},{"type":"AstTypeReference","location":"0,32 - 0,35","name":"nil","nameLocation":"0,32 - 0,35","parameters":[]}]}}]}}}},{"type":"AstTypeGroup","location":"0,40 - 0,56","inner":{"type":"AstTypeFunction","location":"0,41 - 0,55","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,42 - 0,48","name":"string","nameLocation":"0,42 - 0,48","parameters":[]}]},"argNames":[],"returnTypes":{"type":"AstTypePackExplicit","location":"0,53 - 0,55","typeList":{"type":"AstTypeList","types":[]}}}}]},"exported":false})"; + CHECK(toJson(statement) == expected); } TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_type_literal") @@ -541,18 +501,9 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeFunction") { AstStat* statement = expectParseStatement(R"(type fun = (string, bool, named: number) -> ())"); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - std::string_view expected = - R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypePackExplicit","location":"0,44 - 0,46","typeList":{"type":"AstTypeList","types":[]}}},"exported":false})"; - CHECK(toJson(statement) == expected); - } - else - { - std::string_view expected = - R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypeList","types":[]}},"exported":false})"; - CHECK(toJson(statement) == expected); - } + std::string_view expected = + R"({"type":"AstStatTypeAlias","location":"0,0 - 0,46","name":"fun","generics":[],"genericPacks":[],"value":{"type":"AstTypeFunction","location":"0,11 - 0,46","attributes":[],"generics":[],"genericPacks":[],"argTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,12 - 0,18","name":"string","nameLocation":"0,12 - 0,18","parameters":[]},{"type":"AstTypeReference","location":"0,20 - 0,24","name":"bool","nameLocation":"0,20 - 0,24","parameters":[]},{"type":"AstTypeReference","location":"0,33 - 0,39","name":"number","nameLocation":"0,33 - 0,39","parameters":[]}]},"argNames":[null,null,{"type":"AstArgumentName","name":"named","location":"0,26 - 0,31"}],"returnTypes":{"type":"AstTypePackExplicit","location":"0,44 - 0,46","typeList":{"type":"AstTypeList","types":[]}}},"exported":false})"; + CHECK(toJson(statement) == expected); } TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstTypeError") diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index 3a5e708f..bbe733dd 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -21,7 +21,7 @@ LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauExpectedTypeVisitor) -LUAU_FASTFLAG(LuauImplicitTableIndexerKeys) +LUAU_FASTFLAG(LuauImplicitTableIndexerKeys2) using namespace Luau; @@ -4621,7 +4621,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr") // Somewhat surprisingly, the old solver didn't cover this case. {FFlag::LuauSolverV2, true}, {FFlag::LuauExpectedTypeVisitor, true}, - {FFlag::LuauImplicitTableIndexerKeys, true}, + {FFlag::LuauImplicitTableIndexerKeys2, true}, }; check(R"( @@ -4636,8 +4636,62 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr") auto ac = autocomplete('1'); CHECK_EQ(ac.entryMap.count("A"), 1); + CHECK_EQ(ac.entryMap["A"].kind, AutocompleteEntryKind::String); CHECK_EQ(ac.entryMap.count("B"), 1); + CHECK_EQ(ac.entryMap["B"].kind, AutocompleteEntryKind::String); CHECK_EQ(ac.entryMap.count("C"), 1); + CHECK_EQ(ac.entryMap["C"].kind, AutocompleteEntryKind::String); +} + +TEST_CASE_FIXTURE(ACFixture, "autocomplete_implicit_named_index_index_expr_without_annotation") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauExpectedTypeVisitor, true}, + {FFlag::LuauImplicitTableIndexerKeys2, false}, + }; + + check(R"( + local foo = { + ["Item/Foo"] = 42, + ["Item/Bar"] = "it's true", + ["Item/Baz"] = true, + } + foo["@1"] + )"); + + auto ac = autocomplete('1'); + + auto checkEntry = [&](auto key, auto type) + { + REQUIRE_EQ(ac.entryMap.count(key), 1); + auto entry = ac.entryMap.at(key); + CHECK_EQ(entry.kind, AutocompleteEntryKind::Property); + REQUIRE(entry.type); + CHECK_EQ(type, toString(*entry.type)); + }; + + checkEntry("Item/Foo", "number"); + checkEntry("Item/Bar", "string"); + checkEntry("Item/Baz", "boolean"); +} + +TEST_CASE_FIXTURE(ACFixture, "bidirectional_autocomplete_in_function_call") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauExpectedTypeVisitor, true}, + }; + + check(R"( + local function take(_: { choice: "left" | "right" }) end + + take({ choice = "@1" }) + )"); + + auto ac = autocomplete('1'); + CHECK_EQ(ac.entryMap.count("left"), 1); + CHECK_EQ(ac.entryMap.count("right"), 1); } TEST_SUITE_END(); diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index d20041ae..db8ffdee 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -17,6 +17,7 @@ namespace Luau std::string rep(const std::string& s, size_t n); } +LUAU_FASTFLAG(LuauCompileInlineNonConstInit) LUAU_FASTINT(LuauCompileInlineDepth) LUAU_FASTINT(LuauCompileInlineThreshold) LUAU_FASTINT(LuauCompileInlineThresholdMaxBoost) @@ -24,6 +25,7 @@ LUAU_FASTINT(LuauCompileLoopUnrollThreshold) LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost) LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTFLAG(LuauCompileFixTypeFunctionSkip) +LUAU_FASTFLAG(LuauCompileCostModelConstants) using namespace Luau; @@ -121,6 +123,20 @@ static std::string compileTypeTable(const char* source) return bcb.dumpTypeInfo(); } +static std::string compileWithRemarks(const char* source) +{ + Luau::BytecodeBuilder bcb; + bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks); + bcb.setDumpSource(source); + + Luau::CompileOptions options; + options.optimizationLevel = 2; + + Luau::compileOrThrow(bcb, source, options); + + return bcb.dumpSourceRemarks(); +} + TEST_SUITE_BEGIN("Compiler"); TEST_CASE("BytecodeIsStable") @@ -3598,9 +3614,12 @@ RETURN R4 1 )"); } -TEST_CASE("SourceRemarks") +TEST_CASE("CostModelRemarks") { - const char* source = R"( + ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, true}; + + CHECK_EQ( + compileWithRemarks(R"( local a, b = ... local function foo(x) @@ -3608,20 +3627,8 @@ local function foo(x) end return foo(a) + foo(assert(b)) -)"; - - Luau::BytecodeBuilder bcb; - bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks); - bcb.setDumpSource(source); - - Luau::CompileOptions options; - options.optimizationLevel = 2; - - Luau::compileOrThrow(bcb, source, options); - - std::string remarks = bcb.dumpSourceRemarks(); - - CHECK_EQ(remarks, R"( +)"), + R"( local a, b = ... local function foo(x) @@ -3632,7 +3639,149 @@ end -- remark: builtin assert/1 -- remark: inlining succeeded (cost 2, profit 2.50x, depth 0) return foo(a) + foo(assert(b)) -)"); +)" + ); + + CHECK_EQ( + compileWithRemarks(R"( +local value = true + +local function foo() + return value +end + +return foo() +)"), + R"( +local value = true + +local function foo() + return value +end + +-- remark: inlining succeeded (cost 0, profit 3.00x, depth 0) +return foo() +)" + ); + + CHECK_EQ( + compileWithRemarks(R"( +local value = true + +local function foo() + return not value +end + +return foo() +)"), + R"( +local value = true + +local function foo() + return not value +end + +-- remark: inlining succeeded (cost 0, profit 3.00x, depth 0) +return foo() +)" + ); + + CHECK_EQ( + compileWithRemarks(R"( +local function foo() + local s = 0 + for i = 1, 100 do s += i end + return s +end + +return foo() +)"), + R"( +local function foo() + local s = 0 + -- remark: loop unroll failed: too many iterations (100) + for i = 1, 100 do s += i end + return s +end + +-- remark: inlining failed: too expensive (cost 127, profit 1.02x) +return foo() +)" + ); + + CHECK_EQ( + compileWithRemarks(R"( +local function foo() + local s = 0 + for i = 1, 4 * 25 do s += i end + return s +end + +return foo() +)"), + R"( +local function foo() + local s = 0 + -- remark: loop unroll failed: too many iterations (100) + for i = 1, 4 * 25 do s += i end + return s +end + +-- remark: inlining failed: too expensive (cost 127, profit 1.02x) +return foo() +)" + ); + + CHECK_EQ( + compileWithRemarks(R"( +local x = ... +local function test(a) + while a < 0 do + a += 1 + end + for i=10,1,-1 do + a += 1 + end + for i in pairs({}) do + a += 1 + if a % 2 == 0 then continue end + end + repeat + a += 1 + if a % 2 == 0 then break end + until a > 10 + return a +end +local a = test(x) +local b = test(2) +)"), + R"( +local x = ... +local function test(a) + while a < 0 do + a += 1 + end + -- remark: loop unroll succeeded (iterations 10, cost 10, profit 2.00x) + for i=10,1,-1 do + a += 1 + end + -- remark: allocation: table hash 0 + for i in pairs({}) do + a += 1 + if a % 2 == 0 then continue end + end + repeat + a += 1 + if a % 2 == 0 then break end + until a > 10 + return a +end +-- remark: inlining failed: too expensive (cost 76, profit 1.03x) +local a = test(x) +-- remark: inlining failed: too expensive (cost 73, profit 1.08x) +local b = test(2) +)" + ); } TEST_CASE("AssignmentConflict") @@ -7473,6 +7622,74 @@ RETURN R1 1 ); } +TEST_CASE("InlineNonConstInitializers") +{ + ScopedFastFlag luauCompileInlineNonConstInit{FFlag::LuauCompileInlineNonConstInit, true}; + + CHECK_EQ( + "\n" + compileFunction( + R"( +local function caller(f) + f(1) +end + +local function callback(n) + print(n + 5) +end + +caller(callback) +)", + 2, + 2 + ), + R"( +DUPCLOSURE R0 K0 ['caller'] +DUPCLOSURE R1 K1 ['callback'] +GETIMPORT R2 3 [print] +LOADN R3 6 +CALL R2 1 0 +RETURN R0 0 +)" + ); +} + +TEST_CASE("InlineNonConstInitializers2") +{ + ScopedFastFlag luauCompileInlineNonConstInit{FFlag::LuauCompileInlineNonConstInit, true}; + + CHECK_EQ( + "\n" + compileFunction( + R"( +local x, y, z = ... +local function test(a, b, c, comp) + return comp(a, b) and comp(b, c) +end + +local function greater(a, b) + return a > b +end + +test(x, y, z, greater) +)", + 2, + 2 + ), + R"( +GETVARARGS R0 3 +DUPCLOSURE R3 K0 ['test'] +DUPCLOSURE R4 K1 ['greater'] +JUMPIFLT R1 R0 L0 +LOADB R5 0 +1 +L0: LOADB R5 1 +L1: JUMPIFNOT R5 L3 +JUMPIFLT R2 R1 L2 +LOADB R5 0 +1 +L2: LOADB R5 1 +L3: RETURN R0 0 +)" + ); +} + TEST_CASE("ReturnConsecutive") { // we can return a single local directly diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 6c78885f..f5578a75 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -37,9 +37,8 @@ void luau_callhook(lua_State* L, lua_Hook hook, void* userdata); LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTFLAG(LuauYieldableContinuations) -LUAU_FASTFLAG(LuauCurrentLineBounds) -LUAU_FASTFLAG(LuauLoadNoOomThrow) LUAU_FASTFLAG(LuauHeapNameDetails) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_DYNAMIC_FASTFLAG(LuauGcAgainstOom) static lua_CompileOptions defaultOptions() @@ -1211,7 +1210,16 @@ static void populateRTTI(lua_State* L, Luau::TypeId type) for (const auto& [name, prop] : t->props) { - populateRTTI(L, prop.type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + if (prop.readTy) + populateRTTI(L, *prop.readTy); + else if (prop.writeTy) + populateRTTI(L, *prop.writeTy); + } + else + populateRTTI(L, prop.type_DEPRECATED()); + lua_setfield(L, -2, name.c_str()); } } @@ -1469,8 +1477,6 @@ TEST_CASE("Debugger") TEST_CASE("InterruptInspection") { - ScopedFastFlag luauCurrentLineBounds{FFlag::LuauCurrentLineBounds, true}; - static bool skipbreak = false; runConformance( @@ -1517,8 +1523,6 @@ TEST_CASE("InterruptInspection") TEST_CASE("InterruptErrorInspection") { - ScopedFastFlag luauCurrentLineBounds{FFlag::LuauCurrentLineBounds, true}; - // for easy access in no-capture lambda static int target = 0; static int step = 0; @@ -3237,8 +3241,6 @@ TEST_CASE("HugeFunction") TEST_CASE("HugeFunctionLoadFailure") { - ScopedFastFlag luauLoadNoOomThrow{FFlag::LuauLoadNoOomThrow, true}; - // This test case verifies that if an out-of-memory error occurs inside of // luau_load, we are not left with any GC objects in inconsistent states // that would cause issues during garbage collection. diff --git a/tests/CostModel.test.cpp b/tests/CostModel.test.cpp index 29fffb4f..4abd69fc 100644 --- a/tests/CostModel.test.cpp +++ b/tests/CostModel.test.cpp @@ -5,6 +5,8 @@ #include "doctest.h" +LUAU_FASTFLAG(LuauCompileCostModelConstants) + using namespace Luau; namespace Luau @@ -12,7 +14,7 @@ namespace Luau namespace Compile { -uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount, const DenseHashMap& builtins); +uint64_t modelCost(AstNode* root, AstLocal* const* vars, size_t varCount); int computeCost(uint64_t model, const bool* varsConst, size_t varCount); } // namespace Compile @@ -31,7 +33,7 @@ static uint64_t modelFunction(const char* source) AstStatFunction* func = result.root->body.data[0]->as(); REQUIRE(func); - return Luau::Compile::modelCost(func->func->body, func->func->args.data, func->func->args.size, DenseHashMap{nullptr}); + return Luau::Compile::modelCost(func->func->body, func->func->args.data, func->func->args.size); } TEST_CASE("Expression") @@ -133,6 +135,8 @@ end TEST_CASE("ControlFlow") { + ScopedFastFlag luauCompileCostModelConstants{FFlag::LuauCompileCostModelConstants, false}; + uint64_t model = modelFunction(R"( function test(a) while a < 0 do diff --git a/tests/Module.test.cpp b/tests/Module.test.cpp index 47132350..4b40e248 100644 --- a/tests/Module.test.cpp +++ b/tests/Module.test.cpp @@ -11,9 +11,11 @@ using namespace Luau; -LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTFLAG(DebugLuauFreezeArena); -LUAU_FASTINT(LuauTypeCloneIterationLimit); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(DebugLuauFreezeArena) +LUAU_FASTINT(LuauTypeCloneIterationLimit) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) + TEST_SUITE_BEGIN("ModuleTests"); TEST_CASE_FIXTURE(Fixture, "is_within_comment") @@ -136,7 +138,8 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table") CHECK_EQ(std::optional{"Cyclic"}, ttv->syntheticName); - TypeId methodType = ttv->props["get"].type(); + + TypeId methodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ttv->props["get"].readTy : ttv->props["get"].type_DEPRECATED(); REQUIRE(methodType != nullptr); const FunctionType* ftv = get(methodType); @@ -169,7 +172,7 @@ TEST_CASE_FIXTURE(Fixture, "deepClone_cyclic_table_2") TableType* ctt = getMutable(cloneTy); REQUIRE(ctt); - TypeId clonedMethodType = ctt->props["get"].type(); + TypeId clonedMethodType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *ctt->props["get"].readTy : ctt->props["get"].type_DEPRECATED(); REQUIRE(clonedMethodType); const FunctionType* cmf = get(clonedMethodType); @@ -198,7 +201,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "builtin_types_point_into_globalTypes_arena") TableType* exportsTable = getMutable(*exports); REQUIRE(exportsTable != nullptr); - TypeId signType = exportsTable->props["sign"].type(); + TypeId signType = FFlag::LuauRemoveTypeCallsForReadWriteProps ? *exportsTable->props["sign"].readTy : exportsTable->props["sign"].type_DEPRECATED(); REQUIRE(signType != nullptr); CHECK(!isInArena(signType, module->interfaceTypes)); @@ -352,8 +355,17 @@ TEST_CASE_FIXTURE(Fixture, "clone_iteration_limit") for (int i = 0; i < nesting; i++) { TableType* ttv = getMutable(nested); - ttv->props["a"].setType(src.addType(TableType{})); - nested = ttv->props["a"].type(); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + ttv->props["a"].readTy = src.addType(TableType{}); + ttv->props["a"].writeTy = ttv->props["a"].readTy; + nested = *ttv->props["a"].readTy; + } + else + { + ttv->props["a"].setType(src.addType(TableType{})); + nested = ttv->props["a"].type_DEPRECATED(); + } } TypeArena dest; @@ -444,7 +456,10 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_reexports") TypeId typeB = modBiter->second.type; TableType* tableB = getMutable(typeB); REQUIRE(tableB); - CHECK(typeA == tableB->props["q"].type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + CHECK(typeA == tableB->props["q"].readTy); + else + CHECK(typeA == tableB->props["q"].type_DEPRECATED()); } TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values") @@ -478,7 +493,13 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "do_not_clone_types_of_reexported_values") TableType* tableB = getMutable(*typeB); REQUIRE_MESSAGE(tableB, "Expected a table, but got " << toString(*typeB)); - CHECK(tableA->props["a"].type() == tableB->props["b"].type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + CHECK(tableA->props["a"].readTy == tableB->props["b"].readTy); + CHECK(tableA->props["a"].writeTy == tableB->props["b"].writeTy); + } + else + CHECK(tableA->props["a"].type_DEPRECATED() == tableB->props["b"].type_DEPRECATED()); } TEST_CASE_FIXTURE(BuiltinsFixture, "clone_table_bound_to_table_bound_to_table") diff --git a/tests/NonstrictMode.test.cpp b/tests/NonstrictMode.test.cpp index 9fcf5f55..ed39df31 100644 --- a/tests/NonstrictMode.test.cpp +++ b/tests/NonstrictMode.test.cpp @@ -177,7 +177,7 @@ TEST_CASE_FIXTURE(Fixture, "table_props_are_any") REQUIRE(ttv != nullptr); REQUIRE(ttv->props.count("foo")); - TypeId fooProp = ttv->props["foo"].type(); + TypeId fooProp = ttv->props["foo"].type_DEPRECATED(); REQUIRE(fooProp != nullptr); CHECK_EQ(*fooProp, *getBuiltins()->anyType); @@ -200,9 +200,9 @@ TEST_CASE_FIXTURE(Fixture, "inline_table_props_are_also_any") TableType* ttv = getMutable(requireType("T")); REQUIRE_MESSAGE(ttv, "Should be a table: " << toString(requireType("T"))); - CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type()); - CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type()); - CHECK_MESSAGE(get(follow(ttv->props["three"].type())), "Should be a function: " << *ttv->props["three"].type()); + CHECK_EQ(*getBuiltins()->anyType, *ttv->props["one"].type_DEPRECATED()); + CHECK_EQ(*getBuiltins()->anyType, *ttv->props["two"].type_DEPRECATED()); + CHECK_MESSAGE(get(follow(ttv->props["three"].type_DEPRECATED())), "Should be a function: " << *ttv->props["three"].type_DEPRECATED()); } TEST_CASE_FIXTURE(BuiltinsFixture, "for_in_iterator_variables_are_any") diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index 411bebdc..7640b2fd 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -17,10 +17,8 @@ LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTINT(LuauTypeLengthLimit) LUAU_FASTINT(LuauParseErrorLimit) LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauParseStringIndexer) LUAU_FASTFLAG(LuauDeclareExternType) -LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_DYNAMIC_FASTFLAG(DebugLuauReportReturnTypeVariadicWithTypeSuffix) // Clip with DebugLuauReportReturnTypeVariadicWithTypeSuffix @@ -152,20 +150,11 @@ TEST_CASE_FIXTURE(Fixture, "functions_can_have_return_annotations") AstStatFunction* statFunction = block->body.data[0]->as(); REQUIRE(statFunction != nullptr); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - REQUIRE(statFunction->func->returnAnnotation); - auto typePack = statFunction->func->returnAnnotation->as(); - REQUIRE(typePack); - CHECK_EQ(typePack->typeList.types.size, 1); - CHECK(typePack->typeList.tailType == nullptr); - } - else - { - REQUIRE(statFunction->func->returnAnnotation_DEPRECATED.has_value()); - CHECK_EQ(statFunction->func->returnAnnotation_DEPRECATED->types.size, 1); - CHECK(statFunction->func->returnAnnotation_DEPRECATED->tailType == nullptr); - } + REQUIRE(statFunction->func->returnAnnotation); + auto typePack = statFunction->func->returnAnnotation->as(); + REQUIRE(typePack); + CHECK_EQ(typePack->typeList.types.size, 1); + CHECK(typePack->typeList.tailType == nullptr); } TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation") @@ -180,28 +169,15 @@ TEST_CASE_FIXTURE(Fixture, "functions_can_have_a_function_type_annotation") AstStatFunction* statFunc = block->body.data[0]->as(); REQUIRE(statFunc != nullptr); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - REQUIRE(statFunc->func->returnAnnotation); - auto typePack = statFunc->func->returnAnnotation->as(); - REQUIRE(typePack); - CHECK(typePack->typeList.tailType == nullptr); - AstArray& retTypes = typePack->typeList.types; - REQUIRE(retTypes.size == 1); + REQUIRE(statFunc->func->returnAnnotation); + auto typePack = statFunc->func->returnAnnotation->as(); + REQUIRE(typePack); + CHECK(typePack->typeList.tailType == nullptr); + AstArray& retTypes = typePack->typeList.types; + REQUIRE(retTypes.size == 1); - AstTypeFunction* funTy = retTypes.data[0]->as(); - REQUIRE(funTy != nullptr); - } - else - { - REQUIRE(statFunc->func->returnAnnotation_DEPRECATED.has_value()); - CHECK(statFunc->func->returnAnnotation_DEPRECATED->tailType == nullptr); - AstArray& retTypes = statFunc->func->returnAnnotation_DEPRECATED->types; - REQUIRE(retTypes.size == 1); - - AstTypeFunction* funTy = retTypes.data[0]->as(); - REQUIRE(funTy != nullptr); - } + AstTypeFunction* funTy = retTypes.data[0]->as(); + REQUIRE(funTy != nullptr); } TEST_CASE_FIXTURE(Fixture, "function_return_type_should_disambiguate_from_function_type_and_multiple_returns") @@ -216,38 +192,20 @@ TEST_CASE_FIXTURE(Fixture, "function_return_type_should_disambiguate_from_functi AstStatFunction* statFunc = block->body.data[0]->as(); REQUIRE(statFunc != nullptr); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - REQUIRE(statFunc->func->returnAnnotation); - auto typePack = statFunc->func->returnAnnotation->as(); - REQUIRE(typePack); - CHECK(typePack->typeList.tailType == nullptr); - AstArray& retTypes = typePack->typeList.types; - REQUIRE(retTypes.size == 2); + REQUIRE(statFunc->func->returnAnnotation); + auto typePack = statFunc->func->returnAnnotation->as(); + REQUIRE(typePack); + CHECK(typePack->typeList.tailType == nullptr); + AstArray& retTypes = typePack->typeList.types; + REQUIRE(retTypes.size == 2); - AstTypeReference* ty0 = retTypes.data[0]->as(); - REQUIRE(ty0 != nullptr); - REQUIRE(ty0->name == "number"); + AstTypeReference* ty0 = retTypes.data[0]->as(); + REQUIRE(ty0 != nullptr); + REQUIRE(ty0->name == "number"); - AstTypeReference* ty1 = retTypes.data[1]->as(); - REQUIRE(ty1 != nullptr); - REQUIRE(ty1->name == "string"); - } - else - { - REQUIRE(statFunc->func->returnAnnotation_DEPRECATED.has_value()); - CHECK(statFunc->func->returnAnnotation_DEPRECATED->tailType == nullptr); - AstArray& retTypes = statFunc->func->returnAnnotation_DEPRECATED->types; - REQUIRE(retTypes.size == 2); - - AstTypeReference* ty0 = retTypes.data[0]->as(); - REQUIRE(ty0 != nullptr); - REQUIRE(ty0->name == "number"); - - AstTypeReference* ty1 = retTypes.data[1]->as(); - REQUIRE(ty1 != nullptr); - REQUIRE(ty1->name == "string"); - } + AstTypeReference* ty1 = retTypes.data[1]->as(); + REQUIRE(ty1 != nullptr); + REQUIRE(ty1->name == "string"); } TEST_CASE_FIXTURE(Fixture, "function_return_type_should_parse_as_function_type_annotation_with_no_args") @@ -262,45 +220,25 @@ TEST_CASE_FIXTURE(Fixture, "function_return_type_should_parse_as_function_type_a AstStatFunction* statFunc = block->body.data[0]->as(); REQUIRE(statFunc != nullptr); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - REQUIRE(statFunc->func->returnAnnotation); - auto typePack = statFunc->func->returnAnnotation->as(); - REQUIRE(typePack); - CHECK(typePack->typeList.tailType == nullptr); - AstArray& retTypes = typePack->typeList.types; - REQUIRE(retTypes.size == 1); + REQUIRE(statFunc->func->returnAnnotation); + auto typePack = statFunc->func->returnAnnotation->as(); + REQUIRE(typePack); + CHECK(typePack->typeList.tailType == nullptr); + AstArray& retTypes = typePack->typeList.types; + REQUIRE(retTypes.size == 1); - AstTypeFunction* funTy = retTypes.data[0]->as(); - REQUIRE(funTy != nullptr); - REQUIRE(funTy->argTypes.types.size == 0); - CHECK(funTy->argTypes.tailType == nullptr); + AstTypeFunction* funTy = retTypes.data[0]->as(); + REQUIRE(funTy != nullptr); + REQUIRE(funTy->argTypes.types.size == 0); + CHECK(funTy->argTypes.tailType == nullptr); - auto funReturnPack = funTy->returnTypes->as(); - REQUIRE(funReturnPack); - CHECK(funReturnPack->typeList.tailType == nullptr); + auto funReturnPack = funTy->returnTypes->as(); + REQUIRE(funReturnPack); + CHECK(funReturnPack->typeList.tailType == nullptr); - AstTypeReference* ty = funReturnPack->typeList.types.data[0]->as(); - REQUIRE(ty != nullptr); - REQUIRE(ty->name == "nil"); - } - else - { - REQUIRE(statFunc->func->returnAnnotation_DEPRECATED.has_value()); - CHECK(statFunc->func->returnAnnotation_DEPRECATED->tailType == nullptr); - AstArray& retTypes = statFunc->func->returnAnnotation_DEPRECATED->types; - REQUIRE(retTypes.size == 1); - - AstTypeFunction* funTy = retTypes.data[0]->as(); - REQUIRE(funTy != nullptr); - REQUIRE(funTy->argTypes.types.size == 0); - CHECK(funTy->argTypes.tailType == nullptr); - CHECK(funTy->returnTypes_DEPRECATED.tailType == nullptr); - - AstTypeReference* ty = funTy->returnTypes_DEPRECATED.types.data[0]->as(); - REQUIRE(ty != nullptr); - REQUIRE(ty->name == "nil"); - } + AstTypeReference* ty = funReturnPack->typeList.types.data[0]->as(); + REQUIRE(ty != nullptr); + REQUIRE(ty->name == "nil"); } TEST_CASE_FIXTURE(Fixture, "annotations_can_be_tables") @@ -442,17 +380,9 @@ TEST_CASE_FIXTURE(Fixture, "return_type_is_an_intersection_type_if_led_with_one_ AstTypeFunction* annotation = local->vars.data[0]->annotation->as(); REQUIRE(annotation != nullptr); - AstTypeIntersection* returnAnnotation; - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - auto returnTypePack = annotation->returnTypes->as(); - REQUIRE(returnTypePack); - returnAnnotation = returnTypePack->typeList.types.data[0]->as(); - } - else - { - returnAnnotation = annotation->returnTypes_DEPRECATED.types.data[0]->as(); - } + auto returnTypePack = annotation->returnTypes->as(); + REQUIRE(returnTypePack); + AstTypeIntersection* returnAnnotation = returnTypePack->typeList.types.data[0]->as(); REQUIRE(returnAnnotation != nullptr); CHECK(returnAnnotation->types.data[0]->as()); CHECK(returnAnnotation->types.data[1]->as()); @@ -2000,16 +1930,10 @@ TEST_CASE_FIXTURE(Fixture, "parse_declarations") CHECK(func->name == "bar"); CHECK(func->nameLocation == Location({2, 25}, {2, 28})); REQUIRE_EQ(func->params.types.size, 1); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - auto retTypePack = func->retTypes->as(); - REQUIRE(retTypePack); - REQUIRE_EQ(retTypePack->typeList.types.size, 1); - } - else - { - REQUIRE_EQ(func->retTypes_DEPRECATED.types.size, 1); - } + + auto retTypePack = func->retTypes->as(); + REQUIRE(retTypePack); + REQUIRE_EQ(retTypePack->typeList.types.size, 1); AstStatDeclareFunction* varFunc = stat->body.data[2]->as(); REQUIRE(varFunc); @@ -2392,15 +2316,7 @@ TEST_CASE_FIXTURE(Fixture, "parse_variadics") REQUIRE(fnFoo); CHECK_EQ(fnFoo->argTypes.types.size, 2); CHECK(fnFoo->argTypes.tailType); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - CHECK(fnFoo->returnTypes->is()); - } - else - { - CHECK_EQ(fnFoo->returnTypes_DEPRECATED.types.size, 0); - CHECK(fnFoo->returnTypes_DEPRECATED.tailType); - } + CHECK(fnFoo->returnTypes->is()); AstStatTypeAlias* bar = stat->body.data[2]->as(); REQUIRE(bar); @@ -2408,18 +2324,10 @@ TEST_CASE_FIXTURE(Fixture, "parse_variadics") REQUIRE(fnBar); CHECK_EQ(fnBar->argTypes.types.size, 0); CHECK(!fnBar->argTypes.tailType); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - auto returnTypePack = fnBar->returnTypes->as(); - REQUIRE(returnTypePack); - CHECK_EQ(returnTypePack->typeList.types.size, 1); - CHECK(returnTypePack->typeList.tailType); - } - else - { - CHECK_EQ(fnBar->returnTypes_DEPRECATED.types.size, 1); - CHECK(fnBar->returnTypes_DEPRECATED.tailType); - } + auto returnTypePack = fnBar->returnTypes->as(); + REQUIRE(returnTypePack); + CHECK_EQ(returnTypePack->typeList.types.size, 1); + CHECK(returnTypePack->typeList.tailType); } TEST_CASE_FIXTURE(Fixture, "variadics_must_be_last") @@ -2485,8 +2393,7 @@ TEST_CASE_FIXTURE(Fixture, "generic_pack_parsing") REQUIRE(argAnnot != nullptr); CHECK(argAnnot->genericName == "a"); - AstTypePackGeneric* retAnnot = FFlag::LuauStoreReturnTypesAsPackOnAst ? fnTy->returnTypes->as() - : fnTy->returnTypes_DEPRECATED.tailType->as(); + AstTypePackGeneric* retAnnot = fnTy->returnTypes->as(); REQUIRE(retAnnot != nullptr); CHECK(retAnnot->genericName == "b"); } @@ -2571,9 +2478,7 @@ TEST_CASE_FIXTURE(Fixture, "function_type_named_arguments") REQUIRE_EQ(func->argNames.size, 3); REQUIRE(func->argNames.data[2]); CHECK_EQ(func->argNames.data[2]->first, "c"); - AstTypeFunction* funcRet = FFlag::LuauStoreReturnTypesAsPackOnAst - ? func->returnTypes->as()->typeList.types.data[0]->as() - : func->returnTypes_DEPRECATED.types.data[0]->as(); + AstTypeFunction* funcRet = func->returnTypes->as()->typeList.types.data[0]->as(); REQUIRE(funcRet != nullptr); REQUIRE_EQ(funcRet->argTypes.types.size, 3); REQUIRE_EQ(funcRet->argNames.size, 3); @@ -2817,20 +2722,11 @@ TEST_CASE_FIXTURE(Fixture, "parse_return_type_ast_type_group") auto funcType = alias1->type->as(); REQUIRE(funcType); - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - auto returnTypePack = funcType->returnTypes->as(); - REQUIRE(returnTypePack); - REQUIRE_EQ(1, returnTypePack->typeList.types.size); - REQUIRE(!returnTypePack->typeList.tailType); - CHECK(returnTypePack->typeList.types.data[0]->is()); - } - else - { - REQUIRE_EQ(1, funcType->returnTypes_DEPRECATED.types.size); - REQUIRE(!funcType->returnTypes_DEPRECATED.tailType); - CHECK(funcType->returnTypes_DEPRECATED.types.data[0]->is()); - } + auto returnTypePack = funcType->returnTypes->as(); + REQUIRE(returnTypePack); + REQUIRE_EQ(1, returnTypePack->typeList.types.size); + REQUIRE(!returnTypePack->typeList.tailType); + CHECK(returnTypePack->typeList.types.data[0]->is()); } TEST_CASE_FIXTURE(Fixture, "inner_and_outer_scope_of_functions_have_correct_end_position") @@ -2899,8 +2795,6 @@ TEST_CASE_FIXTURE(Fixture, "function_start_locations_are_before_attributes") TEST_CASE_FIXTURE(Fixture, "for_loop_with_single_var_has_comma_positions_of_size_zero") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; - ParseOptions parseOptions; parseOptions.storeCstData = true; diff --git a/tests/RequireTracer.test.cpp b/tests/RequireTracer.test.cpp index c4d2a312..aa66b100 100644 --- a/tests/RequireTracer.test.cpp +++ b/tests/RequireTracer.test.cpp @@ -6,8 +6,6 @@ #include "doctest.h" -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) - using namespace Luau; namespace @@ -177,24 +175,13 @@ TEST_CASE_FIXTURE(RequireTracerFixture, "follow_typeof_in_return_type") AstStatFunction* func = block->body.data[0]->as(); REQUIRE(func != nullptr); - AstTypeTypeof* typeofAnnotation; - if (FFlag::LuauStoreReturnTypesAsPackOnAst) - { - AstTypePack* retAnnotation = func->func->returnAnnotation; - REQUIRE(retAnnotation); + AstTypePack* retAnnotation = func->func->returnAnnotation; + REQUIRE(retAnnotation); - AstTypePackExplicit* tp = retAnnotation->as(); - REQUIRE(tp); - REQUIRE_EQ(tp->typeList.types.size, 1); - typeofAnnotation = tp->typeList.types.data[0]->as(); - } - else - { - std::optional retAnnotation = func->func->returnAnnotation_DEPRECATED; - REQUIRE(retAnnotation); - REQUIRE_EQ(retAnnotation->types.size, 1); - typeofAnnotation = retAnnotation->types.data[0]->as(); - } + AstTypePackExplicit* tp = retAnnotation->as(); + REQUIRE(tp); + REQUIRE_EQ(tp->typeList.types.size, 1); + AstTypeTypeof* typeofAnnotation = tp->typeList.types.data[0]->as(); REQUIRE(typeofAnnotation != nullptr); AstExprIndexName* indexName = typeofAnnotation->expr->as(); diff --git a/tests/Transpiler.test.cpp b/tests/Transpiler.test.cpp index 65c43b7c..0ab36246 100644 --- a/tests/Transpiler.test.cpp +++ b/tests/Transpiler.test.cpp @@ -12,8 +12,6 @@ using namespace Luau; -LUAU_FASTFLAG(LuauStoreCSTData2) -LUAU_FASTFLAG(LuauStoreReturnTypesAsPackOnAst) LUAU_FASTFLAG(LuauStoreLocalAnnotationColonPositions) LUAU_FASTFLAG(LuauCSTForReturnTypeFunctionTail) @@ -49,7 +47,6 @@ TEST_CASE("string_literals_containing_utf8") TEST_CASE("if_stmt_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( if This then Once() end)"; CHECK_EQ(one, transpile(one).code); @@ -98,31 +95,15 @@ TEST_CASE("elseif_chains_indent_sensibly") TEST_CASE("strips_type_annotations") { const std::string code = R"( local s: string= 'hello there' )"; - if (FFlag::LuauStoreCSTData2) - { - const std::string expected = R"( local s = 'hello there' )"; - CHECK_EQ(expected, transpile(code).code); - } - else - { - const std::string expected = R"( local s = 'hello there' )"; - CHECK_EQ(expected, transpile(code).code); - } + const std::string expected = R"( local s = 'hello there' )"; + CHECK_EQ(expected, transpile(code).code); } TEST_CASE("strips_type_assertion_expressions") { const std::string code = R"( local s= some_function() :: any+ something_else() :: number )"; - if (FFlag::LuauStoreCSTData2) - { - const std::string expected = R"( local s= some_function() + something_else() )"; - CHECK_EQ(expected, transpile(code).code); - } - else - { - const std::string expected = R"( local s= some_function() + something_else() )"; - CHECK_EQ(expected, transpile(code).code); - } + const std::string expected = R"( local s= some_function() + something_else() )"; + CHECK_EQ(expected, transpile(code).code); } TEST_CASE("function_taking_ellipsis") @@ -149,7 +130,6 @@ TEST_CASE("for_loop") TEST_CASE("for_loop_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( for index = 1, 10 do call(index) end )"; CHECK_EQ(one, transpile(one).code); @@ -174,7 +154,6 @@ TEST_CASE("for_in_loop") TEST_CASE("for_in_loop_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( for k, v in ipairs(x) do end )"; CHECK_EQ(one, transpile(one).code); @@ -193,7 +172,6 @@ TEST_CASE("for_in_loop_spaces_around_tokens") TEST_CASE("for_in_single_variable") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( for key in pairs(x) do end )"; CHECK_EQ(one, transpile(one).code); } @@ -206,7 +184,6 @@ TEST_CASE("while_loop") TEST_CASE("while_loop_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( while f(x) do print() end )"; CHECK_EQ(one, transpile(one).code); @@ -228,7 +205,6 @@ TEST_CASE("repeat_until_loop") TEST_CASE("repeat_until_loop_condition_on_new_line") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( repeat print() @@ -260,7 +236,6 @@ TEST_CASE("local_assignment") TEST_CASE("local_assignment_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( local x = 1 )"; CHECK_EQ(one, transpile(one).code); @@ -294,7 +269,6 @@ TEST_CASE("local_function") TEST_CASE("local_function_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( local function p(o, m, ...) end )"; CHECK_EQ(one, transpile(one).code); @@ -313,7 +287,6 @@ TEST_CASE("function") TEST_CASE("function_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string two = R"( function p(o, m, ...) end )"; CHECK_EQ(two, transpile(two).code); @@ -342,8 +315,6 @@ TEST_CASE("function_spaces_around_tokens") TEST_CASE("function_with_types_spaces_around_tokens") { ScopedFastFlag sffs[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true}, }; std::string code = R"( function p(o: string, m: number, ...: any): string end )"; @@ -403,7 +374,6 @@ TEST_CASE("function_with_types_spaces_around_tokens") TEST_CASE("returns_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string one = R"( return 1 )"; CHECK_EQ(one, transpile(one).code); @@ -416,7 +386,6 @@ TEST_CASE("returns_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = string )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -465,7 +434,6 @@ TEST_CASE_FIXTURE(Fixture, "type_alias_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "type_alias_with_defaults_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = string )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -526,7 +494,6 @@ TEST_CASE("table_literal_closing_brace_at_correct_position") TEST_CASE("table_literal_with_semicolon_separators") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1; y = 2 } )"; @@ -536,7 +503,6 @@ TEST_CASE("table_literal_with_semicolon_separators") TEST_CASE("table_literal_with_trailing_separators") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1, y = 2, } )"; @@ -546,7 +512,6 @@ TEST_CASE("table_literal_with_trailing_separators") TEST_CASE("table_literal_with_spaces_around_separator") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1 , y = 2 } )"; @@ -556,7 +521,6 @@ TEST_CASE("table_literal_with_spaces_around_separator") TEST_CASE("table_literal_with_spaces_around_equals") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { x = 1 } )"; @@ -566,7 +530,6 @@ TEST_CASE("table_literal_with_spaces_around_equals") TEST_CASE("table_literal_multiline_with_indexers") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local t = { ["my first value"] = "x"; @@ -591,18 +554,8 @@ TEST_CASE("method_definitions") TEST_CASE("spaces_between_keywords_even_if_it_pushes_the_line_estimation_off") { - // Luau::Parser doesn't exactly preserve the string representation of numbers in Lua, so we can find ourselves - // falling out of sync with the original code. We need to push keywords out so that there's at least one space between them. const std::string code = R"( if math.abs(raySlope) < .01 then return 0 end )"; - if (FFlag::LuauStoreCSTData2) - { - CHECK_EQ(code, transpile(code).code); - } - else - { - const std::string expected = R"( if math.abs(raySlope) < 0.01 then return 0 end)"; - CHECK_EQ(expected, transpile(code).code); - } + CHECK_EQ(code, transpile(code).code); } TEST_CASE("numbers") @@ -614,34 +567,23 @@ TEST_CASE("numbers") TEST_CASE("infinity") { const std::string code = R"( local a = 1e500 local b = 1e400 )"; - if (FFlag::LuauStoreCSTData2) - { - CHECK_EQ(code, transpile(code).code); - } - else - { - const std::string expected = R"( local a = 1e500 local b = 1e500 )"; - CHECK_EQ(expected, transpile(code).code); - } + CHECK_EQ(code, transpile(code).code); } TEST_CASE("numbers_with_separators") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = 123_456_789 )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("hexadecimal_numbers") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = 0xFFFF )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("binary_numbers") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = 0b0101 )"; CHECK_EQ(code, transpile(code).code); } @@ -654,28 +596,24 @@ TEST_CASE("single_quoted_strings") TEST_CASE("double_quoted_strings") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = "hello world" )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("simple_interp_string") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = `hello world` )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("raw_strings") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = [[ hello world ]] )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("raw_strings_with_blocks") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local a = [==[ hello world ]==] )"; CHECK_EQ(code, transpile(code).code); } @@ -694,7 +632,6 @@ TEST_CASE("escaped_strings_2") TEST_CASE("escaped_strings_newline") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( print("foo \ bar") @@ -704,14 +641,12 @@ TEST_CASE("escaped_strings_newline") TEST_CASE("escaped_strings_raw") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( local x = [=[\v<((do|load)file|require)\s*\(?['"]\zs[^'"]+\ze['"]]=] )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("position_correctly_updated_when_writing_multiline_string") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call([[ testing @@ -757,56 +692,48 @@ TEST_CASE("function_call_parentheses_multiple_args_no_space") TEST_CASE("function_call_parentheses_multiple_args_space_before_commas") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call(arg1 ,arg3 ,arg3) )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_spaces_before_parentheses") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call () )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_spaces_within_parentheses") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call( ) )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_string_double_quotes") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call "string" )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_string_single_quotes") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call 'string' )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_string_no_space") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call'string' )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_table_literal") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call { x = 1 } )"; CHECK_EQ(code, transpile(code).code); } TEST_CASE("function_call_table_literal_no_space") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( call{x=1} )"; CHECK_EQ(code, transpile(code).code); } @@ -851,7 +778,6 @@ TEST_CASE("emit_a_do_block_in_cases_of_potentially_ambiguous_syntax") TEST_CASE_FIXTURE(Fixture, "parentheses_multiline") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local test = ( x @@ -863,9 +789,6 @@ local test = ( TEST_CASE_FIXTURE(Fixture, "stmt_semicolon") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( local test = 1; )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -875,7 +798,6 @@ TEST_CASE_FIXTURE(Fixture, "stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon") { - ScopedFastFlag sff{FFlag::LuauStoreCSTData2, true}; std::string code = R"( do return; @@ -886,9 +808,6 @@ TEST_CASE_FIXTURE(Fixture, "do_block_ending_with_semicolon") TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( if init then x = string.sub(x, utf8.offset(x, init)); @@ -899,9 +818,6 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( if (t < 1) then return c/2*t*t + b end; )"; @@ -910,9 +826,6 @@ TEST_CASE_FIXTURE(Fixture, "if_stmt_semicolon_2") TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( for i,v in ... do end; @@ -922,9 +835,6 @@ TEST_CASE_FIXTURE(Fixture, "for_loop_stmt_semicolon") TEST_CASE_FIXTURE(Fixture, "while_do_semicolon") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( while true do end; @@ -934,9 +844,6 @@ TEST_CASE_FIXTURE(Fixture, "while_do_semicolon") TEST_CASE_FIXTURE(Fixture, "function_definition_semicolon") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( function foo() end; @@ -1014,16 +921,7 @@ TEST_CASE("a_table_key_can_be_the_empty_string") TEST_CASE("always_emit_a_space_after_local_keyword") { std::string code = "do local aZZZZ = Workspace.P1.Shape local bZZZZ = Enum.PartType.Cylinder end"; - - if (FFlag::LuauStoreCSTData2) - { - CHECK_EQ(code, transpile(code).code); - } - else - { - std::string expected = "do local aZZZZ = Workspace.P1 .Shape local bZZZZ= Enum.PartType.Cylinder end"; - CHECK_EQ(expected, transpile(code).code); - } + CHECK_EQ(code, transpile(code).code); } TEST_CASE_FIXTURE(Fixture, "types_should_not_be_considered_cyclic_if_they_are_not_recursive") @@ -1060,23 +958,13 @@ TEST_CASE_FIXTURE(Fixture, "type_lists_should_be_emitted_correctly") end )"; - std::string expected = FFlag::LuauStoreCSTData2 ? R"( + std::string expected = R"( local a:(a:string,b:number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number) end local b:(...string)->(...number)=function(...:string): ...number end - local c:()->()=function(): () - end - )" - : R"( - local a:(string,number,...string)->(string,...number)=function(a:string,b:number,...:string): (string,...number) - end - - local b:(...string)->(...number)=function(...:string): ...number - end - local c:()->()=function(): () end )"; @@ -1116,7 +1004,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_assertion") TEST_CASE_FIXTURE(Fixture, "type_assertion_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "local a = 5 :: number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1133,7 +1020,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else") TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "local a = if 1 then 2 elseif 3 then 4 else 5"; CHECK_EQ(code, transpile(code).code); @@ -1141,7 +1027,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions") TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local x = if yes then nil @@ -1157,7 +1042,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_if_then_else_multiple_conditions_2") TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "local a = if 1 then 2 else 3"; CHECK_EQ(code, transpile(code).code); @@ -1194,7 +1078,6 @@ TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "if_then_else_spaces_between_else_if") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( return if a then "was a" else @@ -1222,7 +1105,6 @@ local a: Import.Type TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local _: Foo.Type )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1251,7 +1133,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_reference_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens") { ScopedFastFlag sffs[] = { - {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true}, }; std::string code = R"( local _: Type )"; @@ -1273,7 +1154,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_annotation_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_for_loop_annotation_spaces_around_tokens") { ScopedFastFlag sffs[] = { - {FFlag::LuauStoreCSTData2, true}, {FFlag::LuauStoreLocalAnnotationColonPositions, true}, }; std::string code = R"( for i: number = 1, 10 do end )"; @@ -1314,7 +1194,6 @@ local b: Packed<(number, string)> TEST_CASE_FIXTURE(Fixture, "type_packs_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type _ = Packed< T...> )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1372,11 +1251,7 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_2") TEST_CASE_FIXTURE(Fixture, "transpile_union_type_nested_3") { std::string code = "local a: nil | (string & number)"; - - if (FFlag::LuauStoreCSTData2) - CHECK_EQ(code, transpile(code, {}, true).code); - else - CHECK_EQ("local a: (string & number)?", transpile(code, {}, true).code); + CHECK_EQ(code, transpile(code, {}, true).code); } TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested") @@ -1395,10 +1270,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_nested_2") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, - }; std::string code = "type FnB = () -> U... & T"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1406,9 +1277,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_type_with_function") TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = "local a: | string | number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1418,9 +1286,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_union_pipe") TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = "local a: string | number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1430,9 +1295,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_union_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = "local a: & string & number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1442,9 +1304,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_leading_intersection_ampersand") TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = "local a: string & number"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1454,9 +1313,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_intersection_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = "local a: string | (Foo & Bar)"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1481,9 +1337,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_mixed_union_intersection") TEST_CASE_FIXTURE(Fixture, "transpile_preserve_union_optional_style") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = "local a: string | nil"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1515,7 +1368,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_varargs") TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = "local _ = a.name"; CHECK_EQ(one, transpile(one, {}, true).code); @@ -1528,7 +1380,6 @@ TEST_CASE_FIXTURE(Fixture, "index_name_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "index_name_ends_with_digit") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = "sparkles.Color = Color3.new()"; CHECK_EQ(code, transpile(code, {}, true).code); } @@ -1542,7 +1393,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_index_expr") TEST_CASE_FIXTURE(Fixture, "index_expr_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = "local _ = a[2]"; CHECK_EQ(one, transpile(one, {}, true).code); @@ -1586,7 +1436,6 @@ local _ = # e TEST_CASE_FIXTURE(Fixture, "binary_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local _ = 1+1 local _ = 1 +1 @@ -1628,7 +1477,6 @@ a ..= ' - result' TEST_CASE_FIXTURE(Fixture, "compound_assignment_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = R"( a += 1 )"; CHECK_EQ(one, transpile(one, {}, true).code); @@ -1645,7 +1493,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_assign_multiple") TEST_CASE_FIXTURE(Fixture, "transpile_assign_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string one = "a = 1"; CHECK_EQ(one, transpile(one).code); @@ -1681,11 +1528,7 @@ local f: (T, S...)->(number) = foo TEST_CASE_FIXTURE(Fixture, "transpile_union_reverse") { std::string code = "local a: nil | number"; - - if (FFlag::LuauStoreCSTData2) - CHECK_EQ(code, transpile(code, {}, true).code); - else - CHECK_EQ("local a: number?", transpile(code, {}, true).code); + CHECK_EQ(code, transpile(code, {}, true).code); } TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple") @@ -1800,9 +1643,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_for_in_multiple_types") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp") { - ScopedFastFlag fflags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( local _ = `hello {name}` )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1810,9 +1650,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline") { - ScopedFastFlag fflags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( local _ = `hello { name }!` )"; @@ -1822,9 +1659,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line") { - ScopedFastFlag fflags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( error( `a {b} c` @@ -1836,7 +1670,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_on_new_line") TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( local _ = `hello \ world!` )"; @@ -1845,9 +1678,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_string_interp_multiline_escape") TEST_CASE_FIXTURE(Fixture, "transpile_string_literal_escape") { - ScopedFastFlag fflags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; std::string code = R"( local _ = ` bracket = \{, backtick = \` = {'ok'} ` )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1862,7 +1692,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions") TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type function foo() end )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1878,7 +1707,6 @@ TEST_CASE_FIXTURE(Fixture, "transpile_type_functions_spaces_around_tokens") TEST_CASE_FIXTURE(Fixture, "transpile_typeof_spaces_around_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type X = typeof(x) )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1903,14 +1731,12 @@ TEST_CASE("transpile_single_quoted_string_types") TEST_CASE("transpile_double_quoted_string_types") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( type a = "hello world" )"; CHECK_EQ(code, transpile(code, {}, true).code); } TEST_CASE("transpile_raw_string_types") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type a = [[ hello world ]] )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -1920,14 +1746,12 @@ TEST_CASE("transpile_raw_string_types") TEST_CASE("transpile_escaped_string_types") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( type a = "\\b\\t\\n\\\\" )"; CHECK_EQ(code, transpile(code, {}, true).code); } TEST_CASE("transpile_type_table_semicolon_separators") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; const std::string code = R"( type Foo = { bar: number; @@ -1939,7 +1763,6 @@ TEST_CASE("transpile_type_table_semicolon_separators") TEST_CASE("transpile_type_table_access_modifiers") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { read bar: number, @@ -1960,7 +1783,6 @@ TEST_CASE("transpile_type_table_access_modifiers") TEST_CASE("transpile_type_table_spaces_between_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { bar: number, } )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -2003,7 +1825,6 @@ TEST_CASE("transpile_type_table_spaces_between_tokens") TEST_CASE("transpile_type_table_preserve_original_indexer_style") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { [number]: string @@ -2019,7 +1840,6 @@ TEST_CASE("transpile_type_table_preserve_original_indexer_style") TEST_CASE("transpile_type_table_preserve_indexer_location") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { [number]: string, @@ -2048,7 +1868,6 @@ TEST_CASE("transpile_type_table_preserve_indexer_location") TEST_CASE("transpile_type_table_preserve_property_definition_style") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { ["$$typeof1"]: string, @@ -2060,7 +1879,6 @@ TEST_CASE("transpile_type_table_preserve_property_definition_style") TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( type Foo = { [ "$$typeof1"]: string, @@ -2072,10 +1890,6 @@ TEST_CASE("transpile_type_table_string_properties_spaces_between_tokens") TEST_CASE("transpile_types_preserve_parentheses_style") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - }; - std::string code = R"( type Foo = number )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -2113,10 +1927,6 @@ end TEST_CASE("transpile_type_function_unnamed_arguments") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, - }; std::string code = R"( type Foo = () -> () )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -2150,10 +1960,6 @@ TEST_CASE("transpile_type_function_unnamed_arguments") TEST_CASE("transpile_type_function_named_arguments") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, - }; std::string code = R"( type Foo = (x: string) -> () )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -2181,10 +1987,6 @@ TEST_CASE("transpile_type_function_named_arguments") TEST_CASE("transpile_type_function_generics") { - ScopedFastFlag flags[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, - }; std::string code = R"( type Foo = () -> () )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -2218,10 +2020,6 @@ TEST_CASE("transpile_type_function_generics") TEST_CASE("transpile_type_function_return_types") { - ScopedFastFlag fflags[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, - }; std::string code = R"( type Foo = () -> () )"; CHECK_EQ(code, transpile(code, {}, true).code); @@ -2268,8 +2066,6 @@ TEST_CASE("transpile_type_function_return_types") TEST_CASE("transpile_chained_function_types") { ScopedFastFlag fflags[] = { - {FFlag::LuauStoreCSTData2, true}, - {FFlag::LuauStoreReturnTypesAsPackOnAst, true}, {FFlag::LuauCSTForReturnTypeFunctionTail, true}, }; std::string code = R"( type Foo = () -> () -> () )"; @@ -2290,7 +2086,6 @@ TEST_CASE("fuzzer_nil_optional") TEST_CASE("transpile_function_attributes") { - ScopedFastFlag _{FFlag::LuauStoreCSTData2, true}; std::string code = R"( @native function foo() diff --git a/tests/TypeFunction.test.cpp b/tests/TypeFunction.test.cpp index 237a7bb5..068c173f 100644 --- a/tests/TypeFunction.test.cpp +++ b/tests/TypeFunction.test.cpp @@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauErrorSuppressionTypeFunctionArgs) +LUAU_FASTFLAG(LuauEmptyStringInKeyOf) struct TypeFunctionFixture : Fixture { @@ -1706,6 +1707,31 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "error_suppression_should_work_on_type_functi CHECK("Unknown type 'Colours'" == toString(result.errors[0])); } +TEST_CASE_FIXTURE(BuiltinsFixture, "keyof_should_not_assert_on_empty_string_props") +{ + if (!FFlag::LuauSolverV2) + return; + + ScopedFastFlag _{FFlag::LuauEmptyStringInKeyOf, true}; + + loadDefinition(R"( + declare class Foobar + one: boolean + [""]: number + end + )"); + + CheckResult results = check(R"( + export type FoobarKeys = keyof; + export type TableKeys = keyof<{ [""]: string, two: boolean }> + )"); + + LUAU_REQUIRE_NO_ERRORS(results); + CHECK_EQ(R"("" | "one")", toString(requireTypeAlias("FoobarKeys"))); + CHECK_EQ(R"("" | "two")", toString(requireTypeAlias("TableKeys"))); + +} + struct TFFixture { TypeArena arena_; diff --git a/tests/TypeFunction.user.test.cpp b/tests/TypeFunction.user.test.cpp index b76fb9ba..89d624a6 100644 --- a/tests/TypeFunction.user.test.cpp +++ b/tests/TypeFunction.user.test.cpp @@ -14,6 +14,7 @@ LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauUserTypeFunctionAliases) LUAU_FASTFLAG(LuauFollowTypeAlias) LUAU_FASTFLAG(LuauFollowExistingTypeFunction) +LUAU_FASTFLAG(LuauTypeFunctionSerializeFollowMetatable) TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests"); @@ -2468,6 +2469,20 @@ end )")); } +TEST_CASE_FIXTURE(Fixture, "udtf_metatable_serialization_follows") +{ + ScopedFastFlag luauTypeFunctionSerializeFollowMetatable{FFlag::LuauTypeFunctionSerializeFollowMetatable, true}; + + LUAU_REQUIRE_ERRORS(check(R"( +_ = setmetatable(_(),_),(_) or _ == _ or f +while _() do +export type function t0(l0,l0) +end +type t39 = t0<{write [Vector3]:any},typeof(_),l0.t0,any> +end + )")); +} + TEST_CASE_FIXTURE(Fixture, "udtf_double_definition") { ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; diff --git a/tests/TypeInfer.annotations.test.cpp b/tests/TypeInfer.annotations.test.cpp index ebbc3ec3..08383ac4 100644 --- a/tests/TypeInfer.annotations.test.cpp +++ b/tests/TypeInfer.annotations.test.cpp @@ -7,8 +7,9 @@ #include "doctest.h" -LUAU_FASTFLAG(LuauSolverV2); -LUAU_FASTFLAG(DebugLuauMagicTypes); +LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(DebugLuauMagicTypes) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) using namespace Luau; @@ -443,7 +444,10 @@ TEST_CASE_FIXTURE(Fixture, "self_referential_type_alias") std::optional incr = get(oTable->props, "incr"); REQUIRE(incr); - const FunctionType* incrFunc = get(incr->type()); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + REQUIRE(incr->readTy); + + const FunctionType* incrFunc = FFlag::LuauRemoveTypeCallsForReadWriteProps ? get(*incr->readTy) : get(incr->type_DEPRECATED()); REQUIRE(incrFunc); std::optional firstArg = first(incrFunc->argTypes); @@ -602,7 +606,14 @@ TEST_CASE_FIXTURE(Fixture, "interface_types_belong_to_interface_arena") TableType* exportsTable = getMutable(*exportsType); REQUIRE(exportsTable != nullptr); - TypeId n = exportsTable->props["n"].type(); + TypeId n; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(exportsTable->props["n"].readTy); + n = *exportsTable->props["n"].readTy; + } + else + n = exportsTable->props["n"].type_DEPRECATED(); REQUIRE(n != nullptr); CHECK(isInArena(n, mod.interfaceTypes)); @@ -657,10 +668,24 @@ TEST_CASE_FIXTURE(Fixture, "cloned_interface_maintains_pointers_between_definiti TableType* exportsTable = getMutable(*exportsType); REQUIRE(exportsTable != nullptr); - TypeId aType = exportsTable->props["a"].type(); + TypeId aType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(exportsTable->props["a"].readTy); + aType = *exportsTable->props["a"].readTy; + } + else + aType = exportsTable->props["a"].type_DEPRECATED(); REQUIRE(aType); - TypeId bType = exportsTable->props["b"].type(); + TypeId bType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(exportsTable->props["b"].readTy); + bType = *exportsTable->props["b"].readTy; + } + else + bType = exportsTable->props["b"].type_DEPRECATED(); REQUIRE(bType); CHECK(isInArena(recordType, mod.interfaceTypes)); diff --git a/tests/TypeInfer.anyerror.test.cpp b/tests/TypeInfer.anyerror.test.cpp index d2972b1b..521ed781 100644 --- a/tests/TypeInfer.anyerror.test.cpp +++ b/tests/TypeInfer.anyerror.test.cpp @@ -13,9 +13,10 @@ using namespace Luau; -LUAU_FASTFLAG(LuauSolverV2); +LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(LuauAddCallConstraintForIterableFunctions) LUAU_FASTFLAG(LuauDfgAllowUpdatesInLoops) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) TEST_SUITE_BEGIN("TypeInferAnyError"); @@ -294,7 +295,13 @@ TEST_CASE_FIXTURE(Fixture, "assign_prop_to_table_by_calling_any_yields_any") REQUIRE(ttv); REQUIRE(ttv->props.count("prop")); - REQUIRE_EQ("any", toString(ttv->props["prop"].type())); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(ttv->props["prop"].readTy); + CHECK_EQ("any", toString(*ttv->props["prop"].readTy)); + } + else + REQUIRE_EQ("any", toString(ttv->props["prop"].type_DEPRECATED())); } TEST_CASE_FIXTURE(Fixture, "quantify_any_does_not_bind_to_itself") diff --git a/tests/TypeInfer.builtins.test.cpp b/tests/TypeInfer.builtins.test.cpp index a0987a0a..f337b5db 100644 --- a/tests/TypeInfer.builtins.test.cpp +++ b/tests/TypeInfer.builtins.test.cpp @@ -14,7 +14,11 @@ LUAU_FASTFLAG(LuauTableCloneClonesType3) LUAU_FASTFLAG(LuauEagerGeneralization4) LUAU_FASTFLAG(LuauArityMismatchOnUndersaturatedUnknownArguments) LUAU_FASTFLAG(LuauStringFormatImprovements) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) LUAU_FASTFLAG(LuauWriteOnlyPropertyMangling) +LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) +LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) +LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck) TEST_SUITE_BEGIN("BuiltinTests"); @@ -1313,7 +1317,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "no_persistent_typelevel_change") REQUIRE(mathTy); TableType* ttv = getMutable(mathTy); REQUIRE(ttv); - const FunctionType* ftv = get(ttv->props["frexp"].type()); + const FunctionType* ftv; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(ttv->props["frexp"].readTy); + ftv = get(*ttv->props["frexp"].readTy); + } + else + ftv = get(ttv->props["frexp"].type_DEPRECATED()); REQUIRE(ftv); auto original = ftv->level; @@ -1720,12 +1731,14 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "better_string_format_error_when_format_strin TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion") { - ScopedFastFlag _{FFlag::LuauWriteOnlyPropertyMangling, true}; + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauEnableWriteOnlyProperties, true}, + {FFlag::LuauWriteOnlyPropertyMangling, true}, + {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, + }; - // CLI-157307: This currently errors as we claim the literal is not a - // `{ write foo: number }`, which is wrong as every table literal is - // trivially a write-only table. - LUAU_REQUIRE_ERRORS(check(R"( + LUAU_REQUIRE_NO_ERRORS(check(R"( local function accept(t: { write foo: number }) end @@ -1733,4 +1746,42 @@ TEST_CASE_FIXTURE(Fixture, "write_only_table_assertion") )")); } +TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_known_property_identity") +{ + // This will not result in a real refinement, as we refine `bnot`, a function, to be truthy + LUAU_REQUIRE_NO_ERRORS(check(R"( + if bit32.bnot then + end + )")); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_unknown_property") +{ + ScopedFastFlag _{FFlag::LuauTypeCheckerStricterIndexCheck, true}; + + CheckResult results = check(R"( + if bit32.scrambleEggs then + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, results); + auto err = get(results.errors[0]); + CHECK_EQ(err->key, "scrambleEggs"); + CHECK_EQ(toString(err->table), "typeof(bit32)"); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "read_refinements_on_persistent_tables_known_property_narrow") +{ + ScopedFastFlag _{FFlag::LuauSolverV2, true}; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + local myutf8 = utf8 + if myutf8.charpattern == "lol" then + local foobar = myutf8.charpattern + local _ = foobar + end + )")); + CHECK_EQ("\"lol\"", toString(requireTypeAtPosition(Position{4, 23}))); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.classes.test.cpp b/tests/TypeInfer.classes.test.cpp index e9484f74..a903a037 100644 --- a/tests/TypeInfer.classes.test.cpp +++ b/tests/TypeInfer.classes.test.cpp @@ -15,6 +15,7 @@ using namespace Luau; using std::nullopt; LUAU_FASTFLAG(LuauSolverV2) +LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) TEST_SUITE_BEGIN("TypeInferExternTypes"); @@ -441,6 +442,8 @@ b.X = 2 -- real Vector2.X is also read-only TEST_CASE_FIXTURE(ExternTypeFixture, "detailed_class_unification_error") { + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}; + CheckResult result = check(R"( local function foo(v) return v.X :: number + string.len(v.Y) @@ -455,7 +458,10 @@ b(a) if (FFlag::LuauSolverV2) { - CHECK("Type 'number' could not be converted into 'string'" == toString(result.errors.at(0))); + const std::string expected = "Type 'Vector2' could not be converted into '{ read X: unknown, read Y: string }'; \n" + "this is because accessing `Y` results in `number` in the former type and `string` in the latter type, " + "and `number` is not a subtype of `string`"; + CHECK_EQ(expected, toString(result.errors.at(0))); } else { diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index 1b11c56e..c87ee7a3 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -11,6 +11,7 @@ using namespace Luau; LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) TEST_SUITE_BEGIN("DefinitionTests"); @@ -170,10 +171,23 @@ TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_overload_non_function") REQUIRE(!result.success); CHECK_EQ(result.parseResult.errors.size(), 0); REQUIRE(bool(result.module)); - REQUIRE_EQ(result.module->errors.size(), 1); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + REQUIRE_EQ(result.module->errors.size(), 2); + else + REQUIRE_EQ(result.module->errors.size(), 1); GenericError* ge = get(result.module->errors[0]); REQUIRE(ge); - CHECK_EQ("Cannot overload non-function class member 'X'", ge->message); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + CHECK_EQ("Cannot overload read type of non-function class member 'X'", ge->message); + else + CHECK_EQ("Cannot overload non-function class member 'X'", ge->message); + + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + GenericError* ge2 = get(result.module->errors[1]); + REQUIRE(ge2); + CHECK_EQ("Cannot overload write type of non-function class member 'X'", ge2->message); + } } TEST_CASE_FIXTURE(Fixture, "class_definitions_cannot_extend_non_class") @@ -366,7 +380,14 @@ TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_re const auto& method = cls->props["myMethod"]; CHECK_EQ(method.documentationSymbol, "@test/globaltype/MyClass.myMethod"); - FunctionType* function = getMutable(method.type()); + FunctionType* function; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(method.readTy); + function = getMutable(*method.readTy); + } + else + function = getMutable(method.type_DEPRECATED()); REQUIRE(function); REQUIRE(function->definition.has_value()); diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index ee6bc94e..40ae20de 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -29,6 +29,7 @@ LUAU_FASTFLAG(LuauDoNotAddUpvalueTypesToLocalType) LUAU_FASTFLAG(LuauSimplifyOutOfLine2) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) TEST_SUITE_BEGIN("TypeInferFunctions"); @@ -190,7 +191,15 @@ TEST_CASE_FIXTURE(Fixture, "generalize_table_property") const TableType* tt = get(follow(t)); REQUIRE(tt); - TypeId fooTy = tt->props.at("foo").type(); + TypeId fooTy; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + const Property& foo = tt->props.at("foo"); + REQUIRE(foo.readTy); + fooTy = *foo.readTy; + } + else + fooTy = tt->props.at("foo").type_DEPRECATED(); CHECK("(a) -> a" == toString(fooTy)); } @@ -235,7 +244,16 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "vararg_function_is_quantified") REQUIRE(ttv); REQUIRE(ttv->props.count("f")); - TypeId k = ttv->props["f"].type(); + + TypeId k; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + const Property& f = ttv->props["f"]; + REQUIRE(f.readTy); + k = *f.readTy; + } + else + k = ttv->props["f"].type_DEPRECATED(); REQUIRE(k); } diff --git a/tests/TypeInfer.generics.test.cpp b/tests/TypeInfer.generics.test.cpp index 64ff3832..1c55a43b 100644 --- a/tests/TypeInfer.generics.test.cpp +++ b/tests/TypeInfer.generics.test.cpp @@ -19,6 +19,7 @@ LUAU_FASTFLAG(LuauIntersectNotNil) LUAU_FASTFLAG(LuauSubtypingCheckFunctionGenericCounts) LUAU_FASTFLAG(LuauReportSubtypingErrors) LUAU_FASTFLAG(LuauEagerGeneralization4) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) using namespace Luau; @@ -1142,7 +1143,15 @@ TEST_CASE_FIXTURE(Fixture, "generic_table_method") REQUIRE(tTable != nullptr); REQUIRE(tTable->props.count("bar")); - TypeId barType = tTable->props["bar"].type(); + TypeId barType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + Property& bar = tTable->props["bar"]; + REQUIRE(bar.readTy); + barType = *bar.readTy; + } + else + barType = tTable->props["bar"].type_DEPRECATED(); REQUIRE(barType != nullptr); const FunctionType* ftv = get(follow(barType)); @@ -1177,7 +1186,15 @@ TEST_CASE_FIXTURE(Fixture, "correctly_instantiate_polymorphic_member_functions") std::optional fooProp = get(t->props, "foo"); REQUIRE(bool(fooProp)); - const FunctionType* foo = get(follow(fooProp->type())); + + const FunctionType* foo; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(fooProp->readTy); + foo = get(follow(*fooProp->readTy)); + } + else + foo = get(follow(fooProp->type_DEPRECATED())); REQUIRE(bool(foo)); std::optional ret_ = first(foo->retTypes); @@ -1224,7 +1241,14 @@ TEST_CASE_FIXTURE(Fixture, "instantiate_cyclic_generic_function") std::optional methodProp = get(argTable->props, "method"); REQUIRE(bool(methodProp)); - const FunctionType* methodFunction = get(follow(methodProp->type())); + const FunctionType* methodFunction; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(methodProp->readTy); + methodFunction = get(follow(*methodProp->readTy)); + } + else + methodFunction = get(follow(methodProp->type_DEPRECATED())); REQUIRE(methodFunction != nullptr); std::optional methodArg = first(methodFunction->argTypes); diff --git a/tests/TypeInfer.intersectionTypes.test.cpp b/tests/TypeInfer.intersectionTypes.test.cpp index 427b8442..5cd578d3 100644 --- a/tests/TypeInfer.intersectionTypes.test.cpp +++ b/tests/TypeInfer.intersectionTypes.test.cpp @@ -10,8 +10,8 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) -LUAU_FASTFLAG(LuauNarrowIntersectionNevers) LUAU_FASTFLAG(LuauTableLiteralSubtypeSpecificCheck2) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) TEST_SUITE_BEGIN("IntersectionTypes"); @@ -1458,8 +1458,10 @@ TEST_CASE_FIXTURE(Fixture, "cli_80596_simplify_more_realistic_intersections") TEST_CASE_FIXTURE(BuiltinsFixture, "narrow_intersection_nevers") { - ScopedFastFlag newSolver{FFlag::LuauSolverV2, true}; - ScopedFastFlag narrowIntersections{FFlag::LuauNarrowIntersectionNevers, true}; + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauRefineTablesWithReadType, true}, + }; loadDefinition(R"( declare class Player @@ -1474,7 +1476,7 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "narrow_intersection_nevers") end )")); - CHECK_EQ("Player & { Character: ~(false?) }", toString(requireTypeAtPosition({3, 23}))); + CHECK_EQ("Player & { read Character: ~(false?) }", toString(requireTypeAtPosition({3, 23}))); } TEST_SUITE_END(); diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index 88c4ec9b..20da4322 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -13,7 +13,6 @@ using namespace Luau; LUAU_FASTFLAG(LuauSolverV2) LUAU_FASTFLAG(DebugLuauEqSatSimplification) -LUAU_FASTFLAG(LuauStoreCSTData2) LUAU_FASTINT(LuauNormalizeCacheLimit) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTypeInferIterationLimit) @@ -49,7 +48,7 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") end )"; - const std::string expected = FFlag::LuauStoreCSTData2 ? R"( + const std::string expected = R"( function f(a:{fn:()->(a,b...)}): () if type(a) == 'boolean' then local a1:boolean=a @@ -57,18 +56,9 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") local a2:{fn:()->(a,b...)}=a end end - )" - : R"( - function f(a:{fn:()->(a,b...)}): () - if type(a) == 'boolean'then - local a1:boolean=a - elseif a.fn()then - local a2:{fn:()->(a,b...)}=a - end - end )"; - const std::string expectedWithNewSolver = FFlag::LuauStoreCSTData2 ? R"( + const std::string expectedWithNewSolver = R"( function f(a:{fn:()->(unknown,...unknown)}): () if type(a) == 'boolean' then local a1:{fn:()->(unknown,...unknown)}&boolean=a @@ -76,18 +66,9 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a end end - )" - : R"( - function f(a:{fn:()->(unknown,...unknown)}): () - if type(a) == 'boolean'then - local a1:{fn:()->(unknown,...unknown)}&boolean=a - elseif a.fn()then - local a2:{fn:()->(unknown,...unknown)}&(class|function|nil|number|string|thread|buffer|table)=a - end - end )"; - const std::string expectedWithEqSat = FFlag::LuauStoreCSTData2 ? R"( + const std::string expectedWithEqSat = R"( function f(a:{fn:()->(unknown,...unknown)}): () if type(a) == 'boolean' then local a1:{fn:()->(unknown,...unknown)}&boolean=a @@ -95,15 +76,6 @@ TEST_CASE_FIXTURE(Fixture, "typeguard_inference_incomplete") local a2:{fn:()->(unknown,...unknown)}&negate=a end end - )" - : R"( - function f(a:{fn:()->(unknown,...unknown)}): () - if type(a) == 'boolean'then - local a1:{fn:()->(unknown,...unknown)}&boolean=a - elseif a.fn()then - local a2:{fn:()->(unknown,...unknown)}&negate=a - end - end )"; if (FFlag::LuauSolverV2 && !FFlag::DebugLuauEqSatSimplification) diff --git a/tests/TypeInfer.refinements.test.cpp b/tests/TypeInfer.refinements.test.cpp index 96776e12..874e4ed9 100644 --- a/tests/TypeInfer.refinements.test.cpp +++ b/tests/TypeInfer.refinements.test.cpp @@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauBetterCannotCallFunctionPrimitive) LUAU_FASTFLAG(LuauTypeCheckerStricterIndexCheck) LUAU_FASTFLAG(LuauNormalizationIntersectTablesPreservesExternTypes) LUAU_FASTFLAG(LuauAvoidDoubleNegation) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) using namespace Luau; @@ -509,6 +510,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "impossible_type_narrow_is_not_an_error") TEST_CASE_FIXTURE(Fixture, "truthy_constraint_on_properties") { + ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true}; + CheckResult result = check(R"( local t: {x: number?} = {x = 1} @@ -530,8 +533,7 @@ TEST_CASE_FIXTURE(Fixture, "truthy_constraint_on_properties") } else { - // CLI-115281 - Types produced by refinements don't always get simplified - CHECK("{ x: number? } & { x: ~(false?) }" == toString(requireTypeAtPosition({4, 23}))); + CHECK("{ read x: number, write x: number? }" == toString(requireTypeAtPosition({4, 23}))); } CHECK("number" == toString(requireTypeAtPosition({5, 26}))); } @@ -998,6 +1000,8 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "either_number_or_string") TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t") { + ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true}; + CheckResult result = check(R"( local function f(t: {x: boolean}?) if not t or t.x then @@ -1010,8 +1014,14 @@ TEST_CASE_FIXTURE(Fixture, "not_t_or_some_prop_of_t") if (FFlag::LuauSolverV2) { - // CLI-115281 Types produced by refinements do not consistently get simplified - CHECK_EQ("({ x: boolean } & { x: ~(false?) })?", toString(requireTypeAtPosition({3, 28}))); + // CLI-115281 Types produced by refinements do not consistently get simplified: we are minting a type like: + // + // intersect<{ x: boolean } | nil, { read x: ~(false?) } | false | nil> + // + // ... which we can't _quite_ refine into the type it ought to be: + // + // { write x: boolean, read x: true } | nil + CHECK_EQ("({ read x: ~(false?) } & { x: boolean })?", toString(requireTypeAtPosition({3, 28}))); } else CHECK_EQ("{| x: boolean |}?", toString(requireTypeAtPosition({3, 28}))); @@ -1244,7 +1254,10 @@ TEST_CASE_FIXTURE(Fixture, "apply_refinements_on_astexprindexexpr_whose_subscrip TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x") { - ScopedFastFlag _{FFlag::LuauAvoidDoubleNegation, true}; + ScopedFastFlag sffs[] = { + {FFlag::LuauAvoidDoubleNegation, true}, + {FFlag::LuauRefineTablesWithReadType, true}, + }; CheckResult result = check(R"( type T = {tag: "missing", x: nil} | {tag: "exists", x: string} @@ -1262,15 +1275,8 @@ TEST_CASE_FIXTURE(Fixture, "discriminate_from_truthiness_of_x") if (FFlag::LuauSolverV2) { - // CLI-115281 Types produced by refinements do not consistently get - // simplified. Sometimes this is due to not refining at the correct - // time, sometimes this is due to hitting the simplifier rather than - // normalization. - CHECK("{ tag: \"exists\", x: string } & { x: ~(false?) }" == toString(requireTypeAtPosition({5, 28}))); - CHECK( - R"(({ tag: "exists", x: string } & { x: false? }) | ({ tag: "missing", x: nil } & { x: false? }))" == - toString(requireTypeAtPosition({7, 28})) - ); + CHECK(R"({ tag: "exists", x: string })" == toString(requireTypeAtPosition({5, 28}))); + CHECK(R"({ tag: "missing", x: nil })" == toString(requireTypeAtPosition({7, 28}))); } else { @@ -2684,9 +2690,35 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1451") )")); } -TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function") +TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_single") { - ScopedFastFlag sff{FFlag::LuauSolverV2, true}; + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauBetterCannotCallFunctionPrimitive, true}, + {FFlag::LuauTypeCheckerStricterIndexCheck, true}, + {FFlag::LuauRefineTablesWithReadType, true}, + }; + + CheckResult result = check(R"( + local function invokeDisconnect(d: unknown) + if type(d) == "function" then + d() + end + end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK_EQ("The type function is not precise enough for us to determine the appropriate result type of this call.", toString(result.errors[0])); +} + +TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function_union") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauBetterCannotCallFunctionPrimitive, true}, + {FFlag::LuauTypeCheckerStricterIndexCheck, true}, + {FFlag::LuauRefineTablesWithReadType, true}, + }; CheckResult result = check(R"( type Disconnectable = { @@ -2704,35 +2736,16 @@ TEST_CASE_FIXTURE(RefinementExternTypeFixture, "cannot_call_a_function") end )"); - if (FFlag::LuauTypeCheckerStricterIndexCheck) - { - LUAU_REQUIRE_ERROR_COUNT(2, result); + LUAU_REQUIRE_ERROR_COUNT(2, result); - CHECK_EQ( - toString(result.errors[0]), - "Key 'Disconnect' is missing from 't2 where t1 = ExternScriptConnection | t2 | { Disconnect: (t1) -> (...any) } ; t2 = { disconnect: " - "(t1) -> (...any) }' in the type 't1 where t1 = ExternScriptConnection | { Disconnect: (t1) -> (...any) } | { disconnect: (t1) -> " - "(...any) }'" - ); + // FIXME CLI-157125: It's a bit clowny that we return a union of + // functions containing `function` here, but it looks like a side + // effect of how we execute `hasProp`. + std::string expectedError = "Cannot call a value of type function in union:\n" + " ((ExternScriptConnection) -> ()) | function | t2 where t1 = ExternScriptConnection | { Disconnect: t2 } | { " + "disconnect: (t1) -> (...any) } ; t2 = (t1) -> (...any)"; - if (FFlag::LuauBetterCannotCallFunctionPrimitive) - CHECK_EQ( - toString(result.errors[1]), "The type function is not precise enough for us to determine the appropriate result type of this call." - ); - else - CHECK_EQ(toString(result.errors[1]), "Cannot call a value of type function"); - } - else - { - LUAU_REQUIRE_ERROR_COUNT(1, result); - - if (FFlag::LuauBetterCannotCallFunctionPrimitive) - CHECK_EQ( - toString(result.errors[0]), "The type function is not precise enough for us to determine the appropriate result type of this call." - ); - else - CHECK_EQ(toString(result.errors[0]), "Cannot call a value of type function"); - } + CHECK_EQ(toString(result.errors[1]), expectedError); } TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1835") diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index efc25df1..ebe2274c 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -31,6 +31,11 @@ LUAU_FASTFLAG(LuauEnableWriteOnlyProperties) LUAU_FASTFLAG(LuauDisablePrimitiveInferenceInLargeTables) LUAU_FASTINT(LuauPrimitiveInferenceInTableLimit) LUAU_FASTFLAG(LuauAutocompleteMissingFollows) +LUAU_FASTFLAG(LuauRemoveTypeCallsForReadWriteProps) +LUAU_FASTFLAG(LuauRelateTablesAreNeverDisjoint) +LUAU_FASTFLAG(LuauTableLiteralSubtypeCheckFunctionCalls) +LUAU_FASTFLAG(LuauAvoidGenericsLeakingDuringFunctionCallCheck) +LUAU_FASTFLAG(LuauRefineTablesWithReadType) TEST_SUITE_BEGIN("TableTests"); @@ -80,15 +85,33 @@ TEST_CASE_FIXTURE(Fixture, "basic") std::optional fooProp = get(tType->props, "foo"); REQUIRE(bool(fooProp)); - CHECK_EQ(PrimitiveType::String, getPrimitiveType(fooProp->type())); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(fooProp->readTy); + CHECK_EQ(PrimitiveType::String, getPrimitiveType(*fooProp->readTy)); + } + else + CHECK_EQ(PrimitiveType::String, getPrimitiveType(fooProp->type_DEPRECATED())); std::optional bazProp = get(tType->props, "baz"); REQUIRE(bool(bazProp)); - CHECK_EQ(PrimitiveType::Number, getPrimitiveType(bazProp->type())); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(bazProp->readTy); + CHECK_EQ(PrimitiveType::Number, getPrimitiveType(*bazProp->readTy)); + } + else + CHECK_EQ(PrimitiveType::Number, getPrimitiveType(bazProp->type_DEPRECATED())); std::optional quuxProp = get(tType->props, "quux"); REQUIRE(bool(quuxProp)); - CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type())); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(quuxProp->readTy); + CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(*quuxProp->readTy)); + } + else + CHECK_EQ(PrimitiveType::NilType, getPrimitiveType(quuxProp->type_DEPRECATED())); } TEST_CASE_FIXTURE(Fixture, "augment_table") @@ -117,7 +140,16 @@ TEST_CASE_FIXTURE(Fixture, "augment_nested_table") REQUIRE(tType != nullptr); REQUIRE(tType->props.find("p") != tType->props.end()); - const TableType* pType = get(tType->props["p"].type()); + + const TableType* pType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + Property& p = tType->props["p"]; + REQUIRE(p.readTy); + pType = get(p.readTy); + } + else + pType = get(tType->props["p"].type_DEPRECATED()); REQUIRE(pType != nullptr); CHECK("{ p: { foo: string } }" == toString(requireType("t"), {true})); @@ -261,7 +293,14 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function") std::optional fooProp = get(tableType->props, "foo"); REQUIRE(bool(fooProp)); - const FunctionType* methodType = get(follow(fooProp->type())); + const FunctionType* methodType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(fooProp->readTy); + methodType = get(follow(fooProp->readTy)); + } + else + methodType = get(follow(fooProp->type_DEPRECATED())); REQUIRE(methodType != nullptr); } @@ -275,7 +314,15 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2") std::optional uProp = get(tableType->props, "U"); REQUIRE(bool(uProp)); - TypeId uType = uProp->type(); + + TypeId uType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(uProp->readTy); + uType = *uProp->readTy; + } + else + uType = uProp->type_DEPRECATED(); const TableType* uTable = get(uType); REQUIRE(uTable != nullptr); @@ -283,7 +330,14 @@ TEST_CASE_FIXTURE(Fixture, "tc_member_function_2") std::optional fooProp = get(uTable->props, "foo"); REQUIRE(bool(fooProp)); - const FunctionType* methodType = get(follow(fooProp->type())); + const FunctionType* methodType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + REQUIRE(fooProp->readTy); + methodType = get(follow(fooProp->readTy)); + } + else + methodType = get(follow(fooProp->type_DEPRECATED())); REQUIRE(methodType != nullptr); std::vector methodArgs = flatten(methodType->argTypes).first; @@ -477,6 +531,8 @@ TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_1") TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2") { + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}; + CheckResult result = check(R"( --!strict function foo(o) @@ -489,23 +545,11 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "table_param_width_subtyping_2") LUAU_REQUIRE_ERROR_COUNT(1, result); - // CLI 114792 We don't report MissingProperties in many places where the old solver does - if (FFlag::LuauSolverV2) - { - TypeMismatch* error = get(result.errors[0]); - REQUIRE_MESSAGE(error != nullptr, "Expected TypeMismatch but got " << toString(result.errors[0])); + MissingProperties* error = get(result.errors[0]); + REQUIRE_MESSAGE(error != nullptr, "Expected MissingProperties but got " << toString(result.errors[0])); + REQUIRE(error->properties.size() == 1); - CHECK("{ read bar: string }" == toString(error->givenType)); - CHECK("{ read bar: string, read baz: string }" == toString(error->wantedType)); - } - else - { - MissingProperties* error = get(result.errors[0]); - REQUIRE_MESSAGE(error != nullptr, "Expected MissingProperties but got " << toString(result.errors[0])); - REQUIRE(error->properties.size() == 1); - - CHECK_EQ("baz", error->properties[0]); - } + CHECK_EQ("baz", error->properties[0]); } TEST_CASE_FIXTURE(Fixture, "table_param_width_subtyping_3") @@ -1063,7 +1107,16 @@ TEST_CASE_FIXTURE(Fixture, "assigning_to_an_unsealed_table_with_string_literal_s REQUIRE(tableType->indexer == std::nullopt); REQUIRE(0 != tableType->props.count("a")); - TypeId propertyA = tableType->props["a"].type(); + + TypeId propertyA; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + Property& a = tableType->props["a"]; + REQUIRE(a.readTy); + propertyA = *a.readTy; + } + else + propertyA = tableType->props["a"].type_DEPRECATED(); REQUIRE(propertyA != nullptr); CHECK_EQ(*getBuiltins()->stringType, *propertyA); } @@ -2298,8 +2351,12 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "quantifying_a_bound_var_works") REQUIRE_MESSAGE(ttv, "Expected a table but got " << toString(ty, {true})); REQUIRE(ttv->props.count("new")); Property& prop = ttv->props["new"]; - REQUIRE(prop.type()); - const FunctionType* ftv = get(follow(prop.type())); + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + REQUIRE(prop.readTy); + else + REQUIRE(prop.type_DEPRECATED()); + const FunctionType* ftv = + (FFlag::LuauRemoveTypeCallsForReadWriteProps) ? get(follow(*prop.readTy)) : get(follow(prop.type_DEPRECATED())); REQUIRE(ftv); const TypePack* res = get(follow(ftv->retTypes)); REQUIRE(res); @@ -3156,7 +3213,15 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_quantify_table_that_belongs_to_outer_sc REQUIRE(counterType); REQUIRE(counterType->props.count("new")); - const FunctionType* newType = get(follow(counterType->props["new"].type())); + const FunctionType* newType; + if (FFlag::LuauRemoveTypeCallsForReadWriteProps) + { + Property& newProp = counterType->props["new"]; + REQUIRE(newProp.readTy); + newType = get(follow(*newProp.readTy)); + } + else + newType = get(follow(counterType->props["new"].type_DEPRECATED())); REQUIRE(newType); std::optional newRetType = *first(newType->retTypes); @@ -3517,6 +3582,8 @@ TEST_CASE_FIXTURE(Fixture, "dont_invalidate_the_properties_iterator_of_free_tabl TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early") { + ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true}; + CheckResult result = check(R"( local t: {x: number?}? = {x = nil} local u = t.x and t or 5 @@ -3527,7 +3594,7 @@ TEST_CASE_FIXTURE(Fixture, "checked_prop_too_early") if (FFlag::LuauSolverV2) { CHECK_EQ("Value of type '{ x: number? }?' could be nil", toString(result.errors[0])); - CHECK_EQ("number | { x: number }", toString(requireType("u"))); + CHECK_EQ("number | { read x: number, write x: number? }", toString(requireType("u"))); } else { @@ -3896,6 +3963,7 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table { ScopedFastFlag sff[]{ {FFlag::LuauInstantiateInSubtyping, true}, + {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, }; CheckResult result = check(R"( @@ -3915,12 +3983,12 @@ TEST_CASE_FIXTURE(Fixture, "invariant_table_properties_means_instantiating_table if (FFlag::LuauSolverV2) { - // FIXME. We really should be reporting just one error in this case. CLI-114509 - LUAU_REQUIRE_ERROR_COUNT(3, result); - - CHECK(get(result.errors[0])); - CHECK(get(result.errors[1])); - CHECK(get(result.errors[2])); + LUAU_REQUIRE_ERROR_COUNT(1, result); + auto err = get(result.errors[0]); + CHECK_EQ(result.errors[0].location, Location{{10, 10}, {10, 11}}); + REQUIRE(err); + CHECK_EQ("{ m: (number) -> number }", toString(err->wantedType)); + CHECK_EQ("{ m: (T) -> T }", toString(err->givenType, {true})); } else { @@ -4457,6 +4525,8 @@ TEST_CASE_FIXTURE(Fixture, "new_solver_supports_read_write_properties") TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") { + ScopedFastFlag _{FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}; + CheckResult result = check(R"( function one(tbl: {x: any}) end function two(tbl: {x: string}) one(tbl) end -- ok, string <: any and any <: string @@ -4476,8 +4546,8 @@ TEST_CASE_FIXTURE(Fixture, "table_subtyping_error_suppression") // honestly not sure which of these is a better developer experience. if (FFlag::LuauSolverV2) { - CHECK_EQ(*tm->wantedType, *getBuiltins()->stringType); - CHECK_EQ(*tm->givenType, *getBuiltins()->numberType); + CHECK_EQ("{ x: any, y: string }", toString(tm->wantedType)); + CHECK_EQ("{ x: string, y: number }", toString(tm->givenType)); } else { @@ -5583,7 +5653,7 @@ TEST_CASE_FIXTURE(Fixture, "unsafe_bidirectional_mutation") { // It's kind of suspect that we allow multiple definitions of keys in // a single table. - LUAU_REQUIRE_NO_ERRORS(check(R"( + CheckResult result = check(R"( type F = { _G: () -> () } @@ -5595,7 +5665,9 @@ TEST_CASE_FIXTURE(Fixture, "unsafe_bidirectional_mutation") _G = {}, _G = _, }) - )")); + )"); + + LUAU_REQUIRE_NO_ERRORS(result); } TEST_CASE_FIXTURE(BuiltinsFixture, "function_call_in_indexer_with_compound_assign") @@ -5643,6 +5715,7 @@ TEST_CASE_FIXTURE(Fixture, "fuzz_match_literal_type_crash_again") } ) )"); + LUAU_REQUIRE_NO_ERRORS(result); } @@ -5847,4 +5920,167 @@ TEST_CASE_FIXTURE(Fixture, "oss_1859") CHECK_EQ("{ actions: { meow: (...any) -> string }, age: number, name: string }", toString(err->givenType)); } +TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1797_intersection_of_tables_arent_disjoint") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauRelateTablesAreNeverDisjoint, true}, + {FFlag::LuauRefineTablesWithReadType, true}, + }; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + --!strict + + export type Foo = { + foo: string, + } + + export type Bar = Foo & { + copy: (...any) -> any + } + + local function _test(nd: { bar: Bar? }) + local bar = nd.bar + if not bar then + return + end + print(bar) + end + )")); + + // This might be better as the expanded intersection, but the point of + // this test is that this _isn't_ `never`. + CHECK_EQ("Foo & { copy: (...any) -> any }", toString(requireTypeAtPosition({16, 20}))); +} + +TEST_CASE_FIXTURE(Fixture, "oss_1344") +{ + ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true}; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + --!strict + type t = { + value: string?, + } + + local t: t = {} + + if not t.value then + t.value = "" + end + + local s: string? = nil + + if not s then + s = "" + end + )")); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1651") +{ + ScopedFastFlag _{FFlag::LuauRefineTablesWithReadType, true}; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + --!strict + local MyModule = {} + MyModule._isEnabled = true :: boolean + + assert(MyModule._isEnabled, `type solver`) + MyModule._isEnabled = false + )")); +} + +TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, + }; + + LUAU_REQUIRE_NO_ERRORS(check(R"( + local function take(_: { foo: string? }) end + + take({ foo = "bar" }) + )")); +} + +TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_incorrect") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, + }; + + CheckResult results = check(R"( + local function take(_: { foo: string?, bing: number }) end + + take({ foo = "bar", bing = true }) + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, results); + + auto err = get(results.errors[0]); + CHECK_EQ(results.errors[0].location, Location{{3, 35}, {3, 39}}); + CHECK_EQ(toString(err->givenType), "boolean"); + CHECK_EQ(toString(err->wantedType), "number"); +} + +TEST_CASE_FIXTURE(Fixture, "narrow_table_literal_check_call_singleton") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, + }; + + CheckResult results = check(R"( + local function take(_: { foo: "foo" }) end + + take({ foo = "foo" }) + )"); + + LUAU_REQUIRE_NO_ERRORS(results); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "oss_1450") +{ + ScopedFastFlag sffs[] = { + {FFlag::LuauSolverV2, true}, + {FFlag::LuauAvoidGenericsLeakingDuringFunctionCallCheck, true}, + {FFlag::LuauTableLiteralSubtypeCheckFunctionCalls, true}, + {FFlag::LuauEagerGeneralization4, true}, + }; + + CheckResult results = check(R"( + local keycodes = { + Alt = 2, + Space = 3, + Tab = 4, + } + + type Keycode = keyof + local function sendInput(keycodes: { Keycode }) + print(keycodes) + end + + sendInput({"Alt"}) -- shouldn't error + sendInput( + { + "Alt", + "Space", + "Ctrl", -- should error + } + ) + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, results); + auto err = get(results.errors[0]); + REQUIRE(err); + // NOTE: Single line with just `"Ctrl"` + CHECK_EQ(results.errors[0].location, Location{{17, 16}, {17, 22}}); + CHECK_EQ(R"("Alt" | "Space" | "Tab")", toString(err->wantedType)); + // FIXME: CLI-157899 + CHECK_EQ(R"("Alt" | "Ctrl" | "Space" | "Tab")", toString(err->givenType)); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.test.cpp b/tests/TypeInfer.test.cpp index c3a12e68..187664a7 100644 --- a/tests/TypeInfer.test.cpp +++ b/tests/TypeInfer.test.cpp @@ -2443,4 +2443,15 @@ TEST_CASE_FIXTURE(Fixture, "fuzzer_infer_divergent_rw_props") )")); } +TEST_CASE_FIXTURE(Fixture, "read_table_type_refinements_persist_scope") +{ + ScopedFastFlag _{FFlag::LuauSolverV2, true}; + + LUAU_REQUIRE_ERRORS(check(R"( +_ = {n0=_,},if _._ then ... else if _[if _ then _ else ({nil,})].setmetatable then if _ then _ elseif l0 then ... elseif _.n0 then _ elseif function(l0) +return _._G,_ +end then _._G else ... + )")); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index a191d1e3..49dd8494 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -93,7 +93,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "tables_can_be_unified") TableType{{{"foo", {arena.freshType(getBuiltins(), globalScope->level)}}}, std::nullopt, globalScope->level, TableState::Unsealed}, }}; - CHECK_NE(*getMutable(&tableOne)->props["foo"].type(), *getMutable(&tableTwo)->props["foo"].type()); + CHECK_NE(*getMutable(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable(&tableTwo)->props["foo"].type_DEPRECATED()); state.tryUnify(&tableTwo, &tableOne); @@ -102,7 +102,7 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "tables_can_be_unified") state.log.commit(); - CHECK_EQ(*getMutable(&tableOne)->props["foo"].type(), *getMutable(&tableTwo)->props["foo"].type()); + CHECK_EQ(*getMutable(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable(&tableTwo)->props["foo"].type_DEPRECATED()); } TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved") @@ -125,14 +125,14 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "incompatible_tables_are_preserved") }, }}; - CHECK_NE(*getMutable(&tableOne)->props["foo"].type(), *getMutable(&tableTwo)->props["foo"].type()); + CHECK_NE(*getMutable(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable(&tableTwo)->props["foo"].type_DEPRECATED()); state.tryUnify(&tableTwo, &tableOne); CHECK(state.failure); CHECK_EQ(1, state.errors.size()); - CHECK_NE(*getMutable(&tableOne)->props["foo"].type(), *getMutable(&tableTwo)->props["foo"].type()); + CHECK_NE(*getMutable(&tableOne)->props["foo"].type_DEPRECATED(), *getMutable(&tableTwo)->props["foo"].type_DEPRECATED()); } TEST_CASE_FIXTURE(Fixture, "uninhabited_intersection_sub_never")