From f763f4c948c96a51e86c78c189d287059e10bf3c Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 27 Jan 2023 14:28:31 -0800 Subject: [PATCH] Sync to upstream/release/561 (#820) * Fix a potential debugger crash by adding checks for invalid stack index values --------- Co-authored-by: Arseny Kapoulkine Co-authored-by: Vyacheslav Egorov --- Analysis/include/Luau/Constraint.h | 1 + .../include/Luau/ConstraintGraphBuilder.h | 5 + Analysis/include/Luau/Normalize.h | 3 +- Analysis/include/Luau/Type.h | 2 + Analysis/include/Luau/VisitType.h | 3 - Analysis/src/BuiltinDefinitions.cpp | 56 +- Analysis/src/ConstraintGraphBuilder.cpp | 43 +- Analysis/src/ConstraintSolver.cpp | 37 +- Analysis/src/EmbeddedBuiltinDefinitions.cpp | 10 +- Analysis/src/Frontend.cpp | 2 +- Analysis/src/Normalize.cpp | 71 +- Analysis/src/Substitution.cpp | 3 +- Analysis/src/ToString.cpp | 8 +- Analysis/src/TxnLog.cpp | 2 - Analysis/src/Type.cpp | 4 +- Analysis/src/TypeChecker2.cpp | 10 +- Analysis/src/TypeInfer.cpp | 271 ++-- Analysis/src/TypeReduction.cpp | 90 +- Analysis/src/Unifier.cpp | 47 +- Ast/include/Luau/Ast.h | 483 +++---- Ast/src/Lexer.cpp | 10 +- Ast/src/Parser.cpp | 6 +- CLI/Repl.cpp | 33 +- CodeGen/include/Luau/CodeGen.h | 5 +- CodeGen/src/CodeGen.cpp | 53 +- CodeGen/src/EmitCommonX64.cpp | 16 +- CodeGen/src/EmitInstructionX64.cpp | 10 +- CodeGen/src/EmitInstructionX64.h | 2 +- CodeGen/src/IrAnalysis.cpp | 50 + CodeGen/src/IrAnalysis.h | 14 + CodeGen/src/IrBuilder.cpp | 11 +- CodeGen/src/IrLoweringX64.cpp | 1242 +++++++++++++++++ CodeGen/src/IrLoweringX64.h | 89 ++ Common/include/Luau/DenseHash.h | 8 +- Common/include/Luau/ExperimentalFlags.h | 1 - Compiler/src/Compiler.cpp | 3 +- Sources.cmake | 5 + VM/src/ldebug.cpp | 17 +- tests/AstJsonEncoder.test.cpp | 2 - tests/Autocomplete.test.cpp | 8 - tests/Compiler.test.cpp | 7 - tests/Conformance.test.cpp | 2 - tests/CostModel.test.cpp | 2 - tests/DenseHash.test.cpp | 79 ++ tests/Fixture.cpp | 3 +- tests/Fixture.h | 1 - tests/Lexer.test.cpp | 10 - tests/Linter.test.cpp | 2 - tests/Normalize.test.cpp | 46 +- tests/Parser.test.cpp | 14 - tests/ToDot.test.cpp | 2 +- tests/Transpiler.test.cpp | 4 - tests/TypeInfer.aliases.test.cpp | 10 +- tests/TypeInfer.functions.test.cpp | 6 - tests/TypeInfer.operators.test.cpp | 6 +- tests/TypeInfer.provisional.test.cpp | 2 - tests/TypeInfer.refinements.test.cpp | 8 +- tests/TypeInfer.singletons.test.cpp | 6 +- tests/TypeInfer.tables.test.cpp | 10 +- tests/TypeInfer.test.cpp | 8 +- tests/TypeInfer.unknownnever.test.cpp | 9 - tests/TypeReduction.test.cpp | 84 ++ tools/faillist.txt | 39 +- 63 files changed, 2334 insertions(+), 762 deletions(-) create mode 100644 CodeGen/src/IrAnalysis.cpp create mode 100644 CodeGen/src/IrAnalysis.h create mode 100644 CodeGen/src/IrLoweringX64.cpp create mode 100644 CodeGen/src/IrLoweringX64.h create mode 100644 tests/DenseHash.test.cpp diff --git a/Analysis/include/Luau/Constraint.h b/Analysis/include/Luau/Constraint.h index ec94eee96..f814cb9f7 100644 --- a/Analysis/include/Luau/Constraint.h +++ b/Analysis/include/Luau/Constraint.h @@ -89,6 +89,7 @@ struct NameConstraint { TypeId namedType; std::string name; + bool synthetic = false; }; // target ~ inst target diff --git a/Analysis/include/Luau/ConstraintGraphBuilder.h b/Analysis/include/Luau/ConstraintGraphBuilder.h index a1caf85af..aac99afc3 100644 --- a/Analysis/include/Luau/ConstraintGraphBuilder.h +++ b/Analysis/include/Luau/ConstraintGraphBuilder.h @@ -80,6 +80,8 @@ struct ConstraintGraphBuilder // A mapping of AST node to TypePackId. DenseHashMap astTypePacks{nullptr}; + DenseHashMap astExpectedTypes{nullptr}; + // If the node was applied as a function, this is the unspecialized type of // that expression. DenseHashMap astOriginalCallTypes{nullptr}; @@ -88,6 +90,8 @@ struct ConstraintGraphBuilder // overload that was selected. DenseHashMap astOverloadResolvedTypes{nullptr}; + + // Types resolved from type annotations. Analogous to astTypes. DenseHashMap astResolvedTypes{nullptr}; @@ -207,6 +211,7 @@ struct ConstraintGraphBuilder Inference check(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType); Inference check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional expectedType); Inference check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert); + Inference check(const ScopePtr& scope, AstExprInterpString* interpString); Inference check(const ScopePtr& scope, AstExprTable* expr, std::optional expectedType); std::tuple checkBinary(const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType); diff --git a/Analysis/include/Luau/Normalize.h b/Analysis/include/Luau/Normalize.h index 865a9c4d3..15dc7d4a1 100644 --- a/Analysis/include/Luau/Normalize.h +++ b/Analysis/include/Luau/Normalize.h @@ -253,7 +253,8 @@ struct NormalizedType TypeId threads; // The (meta)table part of the type. - // Each element of this set is a (meta)table type. + // Each element of this set is a (meta)table type, or the top `table` type. + // An empty set denotes never. TypeIds tables; // The function part of the type. diff --git a/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index 4962274c9..0136327dc 100644 --- a/Analysis/include/Luau/Type.h +++ b/Analysis/include/Luau/Type.h @@ -117,6 +117,7 @@ struct PrimitiveType String, Thread, Function, + Table, }; Type type; @@ -651,6 +652,7 @@ struct BuiltinTypes const TypeId threadType; const TypeId functionType; const TypeId classType; + const TypeId tableType; const TypeId trueType; const TypeId falseType; const TypeId anyType; diff --git a/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index e0ab12e7e..ff4dfc3c3 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/Analysis/include/Luau/VisitType.h @@ -9,7 +9,6 @@ #include "Luau/Type.h" LUAU_FASTINT(LuauVisitRecursionLimit) -LUAU_FASTFLAG(LuauCompleteVisitor); namespace Luau { @@ -322,8 +321,6 @@ struct GenericTypeVisitor if (visit(ty, *ntv)) traverse(ntv->ty); } - else if (!FFlag::LuauCompleteVisitor) - return visit_detail::unsee(seen, ty); else LUAU_ASSERT(!"GenericTypeVisitor::traverse(TypeId) is not exhaustive!"); diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index 1fb915e95..1a5a6bf65 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -15,9 +15,7 @@ #include -LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAGVARIABLE(LuauBuiltInMetatableNoBadSynthetic, false) -LUAU_FASTFLAG(LuauReportShadowedTypeAlias) /** FIXME: Many of these type definitions are not quite completely accurate. * @@ -252,11 +250,8 @@ void registerBuiltinTypes(Frontend& frontend) frontend.getGlobalScope()->addBuiltinTypeBinding("string", TypeFun{{}, frontend.builtinTypes->stringType}); frontend.getGlobalScope()->addBuiltinTypeBinding("boolean", TypeFun{{}, frontend.builtinTypes->booleanType}); frontend.getGlobalScope()->addBuiltinTypeBinding("thread", TypeFun{{}, frontend.builtinTypes->threadType}); - if (FFlag::LuauUnknownAndNeverType) - { - frontend.getGlobalScope()->addBuiltinTypeBinding("unknown", TypeFun{{}, frontend.builtinTypes->unknownType}); - frontend.getGlobalScope()->addBuiltinTypeBinding("never", TypeFun{{}, frontend.builtinTypes->neverType}); - } + frontend.getGlobalScope()->addBuiltinTypeBinding("unknown", TypeFun{{}, frontend.builtinTypes->unknownType}); + frontend.getGlobalScope()->addBuiltinTypeBinding("never", TypeFun{{}, frontend.builtinTypes->neverType}); } void registerBuiltinGlobals(TypeChecker& typeChecker) @@ -315,7 +310,7 @@ void registerBuiltinGlobals(TypeChecker& typeChecker) FunctionType{ {genericMT}, {}, - arena.addTypePack(TypePack{{FFlag::LuauUnknownAndNeverType ? tabTy : tableMetaMT, genericMT}}), + arena.addTypePack(TypePack{{tabTy, genericMT}}), arena.addTypePack(TypePack{{tableMetaMT}}) } ), "@luau" @@ -357,8 +352,7 @@ void registerBuiltinGlobals(Frontend& frontend) LUAU_ASSERT(!frontend.globalTypes.types.isFrozen()); LUAU_ASSERT(!frontend.globalTypes.typePacks.isFrozen()); - if (FFlag::LuauReportShadowedTypeAlias) - registerBuiltinTypes(frontend); + registerBuiltinTypes(frontend); TypeArena& arena = frontend.globalTypes; NotNull builtinTypes = frontend.builtinTypes; @@ -409,7 +403,7 @@ void registerBuiltinGlobals(Frontend& frontend) FunctionType{ {genericMT}, {}, - arena.addTypePack(TypePack{{FFlag::LuauUnknownAndNeverType ? tabTy : tableMetaMT, genericMT}}), + arena.addTypePack(TypePack{{tabTy, genericMT}}), arena.addTypePack(TypePack{{tableMetaMT}}) } ), "@luau" @@ -537,11 +531,8 @@ static std::optional> magicFunctionSetMetaTable( { auto [paramPack, _predicates] = withPredicate; - if (FFlag::LuauUnknownAndNeverType) - { - if (size(paramPack) < 2 && finite(paramPack)) - return std::nullopt; - } + if (size(paramPack) < 2 && finite(paramPack)) + return std::nullopt; TypeArena& arena = typechecker.currentModule->internalTypes; @@ -550,11 +541,8 @@ static std::optional> magicFunctionSetMetaTable( TypeId target = follow(expectedArgs[0]); TypeId mt = follow(expectedArgs[1]); - if (FFlag::LuauUnknownAndNeverType) - { - typechecker.tablify(target); - typechecker.tablify(mt); - } + typechecker.tablify(target); + typechecker.tablify(mt); if (const auto& tab = get(target)) { @@ -564,9 +552,6 @@ static std::optional> magicFunctionSetMetaTable( } else { - if (!FFlag::LuauUnknownAndNeverType) - typechecker.tablify(mt); - const TableType* mtTtv = get(mt); MetatableType mtv{target, mt}; if ((tab->name || tab->syntheticName) && (mtTtv && (mtTtv->name || mtTtv->syntheticName))) @@ -583,12 +568,7 @@ static std::optional> magicFunctionSetMetaTable( TypeId mtTy = arena.addType(mtv); if (expr.args.size < 1) - { - if (FFlag::LuauUnknownAndNeverType) - return std::nullopt; - else - return WithPredicate{}; - } + return std::nullopt; if (!expr.self) { @@ -635,20 +615,10 @@ static std::optional> magicFunctionAssert( if (head.size() > 0) { auto [ty, ok] = typechecker.pickTypesFromSense(head[0], true, typechecker.builtinTypes->nilType); - if (FFlag::LuauUnknownAndNeverType) - { - if (get(*ty)) - head = {*ty}; - else - head[0] = *ty; - } + if (get(*ty)) + head = {*ty}; else - { - if (!ty) - head = {typechecker.nilType}; - else - head[0] = *ty; - } + head[0] = *ty; } return WithPredicate{arena.addTypePack(TypePack{std::move(head), tail})}; diff --git a/Analysis/src/ConstraintGraphBuilder.cpp b/Analysis/src/ConstraintGraphBuilder.cpp index 7181e4f03..b6184e36e 100644 --- a/Analysis/src/ConstraintGraphBuilder.cpp +++ b/Analysis/src/ConstraintGraphBuilder.cpp @@ -414,6 +414,10 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local) std::vector varTypes; varTypes.reserve(local->vars.size); + // Used to name the first value type, even if it's not placed in varTypes, + // for the purpose of synthetic name attribution. + std::optional firstValueType; + for (AstLocal* local : local->vars) { TypeId ty = nullptr; @@ -456,6 +460,9 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local) else varTypes[i] = exprType; } + + if (i == 0) + firstValueType = exprType; } else { @@ -488,6 +495,22 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local) } } + if (local->vars.size == 1 && local->values.size == 1 && firstValueType) + { + AstLocal* var = local->vars.data[0]; + AstExpr* value = local->values.data[0]; + + if (value->is()) + addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true}); + else if (const AstExprCall* call = value->as()) + { + if (const AstExprGlobal* global = call->func->as(); global && global->name == "setmetatable") + { + addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true}); + } + } + } + for (size_t i = 0; i < local->vars.size; ++i) { AstLocal* l = local->vars.data[i]; @@ -1138,7 +1161,13 @@ InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCa TypeId resultTy = arena->addType(mtv); if (AstExprLocal* targetLocal = targetExpr->as()) + { scope->bindings[targetLocal->local].typeId = resultTy; + auto def = dfg->getDef(targetLocal->local); + if (def) + scope->dcrRefinements[*def] = resultTy; // TODO: typestates: track this as an assignment + } + return InferencePack{arena->addTypePack({resultTy}), std::move(returnConnectives)}; } @@ -1248,6 +1277,8 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr, st result = check(scope, ifElse, expectedType); else if (auto typeAssert = expr->as()) result = check(scope, typeAssert); + else if (auto interpString = expr->as()) + result = check(scope, interpString); else if (auto err = expr->as()) { // Open question: Should we traverse into this? @@ -1264,6 +1295,8 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr, st LUAU_ASSERT(result.ty); astTypes[expr] = result.ty; + if (expectedType) + astExpectedTypes[expr] = *expectedType; return result; } @@ -1509,6 +1542,14 @@ Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTypeAssert return Inference{resolveType(scope, typeAssert->annotation, /* inTypeArguments */ false)}; } +Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprInterpString* interpString) +{ + for (AstExpr* expr : interpString->expressions) + check(scope, expr); + + return Inference{builtinTypes->stringType}; +} + std::tuple ConstraintGraphBuilder::checkBinary( const ScopePtr& scope, AstExprBinary* binary, std::optional expectedType) { @@ -1551,7 +1592,7 @@ std::tuple ConstraintGraphBuilder::checkBinary( else if (typeguard->type == "boolean") discriminantTy = builtinTypes->threadType; else if (typeguard->type == "table") - discriminantTy = builtinTypes->neverType; // TODO: replace with top table type + discriminantTy = builtinTypes->tableType; else if (typeguard->type == "function") discriminantTy = builtinTypes->functionType; else if (typeguard->type == "userdata") diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 3fbd7d9e2..0a9b82bad 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -883,7 +883,12 @@ bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull(target)) - ttv->name = c.name; + { + if (c.synthetic && !ttv->name) + ttv->syntheticName = c.name; + else + ttv->name = c.name; + } else if (MetatableType* mtv = getMutable(target)) mtv->syntheticName = c.name; else if (get(target) || get(target)) @@ -1594,7 +1599,7 @@ bool ConstraintSolver::tryDispatchIterableFunction( const TypeId firstIndex = isNil(firstIndexTy) ? arena->freshType(constraint->scope) // FIXME: Surely this should be a union (free | nil) : firstIndexTy; - // nextTy : (tableTy, indexTy?) -> (indexTy, valueTailTy...) + // nextTy : (tableTy, indexTy?) -> (indexTy?, valueTailTy...) const TypePackId nextArgPack = arena->addTypePack({tableTy, arena->addType(UnionType{{firstIndex, builtinTypes->nilType}})}); const TypePackId valueTailTy = arena->addTypePack(FreeTypePack{constraint->scope}); const TypePackId nextRetPack = arena->addTypePack(TypePack{{firstIndex}, valueTailTy}); @@ -1602,7 +1607,25 @@ bool ConstraintSolver::tryDispatchIterableFunction( const TypeId expectedNextTy = arena->addType(FunctionType{TypeLevel{}, constraint->scope, nextArgPack, nextRetPack}); unify(nextTy, expectedNextTy, constraint->scope); - pushConstraint(constraint->scope, constraint->location, PackSubtypeConstraint{c.variables, nextRetPack}); + auto it = begin(nextRetPack); + std::vector modifiedNextRetHead; + + // The first value is never nil in the context of the loop, even if it's nil + // in the next function's return type, because the loop will not advance if + // it's nil. + if (it != end(nextRetPack)) + { + TypeId firstRet = *it; + TypeId modifiedFirstRet = stripNil(builtinTypes, *arena, firstRet); + modifiedNextRetHead.push_back(modifiedFirstRet); + ++it; + } + + for (; it != end(nextRetPack); ++it) + modifiedNextRetHead.push_back(*it); + + TypePackId modifiedNextRetPack = arena->addTypePack(std::move(modifiedNextRetHead), it.tail()); + pushConstraint(constraint->scope, constraint->location, PackSubtypeConstraint{c.variables, modifiedNextRetPack}); return true; } @@ -1649,8 +1672,8 @@ std::optional ConstraintSolver::lookupTableProp(TypeId subjectType, cons resultType = parts[0]; else if (parts.size() > 1) resultType = arena->addType(UnionType{std::move(parts)}); - else - LUAU_ASSERT(false); // parts.size() == 0 + + // otherwise, nothing: no matching property } else if (auto itv = get(subjectType)) { @@ -1662,8 +1685,8 @@ std::optional ConstraintSolver::lookupTableProp(TypeId subjectType, cons resultType = parts[0]; else if (parts.size() > 1) resultType = arena->addType(IntersectionType{std::move(parts)}); - else - LUAU_ASSERT(false); // parts.size() == 0 + + // otherwise, nothing: no matching property } return resultType; diff --git a/Analysis/src/EmbeddedBuiltinDefinitions.cpp b/Analysis/src/EmbeddedBuiltinDefinitions.cpp index 1fe09773c..364244ad3 100644 --- a/Analysis/src/EmbeddedBuiltinDefinitions.cpp +++ b/Analysis/src/EmbeddedBuiltinDefinitions.cpp @@ -1,8 +1,6 @@ // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details #include "Luau/BuiltinDefinitions.h" -LUAU_FASTFLAG(LuauUnknownAndNeverType) - namespace Luau { @@ -115,6 +113,7 @@ declare function typeof(value: T): string -- `assert` has a magic function attached that will give more detailed type information declare function assert(value: T, errorMessage: string?): T +declare function error(message: T, level: number?): never declare function tostring(value: T): string declare function tonumber(value: T, radix: number?): number? @@ -199,14 +198,7 @@ declare function unpack(tab: {V}, i: number?, j: number?): ...V std::string getBuiltinDefinitionSource() { - std::string result = kBuiltinDefinitionLuaSrc; - - if (FFlag::LuauUnknownAndNeverType) - result += "declare function error(message: T, level: number?): never\n"; - else - result += "declare function error(message: T, level: number?)\n"; - return result; } diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index f4e529dbe..d200df343 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -922,10 +922,10 @@ ModulePtr Frontend::check( for (TypeError& e : cs.errors) result->errors.emplace_back(std::move(e)); - result->scopes = std::move(cgb.scopes); result->astTypes = std::move(cgb.astTypes); result->astTypePacks = std::move(cgb.astTypePacks); + result->astExpectedTypes = std::move(cgb.astExpectedTypes); result->astOriginalCallTypes = std::move(cgb.astOriginalCallTypes); result->astOverloadResolvedTypes = std::move(cgb.astOverloadResolvedTypes); result->astResolvedTypes = std::move(cgb.astResolvedTypes); diff --git a/Analysis/src/Normalize.cpp b/Analysis/src/Normalize.cpp index 901144e4a..a7b2b7276 100644 --- a/Analysis/src/Normalize.cpp +++ b/Analysis/src/Normalize.cpp @@ -19,7 +19,7 @@ LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200); LUAU_FASTINTVARIABLE(LuauNormalizeCacheLimit, 100000); LUAU_FASTFLAGVARIABLE(LuauNegatedClassTypes, false); LUAU_FASTFLAGVARIABLE(LuauNegatedFunctionTypes, false); -LUAU_FASTFLAG(LuauUnknownAndNeverType) +LUAU_FASTFLAGVARIABLE(LuauNegatedTableTypes, false); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAG(LuauUninhabitedSubAnything2) @@ -448,8 +448,20 @@ static bool areNormalizedFunctions(const NormalizedFunctionType& tys) static bool areNormalizedTables(const TypeIds& tys) { for (TypeId ty : tys) - if (!get(ty) && !get(ty)) + { + if (get(ty) || get(ty)) + continue; + + const PrimitiveType* pt = get(ty); + if (!pt) return false; + + if (pt->type == PrimitiveType::Table && FFlag::LuauNegatedTableTypes) + continue; + + return false; + } + return true; } @@ -1216,7 +1228,25 @@ void Normalizer::unionTablesWithTable(TypeIds& heres, TypeId there) void Normalizer::unionTables(TypeIds& heres, const TypeIds& theres) { for (TypeId there : theres) - unionTablesWithTable(heres, there); + { + if (FFlag::LuauNegatedTableTypes) + { + if (there == builtinTypes->tableType) + { + heres.clear(); + heres.insert(there); + return; + } + else + { + unionTablesWithTable(heres, there); + } + } + else + { + unionTablesWithTable(heres, there); + } + } } // So why `ignoreSmallerTyvars`? @@ -1375,6 +1405,11 @@ bool Normalizer::unionNormalWithTy(NormalizedType& here, TypeId there, int ignor LUAU_ASSERT(FFlag::LuauNegatedFunctionTypes); here.functions.resetToTop(); } + else if (ptv->type == PrimitiveType::Table && FFlag::LuauNegatedTableTypes) + { + here.tables.clear(); + here.tables.insert(there); + } else LUAU_ASSERT(!"Unreachable"); } @@ -1504,6 +1539,21 @@ std::optional Normalizer::negateNormal(const NormalizedType& her return std::nullopt; } + /* + * It is not possible to negate an arbitrary table type, because function + * types are not runtime-testable. Thus, we prohibit negation of anything + * other than `table` and `never`. + */ + if (FFlag::LuauNegatedTableTypes) + { + if (here.tables.empty()) + result.tables.insert(builtinTypes->tableType); + else if (here.tables.size() == 1 && here.tables.front() == builtinTypes->tableType) + result.tables.clear(); + else + return std::nullopt; + } + // TODO: negating tables // TODO: negating tyvars? @@ -1571,6 +1621,10 @@ void Normalizer::subtractPrimitive(NormalizedType& here, TypeId ty) case PrimitiveType::Function: here.functions.resetToNever(); break; + case PrimitiveType::Table: + LUAU_ASSERT(FFlag::LuauNegatedTableTypes); + here.tables.clear(); + break; } } @@ -1995,6 +2049,11 @@ std::optional Normalizer::intersectionOfTables(TypeId here, TypeId there if (sharedState->counters.recursionLimit > 0 && sharedState->counters.recursionLimit < sharedState->counters.recursionCount) return std::nullopt; + if (isPrim(here, PrimitiveType::Table)) + return there; + else if (isPrim(there, PrimitiveType::Table)) + return here; + TypeId htable = here; TypeId hmtable = nullptr; if (const MetatableType* hmtv = get(here)) @@ -2522,6 +2581,7 @@ bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there) NormalizedStringType strings = std::move(here.strings); NormalizedFunctionType functions = std::move(here.functions); TypeId threads = here.threads; + TypeIds tables = std::move(here.tables); clearNormal(here); @@ -2540,6 +2600,11 @@ bool Normalizer::intersectNormalWithTy(NormalizedType& here, TypeId there) LUAU_ASSERT(FFlag::LuauNegatedFunctionTypes); here.functions = std::move(functions); } + else if (ptv->type == PrimitiveType::Table) + { + LUAU_ASSERT(FFlag::LuauNegatedTableTypes); + here.tables = std::move(tables); + } else LUAU_ASSERT(!"Unreachable"); } diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index 2469152eb..160647a05 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -12,7 +12,6 @@ LUAU_FASTFLAGVARIABLE(LuauSubstitutionFixMissingFields, false) LUAU_FASTFLAG(LuauClonePublicInterfaceLess) LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTFLAGVARIABLE(LuauClassTypeVarsInSubstitution, false) -LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAGVARIABLE(LuauSubstitutionReentrant, false) namespace Luau @@ -184,7 +183,7 @@ TarjanResult Tarjan::loop() if (currEdge == -1) { ++childCount; - if (childLimit > 0 && (FFlag::LuauUnknownAndNeverType ? childLimit <= childCount : childLimit < childCount)) + if (childLimit > 0 && childLimit <= childCount) return TarjanResult::TooManyChildren; stack.push_back(index); diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index 89d3c5557..1972177cf 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -14,7 +14,6 @@ #include LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) -LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAGVARIABLE(LuauFunctionReturnStringificationFixup, false) /* @@ -444,6 +443,9 @@ struct TypeStringifier case PrimitiveType::Function: state.emit("function"); return; + case PrimitiveType::Table: + state.emit("table"); + return; default: LUAU_ASSERT(!"Unknown primitive type"); throw InternalCompilerError("Unknown primitive type " + std::to_string(ptv.type)); @@ -823,7 +825,7 @@ struct TypeStringifier void operator()(TypeId, const ErrorType& tv) { state.result.error = true; - state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*"); + state.emit("*error-type*"); } void operator()(TypeId, const LazyType& ltv) @@ -962,7 +964,7 @@ struct TypePackStringifier void operator()(TypePackId, const Unifiable::Error& error) { state.result.error = true; - state.emit(FFlag::LuauUnknownAndNeverType ? "*error-type*" : "*unknown*"); + state.emit("*error-type*"); } void operator()(TypePackId, const VariadicTypePack& pack) diff --git a/Analysis/src/TxnLog.cpp b/Analysis/src/TxnLog.cpp index dacd82dc1..5040952e8 100644 --- a/Analysis/src/TxnLog.cpp +++ b/Analysis/src/TxnLog.cpp @@ -8,8 +8,6 @@ #include #include -LUAU_FASTFLAG(LuauUnknownAndNeverType) - namespace Luau { diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index 4b2165187..f29a02241 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -24,7 +24,6 @@ LUAU_FASTFLAG(DebugLuauFreezeArena) LUAU_FASTINTVARIABLE(LuauTypeMaximumStringifierLength, 500) LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINT(LuauTypeInferRecursionLimit) -LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAGVARIABLE(LuauMaybeGenericIntersectionTypes, false) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAGVARIABLE(LuauMatchReturnsOptionalString, false); @@ -213,7 +212,7 @@ bool isOptional(TypeId ty) ty = follow(ty); - if (get(ty) || (FFlag::LuauUnknownAndNeverType && get(ty))) + if (get(ty) || get(ty)) return true; auto utv = get(ty); @@ -761,6 +760,7 @@ BuiltinTypes::BuiltinTypes() , threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true})) , functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true})) , classType(arena->addType(Type{ClassType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}}, /*persistent*/ true})) + , tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true})) , trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true})) , falseType(arena->addType(Type{SingletonType{BooleanSingleton{false}}, /*persistent*/ true})) , anyType(arena->addType(Type{AnyType{}, /*persistent*/ true})) diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 1972f26f3..59c488fdf 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -372,7 +372,7 @@ struct TypeChecker2 break; } - AstLocal* var = local->vars.data[i]; + AstLocal* var = local->vars.data[j]; if (var->annotation) { TypeId varType = lookupAnnotation(var->annotation); @@ -755,6 +755,8 @@ struct TypeChecker2 return visit(e); else if (auto e = expr->as()) return visit(e); + else if (auto e = expr->as()) + return visit(e); else if (auto e = expr->as()) return visit(e); else @@ -1358,6 +1360,12 @@ struct TypeChecker2 visit(expr->falseExpr, RValue); } + void visit(AstExprInterpString* interpString) + { + for (AstExpr* expr : interpString->expressions) + visit(expr, RValue); + } + void visit(AstExprError* expr) { // TODO! diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index de52a5261..a25ddc7c4 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -35,18 +35,10 @@ LUAU_FASTFLAG(LuauKnowsTheDataModel3) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false. LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false) -LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false) -LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false) -LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false) LUAU_FASTFLAGVARIABLE(LuauScopelessModule, false) -LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false) LUAU_FASTFLAGVARIABLE(LuauTryhardAnd, false) LUAU_FASTFLAG(LuauInstantiateInSubtyping) -LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false) -LUAU_FASTFLAGVARIABLE(LuauReportShadowedTypeAlias, false) -LUAU_FASTFLAGVARIABLE(LuauBetterMessagingOnCountMismatch, false) LUAU_FASTFLAGVARIABLE(LuauIntersectionTestForEquality, false) -LUAU_FASTFLAGVARIABLE(LuauImplicitElseRefinement, false) LUAU_FASTFLAG(LuauNegatedClassTypes) LUAU_FASTFLAGVARIABLE(LuauAllowIndexClassParameters, false) LUAU_FASTFLAG(LuauUninhabitedSubAnything2) @@ -246,11 +238,8 @@ TypeChecker::TypeChecker(ModuleResolver* resolver, NotNull builtin globalScope->addBuiltinTypeBinding("string", TypeFun{{}, stringType}); globalScope->addBuiltinTypeBinding("boolean", TypeFun{{}, booleanType}); globalScope->addBuiltinTypeBinding("thread", TypeFun{{}, threadType}); - if (FFlag::LuauUnknownAndNeverType) - { - globalScope->addBuiltinTypeBinding("unknown", TypeFun{{}, unknownType}); - globalScope->addBuiltinTypeBinding("never", TypeFun{{}, neverType}); - } + globalScope->addBuiltinTypeBinding("unknown", TypeFun{{}, unknownType}); + globalScope->addBuiltinTypeBinding("never", TypeFun{{}, neverType}); } ModulePtr TypeChecker::check(const SourceModule& module, Mode mode, std::optional environmentScope) @@ -661,7 +650,7 @@ LUAU_NOINLINE void TypeChecker::checkBlockTypeAliases(const ScopePtr& scope, std Name name = typealias->name.value; - if (FFlag::LuauReportShadowedTypeAlias && duplicateTypeAliases.contains({typealias->exported, name})) + if (duplicateTypeAliases.contains({typealias->exported, name})) continue; TypeId type = bindings[name].type; @@ -1066,17 +1055,14 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local) // If the expression list only contains one expression and it's a function call or is otherwise within parentheses, use FunctionResult. // Otherwise, we'll want to use ExprListResult to make the error messaging more general. - CountMismatch::Context ctx = FFlag::LuauBetterMessagingOnCountMismatch ? CountMismatch::ExprListResult : CountMismatch::FunctionResult; - if (FFlag::LuauBetterMessagingOnCountMismatch) + CountMismatch::Context ctx = CountMismatch::ExprListResult; + if (local.values.size == 1) { - if (local.values.size == 1) - { - AstExpr* e = local.values.data[0]; - while (auto group = e->as()) - e = group->expr; - if (e->is()) - ctx = CountMismatch::FunctionResult; - } + AstExpr* e = local.values.data[0]; + while (auto group = e->as()) + e = group->expr; + if (e->is()) + ctx = CountMismatch::FunctionResult; } Unifier state = mkUnifier(scope, local.location); @@ -1438,11 +1424,8 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco checkFunctionBody(funScope, ty, *function.func); - if (FFlag::LuauUnknownAndNeverType) - { - InplaceDemoter demoter{funScope->level, ¤tModule->internalTypes}; - demoter.traverse(ty); - } + InplaceDemoter demoter{funScope->level, ¤tModule->internalTypes}; + demoter.traverse(ty); if (ttv && ttv->state != TableState::Sealed) ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation}; @@ -1591,12 +1574,9 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea Location location = scope->typeAliasLocations[name]; reportError(TypeError{typealias.location, DuplicateTypeDefinition{name, location}}); - if (!FFlag::LuauReportShadowedTypeAlias) - bindingsMap[name] = TypeFun{binding->typeParams, binding->typePackParams, errorRecoveryType(anyType)}; - duplicateTypeAliases.insert({typealias.exported, name}); } - else if (FFlag::LuauReportShadowedTypeAlias) + else { if (globalScope->builtinTypeNames.contains(name)) { @@ -1623,25 +1603,6 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatTypeAlias& typea scope->typeAliasNameLocations[name] = typealias.nameLocation; } } - else - { - ScopePtr aliasScope = childScope(scope, typealias.location); - aliasScope->level = scope->level.incr(); - aliasScope->level.subLevel = subLevel; - - auto [generics, genericPacks] = - createGenericTypes(aliasScope, scope->level, typealias, typealias.generics, typealias.genericPacks, /* useCache = */ true); - - TypeId ty = freshType(aliasScope); - FreeType* ftv = getMutable(ty); - LUAU_ASSERT(ftv); - ftv->forwardedTypeAlias = true; - bindingsMap[name] = {std::move(generics), std::move(genericPacks), ty}; - - scope->typeAliasLocations[name] = typealias.location; - if (FFlag::SupportTypeAliasGoToDeclaration) - scope->typeAliasNameLocations[name] = typealias.nameLocation; - } } void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& declaredClass) @@ -1840,7 +1801,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp else if (auto a = expr.as()) result = checkExpr(scope, *a); else if (auto a = expr.as()) - result = checkExpr(scope, *a, FFlag::LuauBinaryNeedsExpectedTypesToo ? expectedType : std::nullopt); + result = checkExpr(scope, *a, expectedType); else if (auto a = expr.as()) result = checkExpr(scope, *a); else if (auto a = expr.as()) @@ -2084,7 +2045,7 @@ std::optional TypeChecker::getIndexTypeFromTypeImpl( } std::vector result = reduceUnion(goodOptions); - if (FFlag::LuauUnknownAndNeverType && result.empty()) + if (result.empty()) return neverType; if (result.size() == 1) @@ -2432,13 +2393,8 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp operandType = stripFromNilAndReport(operandType, expr.location); // # operator is guaranteed to return number - if ((FFlag::LuauNeverTypesAndOperatorsInference && get(operandType)) || get(operandType) || get(operandType)) - { - if (FFlag::LuauNeverTypesAndOperatorsInference) - return {numberType}; - else - return {!FFlag::LuauUnknownAndNeverType ? errorRecoveryType(scope) : operandType}; - } + if (get(operandType) || get(operandType) || get(operandType)) + return {numberType}; DenseHashSet seen{nullptr}; @@ -2518,7 +2474,7 @@ TypeId TypeChecker::unionOfTypes(TypeId a, TypeId b, const ScopePtr& scope, cons return a; std::vector types = reduceUnion({a, b}); - if (FFlag::LuauUnknownAndNeverType && types.empty()) + if (types.empty()) return neverType; if (types.size() == 1) @@ -2649,12 +2605,9 @@ TypeId TypeChecker::checkRelationalOperation( case AstExprBinary::CompareGe: case AstExprBinary::CompareLe: { - if (FFlag::LuauNeverTypesAndOperatorsInference) - { - // If one of the operand is never, it doesn't make sense to unify these. - if (get(lhsType) || get(rhsType)) - return booleanType; - } + // If one of the operand is never, it doesn't make sense to unify these. + if (get(lhsType) || get(rhsType)) + return booleanType; if (FFlag::LuauIntersectionTestForEquality && isEquality) { @@ -2897,10 +2850,8 @@ TypeId TypeChecker::checkBinaryOperation( // If we know nothing at all about the lhs type, we can usually say nothing about the result. // The notable exception to this is the equality and inequality operators, which always produce a boolean. - const bool lhsIsAny = get(lhsType) || get(lhsType) || - (FFlag::LuauUnknownAndNeverType && FFlag::LuauNeverTypesAndOperatorsInference && get(lhsType)); - const bool rhsIsAny = get(rhsType) || get(rhsType) || - (FFlag::LuauUnknownAndNeverType && FFlag::LuauNeverTypesAndOperatorsInference && get(rhsType)); + const bool lhsIsAny = get(lhsType) || get(lhsType) || get(lhsType); + const bool rhsIsAny = get(rhsType) || get(rhsType) || get(rhsType); if (lhsIsAny) return lhsType; @@ -3102,7 +3053,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp return {trueType.type}; std::vector types = reduceUnion({trueType.type, falseType.type}); - if (FFlag::LuauUnknownAndNeverType && types.empty()) + if (types.empty()) return {neverType}; return {types.size() == 1 ? types[0] : addType(UnionType{std::move(types)})}; } @@ -3709,15 +3660,10 @@ void TypeChecker::checkFunctionBody(const ScopePtr& scope, TypeId ty, const AstE WithPredicate TypeChecker::checkExprPack(const ScopePtr& scope, const AstExpr& expr) { - if (FFlag::LuauUnknownAndNeverType) - { - WithPredicate result = checkExprPackHelper(scope, expr); - if (containsNever(result.type)) - return {uninhabitableTypePack}; - return result; - } - else - return checkExprPackHelper(scope, expr); + WithPredicate result = checkExprPackHelper(scope, expr); + if (containsNever(result.type)) + return {uninhabitableTypePack}; + return result; } WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope, const AstExpr& expr) @@ -3843,10 +3789,7 @@ void TypeChecker::checkArgumentList(const ScopePtr& scope, const AstExpr& funNam } TypePackId varPack = addTypePack(TypePackVar{TypePack{rest, paramIter.tail()}}); - if (FFlag::LuauReturnsFromCallsitesAreNotWidened) - state.tryUnify(tail, varPack); - else - state.tryUnify(varPack, tail); + state.tryUnify(tail, varPack); return; } } @@ -4031,24 +3974,13 @@ WithPredicate TypeChecker::checkExprPackHelper(const ScopePtr& scope return {errorRecoveryTypePack(scope)}; TypePack* args = nullptr; - if (FFlag::LuauUnknownAndNeverType) - { - if (expr.self) - { - argPack = addTypePack(TypePack{{selfType}, argPack}); - argListResult.type = argPack; - } - args = getMutable(argPack); - LUAU_ASSERT(args); - } - else + if (expr.self) { - args = getMutable(argPack); - LUAU_ASSERT(args != nullptr); - - if (expr.self) - args->head.insert(args->head.begin(), selfType); + argPack = addTypePack(TypePack{{selfType}, argPack}); + argListResult.type = argPack; } + args = getMutable(argPack); + LUAU_ASSERT(args); std::vector argLocations; argLocations.reserve(expr.args.size + 1); @@ -4107,7 +4039,7 @@ std::vector> TypeChecker::getExpectedTypesForCall(const st else { std::vector result = reduceUnion({*el, ty}); - if (FFlag::LuauUnknownAndNeverType && result.empty()) + if (result.empty()) el = neverType; else el = result.size() == 1 ? result[0] : addType(UnionType{std::move(result)}); @@ -4451,7 +4383,7 @@ WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, cons auto [typePack, exprPredicates] = checkExprPack(scope, *expr); insert(exprPredicates); - if (FFlag::LuauUnknownAndNeverType && containsNever(typePack)) + if (containsNever(typePack)) { // f(), g() where f() returns (never, string) or (string, never) means this whole TypePackId is uninhabitable, so return (never, // ...never) @@ -4474,7 +4406,7 @@ WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, cons auto [type, exprPredicates] = checkExpr(scope, *expr, expectedType); insert(exprPredicates); - if (FFlag::LuauUnknownAndNeverType && get(type)) + if (get(type)) { // f(), g() where f() returns (never, string) or (string, never) means this whole TypePackId is uninhabitable, so return (never, // ...never) @@ -4509,7 +4441,7 @@ WithPredicate TypeChecker::checkExprList(const ScopePtr& scope, cons for (TxnLog& log : inverseLogs) log.commit(); - if (FFlag::LuauUnknownAndNeverType && uninhabitable) + if (uninhabitable) return {uninhabitableTypePack}; return {pack, predicates}; } @@ -4997,16 +4929,8 @@ std::optional TypeChecker::filterMapImpl(TypeId type, TypeIdPredicate pr std::pair, bool> TypeChecker::filterMap(TypeId type, TypeIdPredicate predicate) { - if (FFlag::LuauUnknownAndNeverType) - { - TypeId ty = filterMapImpl(type, predicate).value_or(neverType); - return {ty, !bool(get(ty))}; - } - else - { - std::optional ty = filterMapImpl(type, predicate); - return {ty, bool(ty)}; - } + TypeId ty = filterMapImpl(type, predicate).value_or(neverType); + return {ty, !bool(get(ty))}; } std::pair, bool> TypeChecker::pickTypesFromSense(TypeId type, bool sense, TypeId emptySetTy) @@ -5587,18 +5511,7 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const if (!key) { auto [result, ok] = filterMap(*ty, predicate); - if (FFlag::LuauUnknownAndNeverType) - { - addRefinement(refis, *target, *result); - } - else - { - if (ok) - addRefinement(refis, *target, *result); - else - addRefinement(refis, *target, errorRecoveryType(scope)); - } - + addRefinement(refis, *target, *result); return; } @@ -5621,21 +5534,10 @@ void TypeChecker::refineLValue(const LValue& lvalue, RefinementMap& refis, const return; // Do nothing. An error was already reported, as per usual. auto [result, ok] = filterMap(*discriminantTy, predicate); - if (FFlag::LuauUnknownAndNeverType) + if (!get(*result)) { - if (!get(*result)) - { - viableTargetOptions.insert(option); - viableChildOptions.insert(*result); - } - } - else - { - if (ok) - { - viableTargetOptions.insert(option); - viableChildOptions.insert(*result); - } + viableTargetOptions.insert(option); + viableChildOptions.insert(*result); } } @@ -5891,7 +5793,7 @@ void TypeChecker::resolve(const TypeGuardPredicate& typeguardP, RefinementMap& r auto refine = [this, &lvalue = typeguardP.lvalue, &refis, &scope, sense](bool(f)(TypeId), std::optional mapsTo = std::nullopt) { TypeIdPredicate predicate = [f, mapsTo, sense](TypeId ty) -> std::optional { - if (FFlag::LuauUnknownAndNeverType && sense && get(ty)) + if (sense && get(ty)) return mapsTo.value_or(ty); if (f(ty) == sense) @@ -5985,56 +5887,44 @@ void TypeChecker::resolve(const EqPredicate& eqP, RefinementMap& refis, const Sc if (maybeSingleton(eqP.type)) { - if (FFlag::LuauImplicitElseRefinement) + bool optionIsSubtype = canUnify(option, eqP.type, scope, eqP.location).empty(); + bool targetIsSubtype = canUnify(eqP.type, option, scope, eqP.location).empty(); + + // terminology refresher: + // - option is the type of the expression `x`, and + // - eqP.type is the type of the expression `"hello"` + // + // "hello" == x where + // x : "hello" | "world" -> x : "hello" + // x : number | string -> x : "hello" + // x : number -> x : never + // + // "hello" ~= x where + // x : "hello" | "world" -> x : "world" + // x : number | string -> x : number | string + // x : number -> x : number + + // local variable works around an odd gcc 9.3 warning: may be used uninitialized + std::optional nope = std::nullopt; + + if (sense) { - bool optionIsSubtype = canUnify(option, eqP.type, scope, eqP.location).empty(); - bool targetIsSubtype = canUnify(eqP.type, option, scope, eqP.location).empty(); - - // terminology refresher: - // - option is the type of the expression `x`, and - // - eqP.type is the type of the expression `"hello"` - // - // "hello" == x where - // x : "hello" | "world" -> x : "hello" - // x : number | string -> x : "hello" - // x : number -> x : never - // - // "hello" ~= x where - // x : "hello" | "world" -> x : "world" - // x : number | string -> x : number | string - // x : number -> x : number - - // local variable works around an odd gcc 9.3 warning: may be used uninitialized - std::optional nope = std::nullopt; - - if (sense) - { - if (optionIsSubtype && !targetIsSubtype) - return option; - else if (!optionIsSubtype && targetIsSubtype) - return follow(eqP.type); - else if (!optionIsSubtype && !targetIsSubtype) - return nope; - else if (optionIsSubtype && targetIsSubtype) - return follow(eqP.type); - } - else - { - bool isOptionSingleton = get(option); - if (!isOptionSingleton) - return option; - else if (optionIsSubtype && targetIsSubtype) - return nope; - } + if (optionIsSubtype && !targetIsSubtype) + return option; + else if (!optionIsSubtype && targetIsSubtype) + return follow(eqP.type); + else if (!optionIsSubtype && !targetIsSubtype) + return nope; + else if (optionIsSubtype && targetIsSubtype) + return follow(eqP.type); } else { - if (!sense || canUnify(eqP.type, option, scope, eqP.location).empty()) - return sense ? eqP.type : option; - - // local variable works around an odd gcc 9.3 warning: may be used uninitialized - std::optional res = std::nullopt; - return res; + bool isOptionSingleton = get(option); + if (!isOptionSingleton) + return option; + else if (optionIsSubtype && targetIsSubtype) + return nope; } } @@ -6063,8 +5953,7 @@ std::vector TypeChecker::unTypePack(const ScopePtr& scope, TypePackId tp // HACK: tryUnify would undo the changes to the expectedTypePack if the length mismatches, but // we want to tie up free types to be error types, so we do this instead. - if (FFlag::LuauUnknownAndNeverType) - currentModule->errors.resize(oldErrorsSize); + currentModule->errors.resize(oldErrorsSize); for (TypeId& tp : expectedPack->head) tp = follow(tp); diff --git a/Analysis/src/TypeReduction.cpp b/Analysis/src/TypeReduction.cpp index c748f8632..3404c71d8 100644 --- a/Analysis/src/TypeReduction.cpp +++ b/Analysis/src/TypeReduction.cpp @@ -56,12 +56,6 @@ struct TypeReducer TypeId memoize(TypeId ty, TypeId reducedTy); TypePackId memoize(TypePackId tp, TypePackId reducedTp); - // It's either cyclic with no memoized result, so we should terminate, or - // there is a memoized result but one that's being reduced top-down, so - // we need to return the root of that memoized result to tighten up things. - TypeId memoizedOr(TypeId ty) const; - TypePackId memoizedOr(TypePackId tp) const; - using BinaryFold = std::optional (TypeReducer::*)(TypeId, TypeId); using UnaryFold = TypeId (TypeReducer::*)(TypeId); @@ -319,6 +313,24 @@ std::optional TypeReducer::intersectionType(TypeId left, TypeId right) } else if (auto [f, p] = get2(left, right); f && p) return intersectionType(right, left); // () -> () & P ~ P & () -> () + else if (auto [p, t] = get2(left, right); p && t) + { + if (p->type == PrimitiveType::Table) + return right; // table & {} ~ {} + else + return builtinTypes->neverType; // string & {} ~ never + } + else if (auto [p, t] = get2(left, right); p && t) + { + if (p->type == PrimitiveType::Table) + return right; // table & {} ~ {} + else + return builtinTypes->neverType; // string & {} ~ never + } + else if (auto [t, p] = get2(left, right); t && p) + return intersectionType(right, left); // {} & P ~ P & {} + else if (auto [t, p] = get2(left, right); t && p) + return intersectionType(right, left); // M & P ~ P & M else if (auto [s1, s2] = get2(left, right); s1 && s2) { if (*s1 == *s2) @@ -472,6 +484,20 @@ std::optional TypeReducer::intersectionType(TypeId left, TypeId right) else return right; // ~Base & Unrelated ~ Unrelated } + else if (auto [np, t] = get2(nlTy, right); np && t) + { + if (np->type == PrimitiveType::Table) + return builtinTypes->neverType; // ~table & {} ~ never + else + return right; // ~string & {} ~ {} + } + else if (auto [np, t] = get2(nlTy, right); np && t) + { + if (np->type == PrimitiveType::Table) + return builtinTypes->neverType; // ~table & {} ~ never + else + return right; // ~string & {} ~ {} + } else return std::nullopt; // TODO } @@ -529,6 +555,24 @@ std::optional TypeReducer::unionType(TypeId left, TypeId right) } else if (auto [f, p] = get2(left, right); f && p) return unionType(right, left); // () -> () | P ~ P | () -> () + else if (auto [p, t] = get2(left, right); p && t) + { + if (p->type == PrimitiveType::Table) + return left; // table | {} ~ table + else + return std::nullopt; // P | {} ~ P | {} + } + else if (auto [p, t] = get2(left, right); p && t) + { + if (p->type == PrimitiveType::Table) + return left; // table | {} ~ table + else + return std::nullopt; // P | {} ~ P | {} + } + else if (auto [t, p] = get2(left, right); t && p) + return unionType(right, left); // {} | P ~ P | {} + else if (auto [t, p] = get2(left, right); t && p) + return unionType(right, left); // M | P ~ P | M else if (auto [s1, s2] = get2(left, right); s1 && s2) { if (*s1 == *s2) @@ -642,6 +686,20 @@ std::optional TypeReducer::unionType(TypeId left, TypeId right) else return left; // ~Base | Unrelated ~ ~Base } + else if (auto [np, t] = get2(nlTy, right); np && t) + { + if (np->type == PrimitiveType::Table) + return std::nullopt; // ~table | {} ~ ~table | {} + else + return right; // ~P | {} ~ ~P | {} + } + else if (auto [np, t] = get2(nlTy, right); np && t) + { + if (np->type == PrimitiveType::Table) + return std::nullopt; // ~table | {} ~ ~table | {} + else + return right; // ~P | M ~ ~P | M + } else return std::nullopt; // TODO } @@ -850,26 +908,6 @@ TypePackId TypeReducer::memoize(TypePackId tp, TypePackId reducedTp) return reducedTp; } -TypeId TypeReducer::memoizedOr(TypeId ty) const -{ - ty = follow(ty); - - if (auto ctx = memoizedTypes->find(ty)) - return ctx->type; - else - return ty; -}; - -TypePackId TypeReducer::memoizedOr(TypePackId tp) const -{ - tp = follow(tp); - - if (auto ctx = memoizedTypePacks->find(tp)) - return ctx->type; - else - return tp; -}; - struct MarkCycles : TypeVisitor { DenseHashSet cyclicTypes{nullptr}; diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index 80f63f10a..e6d614411 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -15,10 +15,8 @@ #include -LUAU_FASTINT(LuauTypeInferTypePackLoopLimit); -LUAU_FASTFLAG(LuauErrorRecoveryType); -LUAU_FASTFLAG(LuauUnknownAndNeverType) -LUAU_FASTFLAGVARIABLE(LuauScalarShapeSubtyping, false) +LUAU_FASTINT(LuauTypeInferTypePackLoopLimit) +LUAU_FASTFLAG(LuauErrorRecoveryType) LUAU_FASTFLAGVARIABLE(LuauUnifyAnyTxnLog, false) LUAU_FASTFLAGVARIABLE(LuauInstantiateInSubtyping, false) LUAU_FASTFLAGVARIABLE(LuauScalarShapeUnifyToMtOwner2, false) @@ -28,6 +26,7 @@ LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAG(LuauNegatedFunctionTypes) LUAU_FASTFLAG(LuauNegatedClassTypes) +LUAU_FASTFLAG(LuauNegatedTableTypes) namespace Luau { @@ -452,13 +451,10 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool } else if (subFree) { - if (FFlag::LuauUnknownAndNeverType) - { - // Normally, if the subtype is free, it should not be bound to any, unknown, or error types. - // But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors. - if (log.get(superTy)) - return; - } + // Normally, if the subtype is free, it should not be bound to any, unknown, or error types. + // But for bug compatibility, we'll only apply this rule to unknown. Doing this will silence cascading type errors. + if (log.get(superTy)) + return; // Unification can't change the level of a generic. auto superGeneric = log.getMutable(superTy); @@ -569,6 +565,11 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool // Ok. Do nothing. forall functions F, F <: function } + else if (FFlag::LuauNegatedTableTypes && isPrim(superTy, PrimitiveType::Table) && (get(subTy) || get(subTy))) + { + // Ok, do nothing: forall tables T, T <: table + } + else if (log.getMutable(superTy) && log.getMutable(subTy)) tryUnifyFunctions(subTy, superTy, isFunctionCall); @@ -576,11 +577,11 @@ void Unifier::tryUnify_(TypeId subTy, TypeId superTy, bool isFunctionCall, bool { tryUnifyTables(subTy, superTy, isIntersection); } - else if (FFlag::LuauScalarShapeSubtyping && log.get(superTy) && (log.get(subTy) || log.get(subTy))) + else if (log.get(superTy) && (log.get(subTy) || log.get(subTy))) { tryUnifyScalarShape(subTy, superTy, /*reversed*/ false); } - else if (FFlag::LuauScalarShapeSubtyping && log.get(subTy) && (log.get(superTy) || log.get(superTy))) + else if (log.get(subTy) && (log.get(superTy) || log.get(superTy))) { tryUnifyScalarShape(subTy, superTy, /*reversed*/ true); } @@ -1032,6 +1033,12 @@ void Unifier::tryUnifyNormalizedTypes( bool found = false; for (TypeId superTable : superNorm.tables) { + if (FFlag::LuauNegatedTableTypes && isPrim(superTable, PrimitiveType::Table)) + { + found = true; + break; + } + Unifier innerState = makeChildUnifier(); if (get(superTable)) innerState.tryUnifyWithMetatable(subTable, superTable, /* reversed */ false); @@ -2031,8 +2038,6 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection) void Unifier::tryUnifyScalarShape(TypeId subTy, TypeId superTy, bool reversed) { - LUAU_ASSERT(FFlag::LuauScalarShapeSubtyping); - TypeId osubTy = subTy; TypeId osuperTy = superTy; @@ -2490,22 +2495,14 @@ void Unifier::tryUnifyWithAny(TypeId subTy, TypeId anyTy) return; } - TypePackId anyTp; - if (FFlag::LuauUnknownAndNeverType) - anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}}); - else - { - const TypePackId anyTypePack = types->addTypePack(TypePackVar{VariadicTypePack{builtinTypes->anyType}}); - anyTp = get(anyTy) ? anyTypePack : types->addTypePack(TypePackVar{Unifiable::Error{}}); - } + TypePackId anyTp = types->addTypePack(TypePackVar{VariadicTypePack{anyTy}}); std::vector queue = {subTy}; sharedState.tempSeenTy.clear(); sharedState.tempSeenTp.clear(); - Luau::tryUnifyWithAny( - queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, FFlag::LuauUnknownAndNeverType ? anyTy : builtinTypes->anyType, anyTp); + Luau::tryUnifyWithAny(queue, *this, sharedState.tempSeenTy, sharedState.tempSeenTp, types, anyTy, anyTp); } void Unifier::tryUnifyWithAny(TypePackId subTy, TypePackId anyTp) diff --git a/Ast/include/Luau/Ast.h b/Ast/include/Luau/Ast.h index 7731312db..81221dd10 100644 --- a/Ast/include/Luau/Ast.h +++ b/Ast/include/Luau/Ast.h @@ -52,246 +52,12 @@ struct AstName } }; -class AstVisitor -{ -public: - virtual ~AstVisitor() {} - - virtual bool visit(class AstNode*) - { - return true; - } - - virtual bool visit(class AstExpr* node) - { - return visit((class AstNode*)node); - } - - virtual bool visit(class AstExprGroup* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprConstantNil* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprConstantBool* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprConstantNumber* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprConstantString* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprLocal* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprGlobal* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprVarargs* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprCall* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprIndexName* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprIndexExpr* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprFunction* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprTable* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprUnary* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprBinary* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprTypeAssertion* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprIfElse* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprInterpString* node) - { - return visit((class AstExpr*)node); - } - virtual bool visit(class AstExprError* node) - { - return visit((class AstExpr*)node); - } - - virtual bool visit(class AstStat* node) - { - return visit((class AstNode*)node); - } - - virtual bool visit(class AstStatBlock* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatIf* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatWhile* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatRepeat* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatBreak* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatContinue* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatReturn* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatExpr* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatLocal* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatFor* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatForIn* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatAssign* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatCompoundAssign* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatFunction* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatLocalFunction* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatTypeAlias* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatDeclareFunction* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatDeclareGlobal* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatDeclareClass* node) - { - return visit((class AstStat*)node); - } - virtual bool visit(class AstStatError* node) - { - return visit((class AstStat*)node); - } - - // By default visiting type annotations is disabled; override this in your visitor if you need to! - virtual bool visit(class AstType* node) - { - return false; - } - - virtual bool visit(class AstTypeReference* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeTable* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeFunction* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeTypeof* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeUnion* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeIntersection* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeSingletonBool* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeSingletonString* node) - { - return visit((class AstType*)node); - } - virtual bool visit(class AstTypeError* node) - { - return visit((class AstType*)node); - } - - virtual bool visit(class AstTypePack* node) - { - return false; - } - virtual bool visit(class AstTypePackExplicit* node) - { - return visit((class AstTypePack*)node); - } - virtual bool visit(class AstTypePackVariadic* node) - { - return visit((class AstTypePack*)node); - } - virtual bool visit(class AstTypePackGeneric* node) - { - return visit((class AstTypePack*)node); - } -}; - class AstType; +class AstVisitor; +class AstStat; +class AstStatBlock; +class AstExpr; +class AstTypePack; struct AstLocal { @@ -1277,6 +1043,245 @@ class AstTypePackGeneric : public AstTypePack AstName genericName; }; +class AstVisitor +{ +public: + virtual ~AstVisitor() {} + + virtual bool visit(class AstNode*) + { + return true; + } + + virtual bool visit(class AstExpr* node) + { + return visit(static_cast(node)); + } + + virtual bool visit(class AstExprGroup* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprConstantNil* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprConstantBool* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprConstantNumber* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprConstantString* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprLocal* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprGlobal* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprVarargs* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprCall* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprIndexName* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprIndexExpr* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprFunction* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprTable* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprUnary* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprBinary* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprTypeAssertion* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprIfElse* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprInterpString* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstExprError* node) + { + return visit(static_cast(node)); + } + + virtual bool visit(class AstStat* node) + { + return visit(static_cast(node)); + } + + virtual bool visit(class AstStatBlock* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatIf* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatWhile* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatRepeat* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatBreak* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatContinue* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatReturn* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatExpr* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatLocal* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatFor* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatForIn* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatAssign* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatCompoundAssign* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatFunction* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatLocalFunction* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatTypeAlias* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatDeclareFunction* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatDeclareGlobal* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatDeclareClass* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstStatError* node) + { + return visit(static_cast(node)); + } + + // By default visiting type annotations is disabled; override this in your visitor if you need to! + virtual bool visit(class AstType* node) + { + return false; + } + + virtual bool visit(class AstTypeReference* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeTable* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeFunction* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeTypeof* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeUnion* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeIntersection* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeSingletonBool* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeSingletonString* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypeError* node) + { + return visit(static_cast(node)); + } + + virtual bool visit(class AstTypePack* node) + { + return false; + } + virtual bool visit(class AstTypePackExplicit* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypePackVariadic* node) + { + return visit(static_cast(node)); + } + virtual bool visit(class AstTypePackGeneric* node) + { + return visit(static_cast(node)); + } +}; + AstName getIdentifier(AstExpr*); Location getLocation(const AstTypeList& typeList); diff --git a/Ast/src/Lexer.cpp b/Ast/src/Lexer.cpp index 66436acde..118b06798 100644 --- a/Ast/src/Lexer.cpp +++ b/Ast/src/Lexer.cpp @@ -6,8 +6,6 @@ #include -LUAU_FASTFLAG(LuauInterpolatedStringBaseSupport) - namespace Luau { @@ -835,13 +833,7 @@ Lexeme Lexer::readNext() return readQuotedString(); case '`': - if (FFlag::LuauInterpolatedStringBaseSupport) - return readInterpolatedStringBegin(); - else - { - consume(); - return Lexeme(Location(start, 1), '`'); - } + return readInterpolatedStringBegin(); case '.': consume(); diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index c71bd7c58..99a41938b 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -17,8 +17,6 @@ LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100) LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false) -LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, false) - LUAU_FASTFLAGVARIABLE(LuauParserErrorsOnMissingDefaultTypePackArgument, false) bool lua_telemetry_parsed_out_of_range_bin_integer = false; @@ -2174,11 +2172,11 @@ AstExpr* Parser::parseSimpleExpr() return parseNumber(); } else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString || - (FFlag::LuauInterpolatedStringBaseSupport && lexer.current().type == Lexeme::InterpStringSimple)) + lexer.current().type == Lexeme::InterpStringSimple) { return parseString(); } - else if (FFlag::LuauInterpolatedStringBaseSupport && lexer.current().type == Lexeme::InterpStringBegin) + else if (lexer.current().type == Lexeme::InterpStringBegin) { return parseInterpString(); } diff --git a/CLI/Repl.cpp b/CLI/Repl.cpp index e567725e5..69a40356b 100644 --- a/CLI/Repl.cpp +++ b/CLI/Repl.cpp @@ -48,8 +48,10 @@ enum class CompileFormat Text, Binary, Remarks, - Codegen, - CodegenVerbose, + Codegen, // Prints annotated native code including IR and assembly + CodegenAsm, // Prints annotated native code assembly + CodegenIr, // Prints annotated native code IR + CodegenVerbose, // Prints annotated native code including IR, assembly and outlined code CodegenNull, Null }; @@ -716,7 +718,19 @@ static bool compileFile(const char* name, CompileFormat format, CompileStats& st try { Luau::BytecodeBuilder bcb; - Luau::CodeGen::AssemblyOptions options = {format == CompileFormat::CodegenNull, format == CompileFormat::Codegen, annotateInstruction, &bcb}; + + Luau::CodeGen::AssemblyOptions options; + options.outputBinary = format == CompileFormat::CodegenNull; + + if (!options.outputBinary) + { + options.includeAssembly = format != CompileFormat::CodegenIr; + options.includeIr = format != CompileFormat::CodegenAsm; + options.includeOutlinedCode = format == CompileFormat::CodegenVerbose; + } + + options.annotator = annotateInstruction; + options.annotatorContext = &bcb; if (format == CompileFormat::Text) { @@ -729,7 +743,8 @@ static bool compileFile(const char* name, CompileFormat format, CompileStats& st bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks); bcb.setDumpSource(*source); } - else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenVerbose) + else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr || + format == CompileFormat::CodegenVerbose) { bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals | Luau::BytecodeBuilder::Dump_Remarks); @@ -760,6 +775,8 @@ static bool compileFile(const char* name, CompileFormat format, CompileStats& st fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout); break; case CompileFormat::Codegen: + case CompileFormat::CodegenAsm: + case CompileFormat::CodegenIr: case CompileFormat::CodegenVerbose: printf("%s", getCodegenAssembly(name, bcb.getBytecode(), options).c_str()); break; @@ -850,6 +867,14 @@ int replMain(int argc, char** argv) { compileFormat = CompileFormat::Codegen; } + else if (strcmp(argv[1], "--compile=codegenasm") == 0) + { + compileFormat = CompileFormat::CodegenAsm; + } + else if (strcmp(argv[1], "--compile=codegenir") == 0) + { + compileFormat = CompileFormat::CodegenIr; + } else if (strcmp(argv[1], "--compile=codegenverbose") == 0) { compileFormat = CompileFormat::CodegenVerbose; diff --git a/CodeGen/include/Luau/CodeGen.h b/CodeGen/include/Luau/CodeGen.h index cef9ec7cb..84cf682f6 100644 --- a/CodeGen/include/Luau/CodeGen.h +++ b/CodeGen/include/Luau/CodeGen.h @@ -22,7 +22,10 @@ using annotatorFn = void (*)(void* context, std::string& result, int fid, int in struct AssemblyOptions { bool outputBinary = false; - bool skipOutlinedCode = false; + + bool includeAssembly = false; + bool includeIr = false; + bool includeOutlinedCode = false; // Optional annotator function can be provided to describe each instruction, it takes function id and sequential instruction id annotatorFn annotator = nullptr; diff --git a/CodeGen/src/CodeGen.cpp b/CodeGen/src/CodeGen.cpp index 405b92ddd..4ed950c07 100644 --- a/CodeGen/src/CodeGen.cpp +++ b/CodeGen/src/CodeGen.cpp @@ -13,6 +13,9 @@ #include "CodeGenX64.h" #include "EmitCommonX64.h" #include "EmitInstructionX64.h" +#include "IrAnalysis.h" +#include "IrBuilder.h" +#include "IrLoweringX64.h" #include "NativeState.h" #include "lapi.h" @@ -27,6 +30,8 @@ #endif #endif +LUAU_FASTFLAGVARIABLE(DebugUseOldCodegen, false) + namespace Luau { namespace CodeGen @@ -241,7 +246,7 @@ static int emitInst(AssemblyBuilderX64& build, NativeState& data, ModuleHelpers& skip = emitInstFastCall2K(build, pc, i, next); break; case LOP_FORNPREP: - emitInstForNPrep(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)]); + emitInstForNPrep(build, pc, i, next, labelarr[i + 1 + LUAU_INSN_D(*pc)]); break; case LOP_FORNLOOP: emitInstForNLoop(build, pc, i, labelarr[i + 1 + LUAU_INSN_D(*pc)], next); @@ -404,7 +409,7 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat result->proto = proto; - if (build.logText) + if (options.includeAssembly || options.includeIr) { if (proto->debugname) build.logAppend("; function %s()", getstr(proto->debugname)); @@ -417,6 +422,38 @@ static NativeProto* assembleFunction(AssemblyBuilderX64& build, NativeState& dat build.logAppend("\n"); } + if (!FFlag::DebugUseOldCodegen) + { + build.align(kFunctionAlignment, AlignmentDataX64::Ud2); + + Label start = build.setLabel(); + + IrBuilder builder; + builder.buildFunctionIr(proto); + + updateUseInfo(builder.function); + + IrLoweringX64 lowering(build, helpers, data, proto, builder.function); + + lowering.lower(options); + + result->instTargets = new uintptr_t[proto->sizecode]; + + for (int i = 0; i < proto->sizecode; i++) + { + auto [irLocation, asmLocation] = builder.function.bcMapping[i]; + + result->instTargets[i] = irLocation == ~0u ? 0 : asmLocation - start.location; + } + + result->location = start.location; + + if (build.logText) + build.logAppend("\n"); + + return result; + } + std::vector