diff --git a/Analysis/include/Luau/Frontend.h b/Analysis/include/Luau/Frontend.h index fc9bc54f2..27a67f40c 100644 --- a/Analysis/include/Luau/Frontend.h +++ b/Analysis/include/Luau/Frontend.h @@ -191,7 +191,7 @@ struct Frontend void queueModuleCheck(const std::vector& names); void queueModuleCheck(const ModuleName& name); std::vector checkQueuedModules(std::optional optionOverride = {}, - std::function task)> executeTask = {}, std::function progress = {}); + std::function task)> executeTask = {}, std::function progress = {}); std::optional getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false); diff --git a/Analysis/include/Luau/TypeFamily.h b/Analysis/include/Luau/TypeFamily.h index 7c68d815f..9d2182dfe 100644 --- a/Analysis/include/Luau/TypeFamily.h +++ b/Analysis/include/Luau/TypeFamily.h @@ -180,12 +180,11 @@ struct BuiltinTypeFamilies TypeFamily rawkeyofFamily; TypeFamily indexFamily; + TypeFamily rawgetFamily; void addToScope(NotNull arena, NotNull scope) const; }; - - -const BuiltinTypeFamilies kBuiltinTypeFamilies{}; +const BuiltinTypeFamilies& builtinTypeFunctions(); } // namespace Luau diff --git a/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index ff0656d6c..8c0f5ed9c 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/Analysis/include/Luau/VisitType.h @@ -348,16 +348,38 @@ struct GenericTypeVisitor { if (visit(ty, *utv)) { + bool unionChanged = false; for (TypeId optTy : utv->options) + { traverse(optTy); + if (!get(follow(ty))) + { + unionChanged = true; + break; + } + } + + if (unionChanged) + traverse(ty); } } else if (auto itv = get(ty)) { if (visit(ty, *itv)) { + bool intersectionChanged = false; for (TypeId partTy : itv->parts) + { traverse(partTy); + if (!get(follow(ty))) + { + intersectionChanged = true; + break; + } + } + + if (intersectionChanged) + traverse(ty); } } else if (auto ltv = get(ty)) diff --git a/Analysis/src/AstJsonEncoder.cpp b/Analysis/src/AstJsonEncoder.cpp index 470d69b32..3507a68f7 100644 --- a/Analysis/src/AstJsonEncoder.cpp +++ b/Analysis/src/AstJsonEncoder.cpp @@ -8,6 +8,8 @@ #include +LUAU_FASTFLAG(LuauDeclarationExtraPropData) + namespace Luau { @@ -735,8 +737,21 @@ struct AstJsonEncoder : public AstVisitor void write(class AstStatDeclareFunction* node) { writeNode(node, "AstStatDeclareFunction", [&]() { + // TODO: attributes PROP(name); + + if (FFlag::LuauDeclarationExtraPropData) + PROP(nameLocation); + PROP(params); + + if (FFlag::LuauDeclarationExtraPropData) + { + PROP(paramNames); + PROP(vararg); + PROP(varargLocation); + } + PROP(retTypes); PROP(generics); PROP(genericPacks); @@ -747,6 +762,10 @@ struct AstJsonEncoder : public AstVisitor { writeNode(node, "AstStatDeclareGlobal", [&]() { PROP(name); + + if (FFlag::LuauDeclarationExtraPropData) + PROP(nameLocation); + PROP(type); }); } @@ -756,8 +775,16 @@ struct AstJsonEncoder : public AstVisitor writeRaw("{"); bool c = pushComma(); write("name", prop.name); + + if (FFlag::LuauDeclarationExtraPropData) + write("nameLocation", prop.nameLocation); + writeType("AstDeclaredClassProp"); write("luauType", prop.ty); + + if (FFlag::LuauDeclarationExtraPropData) + write("location", prop.location); + popComma(c); writeRaw("}"); } diff --git a/Analysis/src/Autocomplete.cpp b/Analysis/src/Autocomplete.cpp index d6f0ab833..0dab640fa 100644 --- a/Analysis/src/Autocomplete.cpp +++ b/Analysis/src/Autocomplete.cpp @@ -1830,12 +1830,21 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName if (!sourceModule) return {}; - ModulePtr module = frontend.moduleResolverForAutocomplete.getModule(moduleName); + ModulePtr module; + if (FFlag::DebugLuauDeferredConstraintResolution) + module = frontend.moduleResolver.getModule(moduleName); + else + module = frontend.moduleResolverForAutocomplete.getModule(moduleName); + if (!module) return {}; NotNull builtinTypes = frontend.builtinTypes; - Scope* globalScope = frontend.globalsForAutocomplete.globalScope.get(); + Scope* globalScope; + if (FFlag::DebugLuauDeferredConstraintResolution) + globalScope = frontend.globals.globalScope.get(); + else + globalScope = frontend.globalsForAutocomplete.globalScope.get(); TypeArena typeArena; return autocomplete(*sourceModule, module, builtinTypes, &typeArena, globalScope, position, callback); diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index 2393bd2ae..582d5a7da 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -216,7 +216,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC NotNull builtinTypes = globals.builtinTypes; if (FFlag::DebugLuauDeferredConstraintResolution) - kBuiltinTypeFamilies.addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()}); + builtinTypeFunctions().addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()}); LoadDefinitionFileResult loadResult = frontend.loadDefinitionFile( globals, globals.globalScope, getBuiltinDefinitionSource(), "@luau", /* captureComments */ false, typeCheckForAutocomplete); @@ -313,7 +313,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC // declare function assert(value: T, errorMessage: string?): intersect TypeId genericT = arena.addType(GenericType{"T"}); TypeId refinedTy = arena.addType(TypeFamilyInstanceType{ - NotNull{&kBuiltinTypeFamilies.intersectFamily}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}}); + NotNull{&builtinTypeFunctions().intersectFamily}, {genericT, arena.addType(NegationType{builtinTypes->falsyType})}, {}}); TypeId assertTy = arena.addType(FunctionType{ {genericT}, {}, arena.addTypePack(TypePack{{genericT, builtinTypes->optionalStringType}}), arena.addTypePack(TypePack{{refinedTy}})}); diff --git a/Analysis/src/ConstraintGenerator.cpp b/Analysis/src/ConstraintGenerator.cpp index 7d92d9ff3..f1ae5eaea 100644 --- a/Analysis/src/ConstraintGenerator.cpp +++ b/Analysis/src/ConstraintGenerator.cpp @@ -29,6 +29,7 @@ LUAU_FASTINT(LuauCheckRecursionLimit); LUAU_FASTFLAG(DebugLuauLogSolverToJson); LUAU_FASTFLAG(DebugLuauMagicTypes); LUAU_FASTFLAG(LuauAttributeSyntax); +LUAU_FASTFLAG(LuauDeclarationExtraPropData); namespace Luau { @@ -431,7 +432,7 @@ void ConstraintGenerator::computeRefinement(const ScopePtr& scope, Location loca discriminantTy = arena->addType(NegationType{discriminantTy}); if (eq) - discriminantTy = createTypeFamilyInstance(kBuiltinTypeFamilies.singletonFamily, {discriminantTy}, {}, scope, location); + discriminantTy = createTypeFamilyInstance(builtinTypeFunctions().singletonFamily, {discriminantTy}, {}, scope, location); for (const RefinementKey* key = proposition->key; key; key = key->parent) { @@ -543,7 +544,7 @@ void ConstraintGenerator::applyRefinements(const ScopePtr& scope, Location locat { if (mustDeferIntersection(ty) || mustDeferIntersection(dt)) { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.refineFamily, {ty, dt}, {}, scope, location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().refineFamily, {ty, dt}, {}, scope, location); ty = resultType; } @@ -1389,6 +1390,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas ftv->argTypes = addTypePack({classTy}, ftv->argTypes); ftv->hasSelf = true; + + if (FFlag::LuauDeclarationExtraPropData) + { + FunctionDefinition defn; + + defn.definitionModuleName = module->name; + defn.definitionLocation = prop.location; + // No data is preserved for varargLocation + defn.originalNameLocation = prop.nameLocation; + + ftv->definition = defn; + } } } @@ -1396,7 +1409,38 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas if (props.count(propName) == 0) { - props[propName] = {propTy}; + if (FFlag::LuauDeclarationExtraPropData) + props[propName] = {propTy, /*deprecated*/ false, /*deprecatedSuggestion*/ "", prop.location}; + else + props[propName] = {propTy}; + } + else if (FFlag::LuauDeclarationExtraPropData) + { + 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)) + { + 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(declaredClass->location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())}); + } } else { @@ -1453,7 +1497,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false); TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false); - TypeId fnType = arena->addType(FunctionType{TypeLevel{}, funScope.get(), std::move(genericTys), std::move(genericTps), paramPack, retPack}); + + FunctionDefinition defn; + + if (FFlag::LuauDeclarationExtraPropData) + { + defn.definitionModuleName = module->name; + defn.definitionLocation = global->location; + defn.varargLocation = global->vararg ? std::make_optional(global->varargLocation) : std::nullopt; + defn.originalNameLocation = global->nameLocation; + } + + TypeId fnType = arena->addType(FunctionType{TypeLevel{}, funScope.get(), std::move(genericTys), std::move(genericTps), paramPack, retPack, defn}); FunctionType* ftv = getMutable(fnType); ftv->isCheckedFunction = FFlag::LuauAttributeSyntax ? global->isCheckedFunction() : false; @@ -2032,17 +2087,17 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary) { case AstExprUnary::Op::Not: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.notFamily, {operandType}, {}, scope, unary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().notFamily, {operandType}, {}, scope, unary->location); return Inference{resultType, refinementArena.negation(refinement)}; } case AstExprUnary::Op::Len: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.lenFamily, {operandType}, {}, scope, unary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().lenFamily, {operandType}, {}, scope, unary->location); return Inference{resultType, refinementArena.negation(refinement)}; } case AstExprUnary::Op::Minus: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.unmFamily, {operandType}, {}, scope, unary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().unmFamily, {operandType}, {}, scope, unary->location); return Inference{resultType, refinementArena.negation(refinement)}; } default: // msvc can't prove that this is exhaustive. @@ -2058,74 +2113,75 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar { case AstExprBinary::Op::Add: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.addFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().addFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Sub: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.subFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().subFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Mul: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.mulFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().mulFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Div: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.divFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().divFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::FloorDiv: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.idivFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().idivFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Pow: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.powFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().powFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Mod: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.modFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().modFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Concat: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.concatFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().concatFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::And: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.andFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().andFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Or: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.orFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().orFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareLt: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.ltFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().ltFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareGe: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.ltFamily, + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().ltFamily, {rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)` {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareLe: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.leFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().leFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::CompareGt: { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.leFamily, + TypeId resultType = createTypeFamilyInstance( +builtinTypeFunctions().leFamily, {rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)` {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; @@ -2147,7 +2203,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar else if (rightSubscripted) rightType = makeUnion(scope, binary->location, rightType, builtinTypes->nilType); - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.eqFamily, {leftType, rightType}, {}, scope, binary->location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().eqFamily, {leftType, rightType}, {}, scope, binary->location); return Inference{resultType, std::move(refinement)}; } case AstExprBinary::Op::Op__Count: @@ -3100,14 +3156,14 @@ TypeId ConstraintGenerator::makeUnion(const ScopePtr& scope, Location location, if (get(follow(rhs))) return lhs; - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.unionFamily, {lhs, rhs}, {}, scope, location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().unionFamily, {lhs, rhs}, {}, scope, location); return resultType; } TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs) { - TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.intersectFamily, {lhs, rhs}, {}, scope, location); + TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().intersectFamily, {lhs, rhs}, {}, scope, location); return resultType; } @@ -3225,7 +3281,7 @@ void ConstraintGenerator::fillInInferredBindings(const ScopePtr& globalScope, As scope->bindings[symbol] = Binding{tys.front(), location}; else { - TypeId ty = createTypeFamilyInstance(kBuiltinTypeFamilies.unionFamily, std::move(tys), {}, globalScope, location); + TypeId ty = createTypeFamilyInstance(builtinTypeFunctions().unionFamily, std::move(tys), {}, globalScope, location); scope->bindings[symbol] = Binding{ty, location}; } diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index cb8ef20d9..5a9e42a7e 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -661,14 +661,14 @@ struct ErrorConverter return "Type family instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid"; } - if ("index" == tfit->family->name) + if ("index" == tfit->family->name || "rawget" == tfit->family->name) { if (tfit->typeArguments.size() != 2) return "Type family instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid"; - if (auto errType = get(tfit->typeArguments[1])) // Second argument to index<_,_> is not a type - return "Second argument to index<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type"; - else // Second argument to index<_,_> is not a property of the first argument + if (auto errType = get(tfit->typeArguments[1])) // Second argument to (index | rawget)<_,_> is not a type + return "Second argument to " + tfit->family->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type"; + else // Property `indexer` does not exist on type `indexee` return "Property '" + Luau::toString(tfit->typeArguments[1]) + "' does not exist on type '" + Luau::toString(tfit->typeArguments[0]) + "'"; } @@ -1321,7 +1321,7 @@ void copyError(T& e, TypeArena& destArena, CloneState& cloneState) else if constexpr (std::is_same_v) { e.recommendedReturn = clone(e.recommendedReturn); - for (auto [_, t] : e.recommendedArgs) + for (auto& [_, t] : e.recommendedArgs) t = clone(t); } else if constexpr (std::is_same_v) diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 618a9a9c0..4339960d5 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -34,6 +34,7 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTFLAG(LuauInferInNoCheckMode) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false) +LUAU_FASTFLAGVARIABLE(LuauCancelFromProgress, false) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile, false) @@ -440,6 +441,8 @@ CheckResult Frontend::check(const ModuleName& name, std::optional result = getCheckResult(name, true, frontendOptions.forAutocomplete)) return std::move(*result); @@ -492,9 +495,11 @@ void Frontend::queueModuleCheck(const ModuleName& name) } std::vector Frontend::checkQueuedModules(std::optional optionOverride, - std::function task)> executeTask, std::function progress) + std::function task)> executeTask, std::function progress) { FrontendOptions frontendOptions = optionOverride.value_or(options); + if (FFlag::DebugLuauDeferredConstraintResolution) + frontendOptions.forAutocomplete = false; // By taking data into locals, we make sure queue is cleared at the end, even if an ICE or a different exception is thrown std::vector currModuleQueue; @@ -673,7 +678,17 @@ std::vector Frontend::checkQueuedModules(std::optional Frontend::checkQueuedModules(std::optional Frontend::getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete) { + if (FFlag::DebugLuauDeferredConstraintResolution) + forAutocomplete = false; + auto it = sourceNodes.find(name); if (it == sourceNodes.end() || it->second->hasDirtyModule(forAutocomplete)) @@ -1006,9 +1024,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item) module->astCompoundAssignResultTypes.clear(); module->astScopes.clear(); module->upperBoundContributors.clear(); - - if (!FFlag::DebugLuauDeferredConstraintResolution) - module->scopes.clear(); + module->scopes.clear(); } if (mode != Mode::NoCheck) diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index fe0bf2dd0..c53a5d304 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -2138,24 +2138,39 @@ struct TypeChecker2 TypeId annotationType = lookupAnnotation(expr->annotation); TypeId computedType = lookupType(expr->expr); - // Note: As an optimization, we try 'number <: number | string' first, as that is the more likely case. - if (subtyping->isSubtype(annotationType, computedType).isSubtype) - return; - - if (subtyping->isSubtype(computedType, annotationType).isSubtype) - return; - switch (shouldSuppressErrors(NotNull{&normalizer}, computedType).orElse(shouldSuppressErrors(NotNull{&normalizer}, annotationType))) { case ErrorSuppression::Suppress: return; case ErrorSuppression::NormalizationFailed: reportError(NormalizationTooComplex{}, expr->location); + return; case ErrorSuppression::DoNotSuppress: break; } - reportError(TypesAreUnrelated{computedType, annotationType}, expr->location); + switch (normalizer.isInhabited(computedType)) + { + case NormalizationResult::True: + break; + case NormalizationResult::False: + return; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + return; + } + + switch (normalizer.isIntersectionInhabited(computedType, annotationType)) + { + case NormalizationResult::True: + return; + case NormalizationResult::False: + reportError(TypesAreUnrelated{computedType, annotationType}, expr->location); + break; + case NormalizationResult::HitLimits: + reportError(NormalizationTooComplex{}, expr->location); + break; + } } void visit(AstExprIfElse* expr) diff --git a/Analysis/src/TypeFamily.cpp b/Analysis/src/TypeFamily.cpp index 54d89a153..816cf0058 100644 --- a/Analysis/src/TypeFamily.cpp +++ b/Analysis/src/TypeFamily.cpp @@ -180,7 +180,10 @@ struct FamilyReducer void replace(T subject, T replacement) { if (subject->owningArena != ctx.arena.get()) - ctx.ice->ice("Attempting to modify a type family instance from another arena", location); + { + result.errors.emplace_back(location, InternalError{"Attempting to modify a type family instance from another arena"}); + return; + } if (FFlag::DebugLuauLogTypeFamilies) printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str()); @@ -514,7 +517,7 @@ static std::optional> tryDistributeTypeFamilyA return {{results[0], false, {}, {}}}; TypeId resultTy = ctx->arena->addType(TypeFamilyInstanceType{ - NotNull{&kBuiltinTypeFamilies.unionFamily}, + NotNull{&builtinTypeFunctions().unionFamily}, std::move(results), {}, }); @@ -1957,15 +1960,9 @@ bool tblIndexInto(TypeId indexer, TypeId indexee, DenseHashSet& result, /* Vocabulary note: indexee refers to the type that contains the properties, indexer refers to the type that is used to access indexee Example: index => `Person` is the indexee and `"name"` is the indexer */ -TypeFamilyReductionResult indexFamilyFn( - TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) +TypeFamilyReductionResult indexFamilyImpl( + const std::vector& typeParams, const std::vector& packParams, NotNull ctx, bool isRaw) { - if (typeParams.size() != 2 || !packParams.empty()) - { - ctx->ice->ice("index type family: encountered a type family instance without the required argument structure"); - LUAU_ASSERT(false); - } - TypeId indexeeTy = follow(typeParams.at(0)); std::shared_ptr indexeeNormTy = ctx->normalizer->normalize(indexeeTy); @@ -2003,12 +2000,14 @@ TypeFamilyReductionResult indexFamilyFn( typesToFind = &singleType; DenseHashSet properties{{}}; // vector of types that will be returned - bool isRaw = false; if (indexeeNormTy->hasClasses()) { LUAU_ASSERT(!indexeeNormTy->hasTables()); + if (isRaw) // rawget should never reduce for classes (to match the behavior of the rawget global function) + return {std::nullopt, true, {}, {}}; + // at least one class is guaranteed to be in the iterator by .hasClasses() for (auto classesIter = indexeeNormTy->classes.ordering.begin(); classesIter != indexeeNormTy->classes.ordering.end(); ++classesIter) { @@ -2021,7 +2020,7 @@ TypeFamilyReductionResult indexFamilyFn( for (TypeId ty : *typesToFind) { - // Search for all instances of indexer in class->props and class->indexer using `indexInto` + // Search for all instances of indexer in class->props and class->indexer if (searchPropsAndIndexer(ty, classTy->props, classTy->indexer, properties, ctx)) continue; // Indexer was found in this class, so we can move on to the next @@ -2065,6 +2064,30 @@ TypeFamilyReductionResult indexFamilyFn( return {ctx->arena->addType(UnionType{std::vector(properties.begin(), properties.end())}), false, {}, {}}; } +TypeFamilyReductionResult indexFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) +{ + if (typeParams.size() != 2 || !packParams.empty()) + { + ctx->ice->ice("index type family: encountered a type family instance without the required argument structure"); + LUAU_ASSERT(false); + } + + return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ false); +} + +TypeFamilyReductionResult rawgetFamilyFn( + TypeId instance, const std::vector& typeParams, const std::vector& packParams, NotNull ctx) +{ + if (typeParams.size() != 2 || !packParams.empty()) + { + ctx->ice->ice("rawget type family: encountered a type family instance without the required argument structure"); + LUAU_ASSERT(false); + } + + return indexFamilyImpl(typeParams, packParams, ctx, /* isRaw */ true); +} + BuiltinTypeFamilies::BuiltinTypeFamilies() : notFamily{"not", notFamilyFn} , lenFamily{"len", lenFamilyFn} @@ -2089,6 +2112,7 @@ BuiltinTypeFamilies::BuiltinTypeFamilies() , keyofFamily{"keyof", keyofFamilyFn} , rawkeyofFamily{"rawkeyof", rawkeyofFamilyFn} , indexFamily{"index", indexFamilyFn} + , rawgetFamily{"rawget", rawgetFamilyFn} { } @@ -2132,6 +2156,14 @@ void BuiltinTypeFamilies::addToScope(NotNull arena, NotNull sc scope->exportedTypeBindings[rawkeyofFamily.name] = mkUnaryTypeFamily(&rawkeyofFamily); scope->exportedTypeBindings[indexFamily.name] = mkBinaryTypeFamily(&indexFamily); + scope->exportedTypeBindings[rawgetFamily.name] = mkBinaryTypeFamily(&rawgetFamily); +} + +const BuiltinTypeFamilies& builtinTypeFunctions() +{ + static std::unique_ptr result = std::make_unique(); + + return *result; } } // namespace Luau diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 9ce1a58ae..d4c25c34d 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -38,6 +38,7 @@ LUAU_FASTFLAGVARIABLE(LuauAlwaysCommitInferencesOfFunctionCalls, false) LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false) LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false) LUAU_FASTFLAGVARIABLE(LuauReusableSubstitutions, false) +LUAU_FASTFLAG(LuauDeclarationExtraPropData) namespace Luau { @@ -1783,12 +1784,55 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass& ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}}); ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes}); ftv->hasSelf = true; + + if (FFlag::LuauDeclarationExtraPropData) + { + FunctionDefinition defn; + + defn.definitionModuleName = currentModule->name; + defn.definitionLocation = prop.location; + // No data is preserved for varargLocation + defn.originalNameLocation = prop.nameLocation; + + ftv->definition = defn; + } } } if (assignTo.count(propName) == 0) { - assignTo[propName] = {propTy}; + if (FFlag::LuauDeclarationExtraPropData) + assignTo[propName] = {propTy, /*deprecated*/ false, /*deprecatedSuggestion*/ "", prop.location}; + else + assignTo[propName] = {propTy}; + } + else if (FFlag::LuauDeclarationExtraPropData) + { + Luau::Property& prop = assignTo[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)) + { + std::vector options = itv->parts; + options.push_back(propTy); + TypeId newItv = addType(IntersectionType{std::move(options)}); + + prop.readTy = newItv; + prop.writeTy = newItv; + } + else if (get(currentTy)) + { + TypeId intersection = addType(IntersectionType{{currentTy, propTy}}); + + prop.readTy = intersection; + prop.writeTy = intersection; + } + else + { + reportError(declaredClass.location, GenericError{format("Cannot overload non-function class member '%s'", propName.c_str())}); + } } else { @@ -1840,7 +1884,18 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti TypePackId argPack = resolveTypePack(funScope, global.params); TypePackId retPack = resolveTypePack(funScope, global.retTypes); - TypeId fnType = addType(FunctionType{funScope->level, std::move(genericTys), std::move(genericTps), argPack, retPack}); + + FunctionDefinition defn; + + if (FFlag::LuauDeclarationExtraPropData) + { + defn.definitionModuleName = currentModule->name; + defn.definitionLocation = global.location; + defn.varargLocation = global.vararg ? std::make_optional(global.varargLocation) : std::nullopt; + defn.originalNameLocation = global.nameLocation; + } + + TypeId fnType = addType(FunctionType{funScope->level, std::move(genericTys), std::move(genericTps), argPack, retPack, defn}); FunctionType* ftv = getMutable(fnType); ftv->argNames.reserve(global.paramNames.size); diff --git a/Analysis/src/Unifier.cpp b/Analysis/src/Unifier.cpp index a0c802dd9..1802345d8 100644 --- a/Analysis/src/Unifier.cpp +++ b/Analysis/src/Unifier.cpp @@ -23,6 +23,7 @@ LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false) LUAU_FASTFLAGVARIABLE(LuauUnifierShouldNotCopyError, false) +LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart, false) namespace Luau { @@ -2179,7 +2180,18 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, // If one of the types stopped being a table altogether, we need to restart from the top if ((superTy != superTyNew || activeSubTy != subTyNew) && errors.empty()) - return tryUnify(subTy, superTy, false, isIntersection); + { + if (FFlag::LuauUnifierRecursionOnRestart) + { + RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + tryUnify(subTy, superTy, false, isIntersection); + return; + } + else + { + return tryUnify(subTy, superTy, false, isIntersection); + } + } // Otherwise, restart only the table unification TableType* newSuperTable = log.getMutable(superTyNew); @@ -2258,7 +2270,18 @@ void Unifier::tryUnifyTables(TypeId subTy, TypeId superTy, bool isIntersection, // If one of the types stopped being a table altogether, we need to restart from the top if ((superTy != superTyNew || activeSubTy != subTyNew) && errors.empty()) - return tryUnify(subTy, superTy, false, isIntersection); + { + if (FFlag::LuauUnifierRecursionOnRestart) + { + RecursionLimiter _ra(&sharedState.counters.recursionCount, sharedState.counters.recursionLimit); + tryUnify(subTy, superTy, false, isIntersection); + return; + } + else + { + return tryUnify(subTy, superTy, false, isIntersection); + } + } // Recursive unification can change the txn log, and invalidate the old // table. If we detect that this has happened, we start over, with the updated diff --git a/Ast/include/Luau/Ast.h b/Ast/include/Luau/Ast.h index ab0d40e2f..e2ac8b7dc 100644 --- a/Ast/include/Luau/Ast.h +++ b/Ast/include/Luau/Ast.h @@ -826,11 +826,12 @@ class AstStatDeclareGlobal : public AstStat public: LUAU_RTTI(AstStatDeclareGlobal) - AstStatDeclareGlobal(const Location& location, const AstName& name, AstType* type); + AstStatDeclareGlobal(const Location& location, const AstName& name, const Location& nameLocation, AstType* type); void visit(AstVisitor* visitor) override; AstName name; + Location nameLocation; AstType* type; }; @@ -839,13 +840,13 @@ class AstStatDeclareFunction : public AstStat public: LUAU_RTTI(AstStatDeclareFunction) - AstStatDeclareFunction(const Location& location, const AstName& name, const AstArray& generics, - const AstArray& genericPacks, const AstTypeList& params, const AstArray& paramNames, - const AstTypeList& retTypes); + 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); - AstStatDeclareFunction(const Location& location, const AstArray& attributes, const AstName& name, + 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, const AstTypeList& retTypes); + const AstArray& paramNames, bool vararg, const Location& varargLocation, const AstTypeList& retTypes); void visit(AstVisitor* visitor) override; @@ -854,18 +855,23 @@ class AstStatDeclareFunction : public AstStat AstArray attributes; AstName name; + Location nameLocation; AstArray generics; AstArray genericPacks; AstTypeList params; AstArray paramNames; + bool vararg = false; + Location varargLocation; AstTypeList retTypes; }; struct AstDeclaredClassProp { AstName name; + Location nameLocation; AstType* ty = nullptr; bool isMethod = false; + Location location; }; enum class AstTableAccess diff --git a/Ast/include/Luau/TimeTrace.h b/Ast/include/Luau/TimeTrace.h index 2f7daf2c6..bd2ca86ba 100644 --- a/Ast/include/Luau/TimeTrace.h +++ b/Ast/include/Luau/TimeTrace.h @@ -134,6 +134,14 @@ struct ThreadContext static constexpr size_t kEventFlushLimit = 8192; }; +using ThreadContextProvider = ThreadContext& (*)(); + +inline ThreadContextProvider& threadContextProvider() +{ + static ThreadContextProvider handler = nullptr; + return handler; +} + ThreadContext& getThreadContext(); struct Scope diff --git a/Ast/src/Ast.cpp b/Ast/src/Ast.cpp index 14b79767c..a3e53af5a 100644 --- a/Ast/src/Ast.cpp +++ b/Ast/src/Ast.cpp @@ -705,9 +705,10 @@ void AstStatTypeAlias::visit(AstVisitor* visitor) } } -AstStatDeclareGlobal::AstStatDeclareGlobal(const Location& location, const AstName& name, AstType* type) +AstStatDeclareGlobal::AstStatDeclareGlobal(const Location& location, const AstName& name, const Location& nameLocation, AstType* type) : AstStat(ClassIndex(), location) , name(name) + , nameLocation(nameLocation) , type(type) { } @@ -718,30 +719,36 @@ void AstStatDeclareGlobal::visit(AstVisitor* visitor) type->visit(visitor); } -AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstName& name, const AstArray& generics, - const AstArray& genericPacks, const AstTypeList& params, const AstArray& paramNames, - const AstTypeList& retTypes) +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(retTypes) { } AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstArray& attributes, const AstName& name, - const AstArray& generics, const AstArray& genericPacks, const AstTypeList& params, - const AstArray& paramNames, const AstTypeList& retTypes) + 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(retTypes) { } diff --git a/Ast/src/Parser.cpp b/Ast/src/Parser.cpp index 3a6625a5c..87af53cb1 100644 --- a/Ast/src/Parser.cpp +++ b/Ast/src/Parser.cpp @@ -21,6 +21,7 @@ LUAU_FASTFLAG(LuauAttributeSyntax) LUAU_FASTFLAGVARIABLE(LuauLeadingBarAndAmpersand2, false) LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false) LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false) +LUAU_FASTFLAGVARIABLE(LuauDeclarationExtraPropData, false) namespace Luau { @@ -909,8 +910,16 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported) AstDeclaredClassProp Parser::parseDeclaredClassMethod() { + Location start; + + if (FFlag::LuauDeclarationExtraPropData) + start = lexer.current().location; + nextLexeme(); - Location start = lexer.current().location; + + if (!FFlag::LuauDeclarationExtraPropData) + start = lexer.current().location; + Name fnName = parseName("function name"); // TODO: generic method declarations CLI-39909 @@ -935,15 +944,15 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod() expectMatchAndConsume(')', matchParen); AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy(nullptr, 0), nullptr}); - Location end = lexer.current().location; + Location end = FFlag::LuauDeclarationExtraPropData ? lexer.previousLocation() : lexer.current().location; TempVector vars(scratchType); TempVector> varNames(scratchOptArgName); if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr) { - return AstDeclaredClassProp{ - fnName.name, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true}; + return AstDeclaredClassProp{fnName.name, FFlag::LuauDeclarationExtraPropData ? fnName.location : Location{}, + reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true}; } // Skip the first index. @@ -963,7 +972,8 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod() AstType* fnType = allocator.alloc( Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes); - return AstDeclaredClassProp{fnName.name, fnType, true}; + return AstDeclaredClassProp{fnName.name, FFlag::LuauDeclarationExtraPropData ? fnName.location : Location{}, fnType, true, + FFlag::LuauDeclarationExtraPropData ? Location(start, end) : Location{}}; } AstStat* Parser::parseDeclaration(const Location& start, const AstArray& attributes) @@ -1014,8 +1024,12 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray(Location(start, end), attributes, globalName.name, generics, genericPacks, - AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes); + if (FFlag::LuauDeclarationExtraPropData) + return allocator.alloc(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, Location{}, generics, genericPacks, + AstTypeList{copy(vars), varargAnnotation}, copy(varNames), false, Location{}, retTypes); } else if (AstName(lexer.current().name) == "class") { @@ -1045,19 +1059,42 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray> chars = parseCharArray(); + if (FFlag::LuauDeclarationExtraPropData) + { + const Location nameBegin = lexer.current().location; + std::optional> chars = parseCharArray(); - expectMatchAndConsume(']', begin); - expectAndConsume(':', "property type annotation"); - AstType* type = parseType(); + const Location nameEnd = lexer.previousLocation(); - // since AstName contains a char*, it can't contain null - bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size); + expectMatchAndConsume(']', begin); + expectAndConsume(':', "property type annotation"); + AstType* type = parseType(); - if (chars && !containsNull) - props.push_back(AstDeclaredClassProp{AstName(chars->data), type, false}); + // since AstName contains a char*, it can't contain null + bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size); + + if (chars && !containsNull) + props.push_back(AstDeclaredClassProp{ + AstName(chars->data), Location(nameBegin, nameEnd), type, false, Location(begin.location, lexer.previousLocation())}); + else + report(begin.location, "String literal contains malformed escape sequence or \\0"); + } else - report(begin.location, "String literal contains malformed escape sequence or \\0"); + { + std::optional> chars = parseCharArray(); + + expectMatchAndConsume(']', begin); + expectAndConsume(':', "property type annotation"); + AstType* type = parseType(); + + // since AstName contains a char*, it can't contain null + bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size); + + if (chars && !containsNull) + props.push_back(AstDeclaredClassProp{AstName(chars->data), Location{}, type, false}); + else + report(begin.location, "String literal contains malformed escape sequence or \\0"); + } } else if (lexer.current().type == '[') { @@ -1075,12 +1112,21 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray(Location(start, type->location), globalName->name, type); + return allocator.alloc( + Location(start, type->location), globalName->name, FFlag::LuauDeclarationExtraPropData ? globalName->location : Location{}, type); } else { diff --git a/Ast/src/TimeTrace.cpp b/Ast/src/TimeTrace.cpp index bc3f3538f..cfcf9ce21 100644 --- a/Ast/src/TimeTrace.cpp +++ b/Ast/src/TimeTrace.cpp @@ -250,6 +250,10 @@ void flushEvents(GlobalContext& context, uint32_t threadId, const std::vector -LUAU_FASTFLAGVARIABLE(LuauCodegenAnalyzeHostVectorOps, false) -LUAU_FASTFLAGVARIABLE(LuauCodegenLoadTypeUpvalCheck, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOps, false) LUAU_FASTFLAGVARIABLE(LuauCodegenFastcall3, false) @@ -72,11 +70,6 @@ void loadBytecodeTypeInfo(IrFunction& function) uint32_t upvalCount = readVarInt(data, offset); uint32_t localCount = readVarInt(data, offset); - if (!FFlag::LuauCodegenLoadTypeUpvalCheck) - { - CODEGEN_ASSERT(upvalCount == unsigned(proto->nups)); - } - if (typeSize != 0) { uint8_t* types = (uint8_t*)data + offset; @@ -94,10 +87,7 @@ void loadBytecodeTypeInfo(IrFunction& function) if (upvalCount != 0) { - if (FFlag::LuauCodegenLoadTypeUpvalCheck) - { - CODEGEN_ASSERT(upvalCount == unsigned(proto->nups)); - } + CODEGEN_ASSERT(upvalCount == unsigned(proto->nups)); typeInfo.upvalueTypes.resize(upvalCount); @@ -775,7 +765,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = LBC_TYPE_NUMBER; } - if (FFlag::LuauCodegenAnalyzeHostVectorOps && regTags[ra] == LBC_TYPE_ANY && hostHooks.vectorAccessBytecodeType) + if (regTags[ra] == LBC_TYPE_ANY && hostHooks.vectorAccessBytecodeType) regTags[ra] = hostHooks.vectorAccessBytecodeType(field, str->len); } else if (isCustomUserdataBytecodeType(bcType.a)) @@ -800,7 +790,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) regTags[ra] = LBC_TYPE_NUMBER; } - if (FFlag::LuauCodegenAnalyzeHostVectorOps && regTags[ra] == LBC_TYPE_ANY && hostHooks.vectorAccessBytecodeType) + if (regTags[ra] == LBC_TYPE_ANY && hostHooks.vectorAccessBytecodeType) regTags[ra] = hostHooks.vectorAccessBytecodeType(field, str->len); } } @@ -1218,14 +1208,14 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) TString* str = gco2ts(function.proto->k[kc].value.gc); const char* field = getstr(str); - if (FFlag::LuauCodegenAnalyzeHostVectorOps && bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) + if (bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) knownNextCallResult = LuauBytecodeType(hostHooks.vectorNamecallBytecodeType(field, str->len)); else if (isCustomUserdataBytecodeType(bcType.a) && hostHooks.userdataNamecallBytecodeType) knownNextCallResult = LuauBytecodeType(hostHooks.userdataNamecallBytecodeType(bcType.a, field, str->len)); } else { - if (FFlag::LuauCodegenAnalyzeHostVectorOps && bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) + if (bcType.a == LBC_TYPE_VECTOR && hostHooks.vectorNamecallBytecodeType) { TString* str = gco2ts(function.proto->k[kc].value.gc); const char* field = getstr(str); @@ -1237,21 +1227,18 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks) } case LOP_CALL: { - if (FFlag::LuauCodegenAnalyzeHostVectorOps) - { - int ra = LUAU_INSN_A(*pc); + int ra = LUAU_INSN_A(*pc); - if (knownNextCallResult != LBC_TYPE_ANY) - { - bcType.result = knownNextCallResult; + if (knownNextCallResult != LBC_TYPE_ANY) + { + bcType.result = knownNextCallResult; - knownNextCallResult = LBC_TYPE_ANY; + knownNextCallResult = LBC_TYPE_ANY; - regTags[ra] = bcType.result; - } - - refineRegType(bcTypeInfo, ra, i, bcType.result); + regTags[ra] = bcType.result; } + + refineRegType(bcTypeInfo, ra, i, bcType.result); break; } case LOP_GETUPVAL: diff --git a/CodeGen/src/CodeGenContext.cpp b/CodeGen/src/CodeGenContext.cpp index 67a2676e0..a31a08bae 100644 --- a/CodeGen/src/CodeGenContext.cpp +++ b/CodeGen/src/CodeGenContext.cpp @@ -12,8 +12,6 @@ #include "lapi.h" -LUAU_FASTFLAGVARIABLE(LuauCodegenCheckNullContext, false) - LUAU_FASTINTVARIABLE(LuauCodeGenBlockSize, 4 * 1024 * 1024) LUAU_FASTINTVARIABLE(LuauCodeGenMaxTotalSize, 256 * 1024 * 1024) LUAU_FASTFLAG(LuauNativeAttribute) @@ -360,7 +358,7 @@ static size_t getMemorySize(lua_State* L, Proto* proto) static void initializeExecutionCallbacks(lua_State* L, BaseCodeGenContext* codeGenContext) noexcept { - CODEGEN_ASSERT(!FFlag::LuauCodegenCheckNullContext || codeGenContext != nullptr); + CODEGEN_ASSERT(codeGenContext != nullptr); lua_ExecutionCallbacks* ecb = &L->global->ecb; diff --git a/CodeGen/src/EmitBuiltinsX64.cpp b/CodeGen/src/EmitBuiltinsX64.cpp index 09f69d69f..15aab4b62 100644 --- a/CodeGen/src/EmitBuiltinsX64.cpp +++ b/CodeGen/src/EmitBuiltinsX64.cpp @@ -12,6 +12,8 @@ #include "lstate.h" +LUAU_FASTFLAG(LuauCodegenMathSign) + namespace Luau { namespace CodeGen @@ -57,6 +59,8 @@ static void emitBuiltinMathModf(IrRegAllocX64& regs, AssemblyBuilderX64& build, static void emitBuiltinMathSign(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int arg) { + CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); + ScopedRegX64 tmp0{regs, SizeX64::xmmword}; ScopedRegX64 tmp1{regs, SizeX64::xmmword}; ScopedRegX64 tmp2{regs, SizeX64::xmmword}; @@ -94,6 +98,7 @@ void emitBuiltin(IrRegAllocX64& regs, AssemblyBuilderX64& build, int bfid, int r CODEGEN_ASSERT(nresults == 1 || nresults == 2); return emitBuiltinMathModf(regs, build, ra, arg, nresults); case LBF_MATH_SIGN: + CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); CODEGEN_ASSERT(nresults == 1); return emitBuiltinMathSign(regs, build, ra, arg); default: diff --git a/CodeGen/src/EmitCommonX64.cpp b/CodeGen/src/EmitCommonX64.cpp index 50f2208bb..79562b883 100644 --- a/CodeGen/src/EmitCommonX64.cpp +++ b/CodeGen/src/EmitCommonX64.cpp @@ -14,8 +14,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauCodegenSplitDoarith, false) - namespace Luau { namespace CodeGen @@ -158,43 +156,35 @@ void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, Ope callWrap.addArgument(SizeX64::qword, b); callWrap.addArgument(SizeX64::qword, c); - if (FFlag::LuauCodegenSplitDoarith) - { - switch (tm) - { - case TM_ADD: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithadd)]); - break; - case TM_SUB: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithsub)]); - break; - case TM_MUL: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmul)]); - break; - case TM_DIV: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithdiv)]); - break; - case TM_IDIV: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithidiv)]); - break; - case TM_MOD: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmod)]); - break; - case TM_POW: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithpow)]); - break; - case TM_UNM: - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithunm)]); - break; - default: - CODEGEN_ASSERT(!"Invalid doarith helper operation tag"); - break; - } - } - else + switch (tm) { - callWrap.addArgument(SizeX64::dword, tm); - callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarith)]); + case TM_ADD: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithadd)]); + break; + case TM_SUB: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithsub)]); + break; + case TM_MUL: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmul)]); + break; + case TM_DIV: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithdiv)]); + break; + case TM_IDIV: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithidiv)]); + break; + case TM_MOD: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmod)]); + break; + case TM_POW: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithpow)]); + break; + case TM_UNM: + callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithunm)]); + break; + default: + CODEGEN_ASSERT(!"Invalid doarith helper operation tag"); + break; } emitUpdateBase(build); diff --git a/CodeGen/src/IrBuilder.cpp b/CodeGen/src/IrBuilder.cpp index 672c27ad5..1f4342f67 100644 --- a/CodeGen/src/IrBuilder.cpp +++ b/CodeGen/src/IrBuilder.cpp @@ -13,7 +13,6 @@ #include -LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps) LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauCodegenInstG) LUAU_FASTFLAG(LuauCodegenFastcall3) @@ -534,15 +533,8 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i) translateInstCapture(*this, pc, i); break; case LOP_NAMECALL: - if (FFlag::LuauCodegenAnalyzeHostVectorOps) - { - if (translateInstNamecall(*this, pc, i)) - cmdSkipTarget = i + 3; - } - else - { - translateInstNamecall(*this, pc, i); - } + if (translateInstNamecall(*this, pc, i)) + cmdSkipTarget = i + 3; break; case LOP_PREPVARARGS: inst(IrCmd::FALLBACK_PREPVARARGS, constUint(i), constInt(LUAU_INSN_A(*pc))); diff --git a/CodeGen/src/IrDump.cpp b/CodeGen/src/IrDump.cpp index 5465d0a00..c4114d899 100644 --- a/CodeGen/src/IrDump.cpp +++ b/CodeGen/src/IrDump.cpp @@ -154,6 +154,8 @@ const char* getCmdName(IrCmd cmd) return "SQRT_NUM"; case IrCmd::ABS_NUM: return "ABS_NUM"; + case IrCmd::SIGN_NUM: + return "SIGN_NUM"; case IrCmd::ADD_VEC: return "ADD_VEC"; case IrCmd::SUB_VEC: diff --git a/CodeGen/src/IrLoweringA64.cpp b/CodeGen/src/IrLoweringA64.cpp index 5b3333742..ef51a4b16 100644 --- a/CodeGen/src/IrLoweringA64.cpp +++ b/CodeGen/src/IrLoweringA64.cpp @@ -11,11 +11,11 @@ #include "lstate.h" #include "lgc.h" -LUAU_FASTFLAG(LuauCodegenSplitDoarith) LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataAlloc, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOpsFixA64, false) LUAU_FASTFLAG(LuauCodegenFastcall3) +LUAU_FASTFLAG(LuauCodegenMathSign) namespace Luau { @@ -240,6 +240,7 @@ static bool emitBuiltin(AssemblyBuilderA64& build, IrFunction& function, IrRegAl } case LBF_MATH_SIGN: { + CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); CODEGEN_ASSERT(nresults == 1); build.ldr(d0, mem(rBase, arg * sizeof(TValue) + offsetof(TValue, value.n))); build.fcmpz(d0); @@ -697,6 +698,24 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) build.fabs(inst.regA64, temp); break; } + case IrCmd::SIGN_NUM: + { + CODEGEN_ASSERT(FFlag::LuauCodegenMathSign); + + inst.regA64 = regs.allocReuse(KindA64::d, index, {inst.a}); + + RegisterA64 temp = tempDouble(inst.a); + RegisterA64 temp0 = regs.allocTemp(KindA64::d); + RegisterA64 temp1 = regs.allocTemp(KindA64::d); + + build.fcmpz(temp); + build.fmov(temp0, 0.0); + build.fmov(temp1, 1.0); + build.fcsel(inst.regA64, temp1, temp0, getConditionFP(IrCondition::Greater)); + build.fmov(temp1, -1.0); + build.fcsel(inst.regA64, temp1, inst.regA64, getConditionFP(IrCondition::Less)); + break; + } case IrCmd::ADD_VEC: { inst.regA64 = regs.allocReuse(KindA64::q, index, {inst.a, inst.b}); @@ -1283,48 +1302,39 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) else build.add(x3, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); - if (FFlag::LuauCodegenSplitDoarith) - { - switch (TMS(intOp(inst.d))) - { - case TM_ADD: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithadd))); - break; - case TM_SUB: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithsub))); - break; - case TM_MUL: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmul))); - break; - case TM_DIV: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithdiv))); - break; - case TM_IDIV: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithidiv))); - break; - case TM_MOD: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmod))); - break; - case TM_POW: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithpow))); - break; - case TM_UNM: - build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithunm))); - break; - default: - CODEGEN_ASSERT(!"Invalid doarith helper operation tag"); - break; - } - - build.blr(x4); - } - else - { - build.mov(w4, TMS(intOp(inst.d))); - build.ldr(x5, mem(rNativeContext, offsetof(NativeContext, luaV_doarith))); - build.blr(x5); + switch (TMS(intOp(inst.d))) + { + case TM_ADD: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithadd))); + break; + case TM_SUB: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithsub))); + break; + case TM_MUL: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmul))); + break; + case TM_DIV: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithdiv))); + break; + case TM_IDIV: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithidiv))); + break; + case TM_MOD: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmod))); + break; + case TM_POW: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithpow))); + break; + case TM_UNM: + build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithunm))); + break; + default: + CODEGEN_ASSERT(!"Invalid doarith helper operation tag"); + break; } + build.blr(x4); + emitUpdateBase(build); break; case IrCmd::DO_LEN: diff --git a/CodeGen/src/IrLoweringX64.cpp b/CodeGen/src/IrLoweringX64.cpp index 5128dce55..f372a7ecf 100644 --- a/CodeGen/src/IrLoweringX64.cpp +++ b/CodeGen/src/IrLoweringX64.cpp @@ -18,6 +18,7 @@ LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataAlloc) LUAU_FASTFLAG(LuauCodegenFastcall3) +LUAU_FASTFLAG(LuauCodegenMathSign) namespace Luau { @@ -590,6 +591,33 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next) build.vandpd(inst.regX64, inst.regX64, build.i64(~(1LL << 63))); break; + case IrCmd::SIGN_NUM: + { + CODEGEN_ASSERT(FFlag::LuauCodegenMathSign); + + inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a}); + + ScopedRegX64 tmp0{regs, SizeX64::xmmword}; + ScopedRegX64 tmp1{regs, SizeX64::xmmword}; + ScopedRegX64 tmp2{regs, SizeX64::xmmword}; + + build.vxorpd(tmp0.reg, tmp0.reg, tmp0.reg); + + // Set tmp1 to -1 if arg < 0, else 0 + build.vcmpltsd(tmp1.reg, regOp(inst.a), tmp0.reg); + build.vmovsd(tmp2.reg, build.f64(-1)); + build.vandpd(tmp1.reg, tmp1.reg, tmp2.reg); + + // Set mask bit to 1 if 0 < arg, else 0 + build.vcmpltsd(inst.regX64, tmp0.reg, regOp(inst.a)); + + // Result = (mask-bit == 1) ? 1.0 : tmp1 + // If arg < 0 then tmp1 is -1 and mask-bit is 0, result is -1 + // If arg == 0 then tmp1 is 0 and mask-bit is 0, result is 0 + // If arg > 0 then tmp1 is 0 and mask-bit is 1, result is 1 + build.vblendvpd(inst.regX64, tmp1.reg, build.f64x2(1, 1), inst.regX64); + break; + } case IrCmd::ADD_VEC: { inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b}); diff --git a/CodeGen/src/IrTranslateBuiltins.cpp b/CodeGen/src/IrTranslateBuiltins.cpp index 668bdfe0c..f6a77f21f 100644 --- a/CodeGen/src/IrTranslateBuiltins.cpp +++ b/CodeGen/src/IrTranslateBuiltins.cpp @@ -9,6 +9,7 @@ #include LUAU_FASTFLAG(LuauCodegenFastcall3) +LUAU_FASTFLAGVARIABLE(LuauCodegenMathSign, false) // TODO: when nresults is less than our actual result count, we can skip computing/writing unused results @@ -42,6 +43,8 @@ static IrOp builtinLoadDouble(IrBuilder& build, IrOp arg) static BuiltinImplResult translateBuiltinNumberToNumber( IrBuilder& build, LuauBuiltinFunction bfid, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos) { + CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); + if (nparams < 1 || nresults > 1) return {BuiltinImplType::None, -1}; @@ -845,7 +848,10 @@ BuiltinImplResult translateBuiltin( case LBF_MATH_LOG10: return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos); case LBF_MATH_SIGN: - return translateBuiltinNumberToNumber(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos); + if (FFlag::LuauCodegenMathSign) + return translateBuiltinMathUnary(build, IrCmd::SIGN_NUM, nparams, ra, arg, nresults, pcpos); + else + return translateBuiltinNumberToNumber(build, LuauBuiltinFunction(bfid), nparams, ra, arg, args, nresults, pcpos); case LBF_MATH_POW: case LBF_MATH_FMOD: case LBF_MATH_ATAN2: diff --git a/CodeGen/src/IrTranslation.cpp b/CodeGen/src/IrTranslation.cpp index e06f14f8d..db867fc93 100644 --- a/CodeGen/src/IrTranslation.cpp +++ b/CodeGen/src/IrTranslation.cpp @@ -13,7 +13,6 @@ #include "lstate.h" #include "ltm.h" -LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps) LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenFastcall3) @@ -1285,8 +1284,7 @@ void translateInstGetTableKS(IrBuilder& build, const Instruction* pc, int pcpos) } else { - if (FFlag::LuauCodegenAnalyzeHostVectorOps && build.hostHooks.vectorAccess && - build.hostHooks.vectorAccess(build, field, str->len, ra, rb, pcpos)) + if (build.hostHooks.vectorAccess && build.hostHooks.vectorAccess(build, field, str->len, ra, rb, pcpos)) return; build.inst(IrCmd::FALLBACK_GETTABLEKS, build.constUint(pcpos), build.vmReg(ra), build.vmReg(rb), build.vmConst(aux)); @@ -1468,7 +1466,7 @@ bool translateInstNamecall(IrBuilder& build, const Instruction* pc, int pcpos) { build.loadAndCheckTag(build.vmReg(rb), LUA_TVECTOR, build.vmExit(pcpos)); - if (FFlag::LuauCodegenAnalyzeHostVectorOps && build.hostHooks.vectorNamecall) + if (build.hostHooks.vectorNamecall) { Instruction call = pc[2]; CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL); diff --git a/CodeGen/src/IrUtils.cpp b/CodeGen/src/IrUtils.cpp index 2244c4d3c..129945df6 100644 --- a/CodeGen/src/IrUtils.cpp +++ b/CodeGen/src/IrUtils.cpp @@ -69,6 +69,7 @@ IrValueKind getCmdValueKind(IrCmd cmd) case IrCmd::ROUND_NUM: case IrCmd::SQRT_NUM: case IrCmd::ABS_NUM: + case IrCmd::SIGN_NUM: return IrValueKind::Double; case IrCmd::ADD_VEC: case IrCmd::SUB_VEC: @@ -658,6 +659,14 @@ void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint3 if (inst.a.kind == IrOpKind::Constant) substitute(function, inst, build.constDouble(fabs(function.doubleOp(inst.a)))); break; + case IrCmd::SIGN_NUM: + if (inst.a.kind == IrOpKind::Constant) + { + double v = function.doubleOp(inst.a); + + substitute(function, inst, build.constDouble(v > 0.0 ? 1.0 : v < 0.0 ? -1.0 : 0.0)); + } + break; case IrCmd::NOT_ANY: if (inst.a.kind == IrOpKind::Constant) { diff --git a/CodeGen/src/NativeState.cpp b/CodeGen/src/NativeState.cpp index 248f0cd31..7aa35f23f 100644 --- a/CodeGen/src/NativeState.cpp +++ b/CodeGen/src/NativeState.cpp @@ -29,7 +29,6 @@ void initFunctions(NativeContext& context) context.luaV_lessthan = luaV_lessthan; context.luaV_lessequal = luaV_lessequal; context.luaV_equalval = luaV_equalval; - context.luaV_doarith = luaV_doarith; context.luaV_doarithadd = luaV_doarithimpl; context.luaV_doarithsub = luaV_doarithimpl; diff --git a/CodeGen/src/NativeState.h b/CodeGen/src/NativeState.h index be73815d1..941db2525 100644 --- a/CodeGen/src/NativeState.h +++ b/CodeGen/src/NativeState.h @@ -33,7 +33,6 @@ struct NativeContext int (*luaV_lessthan)(lua_State* L, const TValue* l, const TValue* r) = nullptr; int (*luaV_lessequal)(lua_State* L, const TValue* l, const TValue* r) = nullptr; int (*luaV_equalval)(lua_State* L, const TValue* t1, const TValue* t2) = nullptr; - void (*luaV_doarith)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op) = nullptr; void (*luaV_doarithadd)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr; void (*luaV_doarithsub)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr; void (*luaV_doarithmul)(lua_State* L, StkId ra, const TValue* rb, const TValue* rc) = nullptr; diff --git a/CodeGen/src/OptimizeConstProp.cpp b/CodeGen/src/OptimizeConstProp.cpp index 0cd2aa518..ac90f8e57 100644 --- a/CodeGen/src/OptimizeConstProp.cpp +++ b/CodeGen/src/OptimizeConstProp.cpp @@ -18,10 +18,10 @@ LUAU_FASTINTVARIABLE(LuauCodeGenMinLinearBlockPath, 3) LUAU_FASTINTVARIABLE(LuauCodeGenReuseSlotLimit, 64) LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64) LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false) -LUAU_FASTFLAGVARIABLE(LuauCodegenFixSplitStoreConstMismatch, false) LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataAlloc) LUAU_FASTFLAG(LuauCodegenFastcall3) +LUAU_FASTFLAG(LuauCodegenMathSign) namespace Luau { @@ -757,48 +757,29 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& } } - if (FFlag::LuauCodegenFixSplitStoreConstMismatch) + // If we have constant tag and value, replace TValue store with tag/value pair store + bool canSplitTvalueStore = false; + + if (tag == LUA_TBOOLEAN && + (value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Int))) + canSplitTvalueStore = true; + else if (tag == LUA_TNUMBER && + (value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Double))) + canSplitTvalueStore = true; + else if (tag != 0xff && isGCO(tag) && value.kind == IrOpKind::Inst) + canSplitTvalueStore = true; + + if (canSplitTvalueStore) { - // If we have constant tag and value, replace TValue store with tag/value pair store - bool canSplitTvalueStore = false; - - if (tag == LUA_TBOOLEAN && - (value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Int))) - canSplitTvalueStore = true; - else if (tag == LUA_TNUMBER && - (value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Double))) - canSplitTvalueStore = true; - else if (tag != 0xff && isGCO(tag) && value.kind == IrOpKind::Inst) - canSplitTvalueStore = true; - - if (canSplitTvalueStore) - { - replace(function, block, index, {IrCmd::STORE_SPLIT_TVALUE, inst.a, build.constTag(tag), value, inst.c}); + replace(function, block, index, {IrCmd::STORE_SPLIT_TVALUE, inst.a, build.constTag(tag), value, inst.c}); - // Value can be propagated to future loads of the same register - if (inst.a.kind == IrOpKind::VmReg && activeLoadValue != kInvalidInstIdx) - state.valueMap[state.versionedVmRegLoad(activeLoadCmd, inst.a)] = activeLoadValue; - } - else if (inst.a.kind == IrOpKind::VmReg) - { - state.forwardVmRegStoreToLoad(inst, IrCmd::LOAD_TVALUE); - } + // Value can be propagated to future loads of the same register + if (inst.a.kind == IrOpKind::VmReg && activeLoadValue != kInvalidInstIdx) + state.valueMap[state.versionedVmRegLoad(activeLoadCmd, inst.a)] = activeLoadValue; } - else + else if (inst.a.kind == IrOpKind::VmReg) { - // If we have constant tag and value, replace TValue store with tag/value pair store - if (tag != 0xff && value.kind != IrOpKind::None && (tag == LUA_TBOOLEAN || tag == LUA_TNUMBER || isGCO(tag))) - { - replace(function, block, index, {IrCmd::STORE_SPLIT_TVALUE, inst.a, build.constTag(tag), value, inst.c}); - - // Value can be propagated to future loads of the same register - if (inst.a.kind == IrOpKind::VmReg && activeLoadValue != kInvalidInstIdx) - state.valueMap[state.versionedVmRegLoad(activeLoadCmd, inst.a)] = activeLoadValue; - } - else if (inst.a.kind == IrOpKind::VmReg) - { - state.forwardVmRegStoreToLoad(inst, IrCmd::LOAD_TVALUE); - } + state.forwardVmRegStoreToLoad(inst, IrCmd::LOAD_TVALUE); } } break; @@ -1160,6 +1141,7 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER); break; case LBF_MATH_SIGN: + CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign); state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); break; default: @@ -1225,6 +1207,7 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction& case IrCmd::ROUND_NUM: case IrCmd::SQRT_NUM: case IrCmd::ABS_NUM: + case IrCmd::SIGN_NUM: case IrCmd::NOT_ANY: state.substituteOrRecord(inst, index); break; diff --git a/Common/include/Luau/Bytecode.h b/Common/include/Luau/Bytecode.h index f971391ba..604b8b86e 100644 --- a/Common/include/Luau/Bytecode.h +++ b/Common/include/Luau/Bytecode.h @@ -443,7 +443,6 @@ enum LuauBytecodeTag LBC_VERSION_MAX = 6, LBC_VERSION_TARGET = 5, // Type encoding version - LBC_TYPE_VERSION_DEPRECATED = 1, LBC_TYPE_VERSION_MIN = 1, LBC_TYPE_VERSION_MAX = 3, LBC_TYPE_VERSION_TARGET = 3, diff --git a/Compiler/include/luacode.h b/Compiler/include/luacode.h index 1d200817b..1440a699c 100644 --- a/Compiler/include/luacode.h +++ b/Compiler/include/luacode.h @@ -44,7 +44,7 @@ struct lua_CompileOptions const char* const* mutableGlobals; // null-terminated array of userdata types that will be included in the type information - const char* const* userdataTypes = nullptr; + const char* const* userdataTypes; }; // compile source to bytecode; when source compilation fails, the resulting bytecode contains the encoded error. use free() to destroy diff --git a/Compiler/src/BytecodeBuilder.cpp b/Compiler/src/BytecodeBuilder.cpp index fac740c2c..f68884c5e 100644 --- a/Compiler/src/BytecodeBuilder.cpp +++ b/Compiler/src/BytecodeBuilder.cpp @@ -7,7 +7,6 @@ #include #include -LUAU_FASTFLAGVARIABLE(LuauCompileTypeInfo, false) LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauCompileFastcall3) @@ -283,11 +282,8 @@ void BytecodeBuilder::endFunction(uint8_t maxstacksize, uint8_t numupvalues, uin debugLocals.clear(); debugUpvals.clear(); - if (FFlag::LuauCompileTypeInfo) - { - typedLocals.clear(); - typedUpvals.clear(); - } + typedLocals.clear(); + typedUpvals.clear(); constantMap.clear(); tableShapeMap.clear(); @@ -559,8 +555,6 @@ void BytecodeBuilder::setFunctionTypeInfo(std::string value) void BytecodeBuilder::pushLocalTypeInfo(LuauBytecodeType type, uint8_t reg, uint32_t startpc, uint32_t endpc) { - LUAU_ASSERT(FFlag::LuauCompileTypeInfo); - TypedLocal local; local.type = type; local.reg = reg; @@ -572,8 +566,6 @@ void BytecodeBuilder::pushLocalTypeInfo(LuauBytecodeType type, uint8_t reg, uint void BytecodeBuilder::pushUpvalTypeInfo(LuauBytecodeType type) { - LUAU_ASSERT(FFlag::LuauCompileTypeInfo); - TypedUpval upval; upval.type = type; @@ -712,7 +704,7 @@ void BytecodeBuilder::finalize() writeStringTable(bytecode); - if (FFlag::LuauCompileTypeInfo && FFlag::LuauCompileUserdataInfo) + if (FFlag::LuauCompileUserdataInfo) { // Write the mapping between used type name indices and their name for (uint32_t i = 0; i < uint32_t(userdataTypes.size()); i++) @@ -747,42 +739,34 @@ void BytecodeBuilder::writeFunction(std::string& ss, uint32_t id, uint8_t flags) writeByte(ss, flags); - if (FFlag::LuauCompileTypeInfo) + if (!func.typeinfo.empty() || !typedUpvals.empty() || !typedLocals.empty()) { - if (!func.typeinfo.empty() || !typedUpvals.empty() || !typedLocals.empty()) - { - // collect type info into a temporary string to know the overall size of type data - tempTypeInfo.clear(); - writeVarInt(tempTypeInfo, uint32_t(func.typeinfo.size())); - writeVarInt(tempTypeInfo, uint32_t(typedUpvals.size())); - writeVarInt(tempTypeInfo, uint32_t(typedLocals.size())); - - tempTypeInfo.append(func.typeinfo); + // collect type info into a temporary string to know the overall size of type data + tempTypeInfo.clear(); + writeVarInt(tempTypeInfo, uint32_t(func.typeinfo.size())); + writeVarInt(tempTypeInfo, uint32_t(typedUpvals.size())); + writeVarInt(tempTypeInfo, uint32_t(typedLocals.size())); - for (const TypedUpval& l : typedUpvals) - writeByte(tempTypeInfo, l.type); + tempTypeInfo.append(func.typeinfo); - for (const TypedLocal& l : typedLocals) - { - writeByte(tempTypeInfo, l.type); - writeByte(tempTypeInfo, l.reg); - writeVarInt(tempTypeInfo, l.startpc); - LUAU_ASSERT(l.endpc >= l.startpc); - writeVarInt(tempTypeInfo, l.endpc - l.startpc); - } + for (const TypedUpval& l : typedUpvals) + writeByte(tempTypeInfo, l.type); - writeVarInt(ss, uint32_t(tempTypeInfo.size())); - ss.append(tempTypeInfo); - } - else + for (const TypedLocal& l : typedLocals) { - writeVarInt(ss, 0); + writeByte(tempTypeInfo, l.type); + writeByte(tempTypeInfo, l.reg); + writeVarInt(tempTypeInfo, l.startpc); + LUAU_ASSERT(l.endpc >= l.startpc); + writeVarInt(tempTypeInfo, l.endpc - l.startpc); } + + writeVarInt(ss, uint32_t(tempTypeInfo.size())); + ss.append(tempTypeInfo); } else { - writeVarInt(ss, uint32_t(func.typeinfo.size())); - ss.append(func.typeinfo); + writeVarInt(ss, 0); } // instructions @@ -1251,10 +1235,10 @@ uint8_t BytecodeBuilder::getVersion() uint8_t BytecodeBuilder::getTypeEncodingVersion() { - if (FFlag::LuauCompileTypeInfo && FFlag::LuauCompileUserdataInfo) + if (FFlag::LuauCompileUserdataInfo) return LBC_TYPE_VERSION_TARGET; - return FFlag::LuauCompileTypeInfo ? 2 : LBC_TYPE_VERSION_DEPRECATED; + return 2; } #ifdef LUAU_ASSERTENABLED @@ -2368,80 +2352,77 @@ std::string BytecodeBuilder::dumpCurrentFunction(std::vector& dumpinstoffs) } } - if (FFlag::LuauCompileTypeInfo) + if (dumpFlags & Dump_Types) { - if (dumpFlags & Dump_Types) - { - const std::string& typeinfo = functions.back().typeinfo; + const std::string& typeinfo = functions.back().typeinfo; - if (FFlag::LuauCompileUserdataInfo) + if (FFlag::LuauCompileUserdataInfo) + { + // Arguments start from third byte in function typeinfo string + for (uint8_t i = 2; i < typeinfo.size(); ++i) { - // Arguments start from third byte in function typeinfo string - for (uint8_t i = 2; i < typeinfo.size(); ++i) - { - uint8_t et = typeinfo[i]; + uint8_t et = typeinfo[i]; - const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et)); - const char* name = userdata ? userdata : getBaseTypeString(et); - const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et)); + const char* name = userdata ? userdata : getBaseTypeString(et); + const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional); - } + formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional); + } - for (size_t i = 0; i < typedUpvals.size(); ++i) - { - const TypedUpval& l = typedUpvals[i]; + for (size_t i = 0; i < typedUpvals.size(); ++i) + { + const TypedUpval& l = typedUpvals[i]; - const char* userdata = tryGetUserdataTypeName(l.type); - const char* name = userdata ? userdata : getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* userdata = tryGetUserdataTypeName(l.type); + const char* name = userdata ? userdata : getBaseTypeString(l.type); + const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "U%d: %s%s\n", int(i), name, optional); - } + formatAppend(result, "U%d: %s%s\n", int(i), name, optional); + } - for (size_t i = 0; i < typedLocals.size(); ++i) - { - const TypedLocal& l = typedLocals[i]; + for (size_t i = 0; i < typedLocals.size(); ++i) + { + const TypedLocal& l = typedLocals[i]; - const char* userdata = tryGetUserdataTypeName(l.type); - const char* name = userdata ? userdata : getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* userdata = tryGetUserdataTypeName(l.type); + const char* name = userdata ? userdata : getBaseTypeString(l.type); + const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, name, optional, l.startpc, l.endpc); - } + formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, name, optional, l.startpc, l.endpc); } - else + } + else + { + // Arguments start from third byte in function typeinfo string + for (uint8_t i = 2; i < typeinfo.size(); ++i) { - // Arguments start from third byte in function typeinfo string - for (uint8_t i = 2; i < typeinfo.size(); ++i) - { - uint8_t et = typeinfo[i]; + uint8_t et = typeinfo[i]; - const char* base = getBaseTypeString(et); - const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* base = getBaseTypeString(et); + const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s [argument]\n", i - 2, base, optional); - } + formatAppend(result, "R%d: %s%s [argument]\n", i - 2, base, optional); + } - for (size_t i = 0; i < typedUpvals.size(); ++i) - { - const TypedUpval& l = typedUpvals[i]; + for (size_t i = 0; i < typedUpvals.size(); ++i) + { + const TypedUpval& l = typedUpvals[i]; - const char* base = getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* base = getBaseTypeString(l.type); + const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "U%d: %s%s\n", int(i), base, optional); - } + formatAppend(result, "U%d: %s%s\n", int(i), base, optional); + } - for (size_t i = 0; i < typedLocals.size(); ++i) - { - const TypedLocal& l = typedLocals[i]; + for (size_t i = 0; i < typedLocals.size(); ++i) + { + const TypedLocal& l = typedLocals[i]; - const char* base = getBaseTypeString(l.type); - const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; + const char* base = getBaseTypeString(l.type); + const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; - formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, base, optional, l.startpc, l.endpc); - } + formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, base, optional, l.startpc, l.endpc); } } } diff --git a/Compiler/src/Compiler.cpp b/Compiler/src/Compiler.cpp index 26d3100c7..98520a7f0 100644 --- a/Compiler/src/Compiler.cpp +++ b/Compiler/src/Compiler.cpp @@ -26,8 +26,6 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25) LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300) LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) -LUAU_FASTFLAG(LuauCompileTypeInfo) -LUAU_FASTFLAGVARIABLE(LuauCompileTempTypeInfo, false) LUAU_FASTFLAGVARIABLE(LuauCompileUserdataInfo, false) LUAU_FASTFLAGVARIABLE(LuauCompileFastcall3, false) @@ -215,13 +213,6 @@ struct Compiler setDebugLine(func); - if (!FFlag::LuauCompileTypeInfo) - { - // note: we move types out of typeMap which is safe because compileFunction is only called once per function - if (std::string* funcType = functionTypes.find(func)) - bytecode.setFunctionTypeInfo(std::move(*funcType)); - } - if (func->vararg) bytecode.emitABC(LOP_PREPVARARGS, uint8_t(self + func->args.size), 0, 0); @@ -233,8 +224,7 @@ struct Compiler for (size_t i = 0; i < func->args.size; ++i) pushLocal(func->args.data[i], uint8_t(args + self + i), kDefaultAllocPc); - if (FFlag::LuauCompileTypeInfo) - argCount = localStack.size(); + argCount = localStack.size(); AstStatBlock* stat = func->body; @@ -266,7 +256,7 @@ struct Compiler bytecode.pushDebugUpval(sref(l->name)); } - if (FFlag::LuauCompileTypeInfo && options.typeInfoLevel >= 1) + if (options.typeInfoLevel >= 1) { for (AstLocal* l : upvals) { @@ -289,12 +279,9 @@ struct Compiler if (bytecode.getInstructionCount() > kMaxInstructionCount) CompileError::raise(func->location, "Exceeded function instruction limit; split the function into parts to compile"); - if (FFlag::LuauCompileTypeInfo) - { - // note: we move types out of typeMap which is safe because compileFunction is only called once per function - if (std::string* funcType = functionTypes.find(func)) - bytecode.setFunctionTypeInfo(std::move(*funcType)); - } + // note: we move types out of typeMap which is safe because compileFunction is only called once per function + if (std::string* funcType = functionTypes.find(func)) + bytecode.setFunctionTypeInfo(std::move(*funcType)); // top-level code only executes once so it can be marked as cold if it has no loops; code with loops might be profitable to compile natively if (func->functionDepth == 0 && !hasLoops) @@ -328,8 +315,7 @@ struct Compiler upvals.clear(); // note: instead of std::move above, we copy & clear to preserve capacity for future pushes stackSize = 0; - if (FFlag::LuauCompileTypeInfo) - argCount = 0; + argCount = 0; hasLoops = false; @@ -659,7 +645,7 @@ struct Compiler // if the last argument can return multiple values, we need to compute all of them into the remaining arguments unsigned int tail = unsigned(func->args.size - expr->args.size) + 1; uint8_t reg = allocReg(arg, tail); - uint32_t allocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; + uint32_t allocpc = bytecode.getDebugPC(); if (AstExprCall* expr = arg->as()) compileExprCall(expr, reg, tail, /* targetTop= */ true); @@ -669,12 +655,7 @@ struct Compiler LUAU_ASSERT(!"Unexpected expression type"); for (size_t j = i; j < func->args.size; ++j) - { - if (FFlag::LuauCompileTypeInfo) - args.push_back({func->args.data[j], uint8_t(reg + (j - i)), {Constant::Type_Unknown}, allocpc}); - else - args.push_back({func->args.data[j], uint8_t(reg + (j - i))}); - } + args.push_back({func->args.data[j], uint8_t(reg + (j - i)), {Constant::Type_Unknown}, allocpc}); // all remaining function arguments have been allocated and assigned to break; @@ -683,17 +664,14 @@ struct Compiler { // if the argument is mutated, we need to allocate a fresh register even if it's a constant uint8_t reg = allocReg(arg, 1); - uint32_t allocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; + uint32_t allocpc = bytecode.getDebugPC(); if (arg) compileExprTemp(arg, reg); else bytecode.emitABC(LOP_LOADNIL, reg, 0, 0); - if (FFlag::LuauCompileTypeInfo) - args.push_back({var, reg, {Constant::Type_Unknown}, allocpc}); - else - args.push_back({var, reg}); + args.push_back({var, reg, {Constant::Type_Unknown}, allocpc}); } else if (arg == nullptr) { @@ -718,14 +696,11 @@ struct Compiler else { uint8_t temp = allocReg(arg, 1); - uint32_t allocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; + uint32_t allocpc = bytecode.getDebugPC(); compileExprTemp(arg, temp); - if (FFlag::LuauCompileTypeInfo) - args.push_back({var, temp, {Constant::Type_Unknown}, allocpc}); - else - args.push_back({var, temp}); + args.push_back({var, temp, {Constant::Type_Unknown}, allocpc}); } } } @@ -739,16 +714,9 @@ struct Compiler for (InlineArg& arg : args) { if (arg.value.type == Constant::Type_Unknown) - { - if (FFlag::LuauCompileTypeInfo) - pushLocal(arg.local, arg.reg, arg.allocpc); - else - pushLocal(arg.local, arg.reg, kDefaultAllocPc); - } + pushLocal(arg.local, arg.reg, arg.allocpc); else - { locstants[arg.local] = arg.value; - } } // the inline frame will be used to compile return statements as well as to reject recursive inlining attempts @@ -970,8 +938,7 @@ struct Compiler bytecode.emitABC(LOP_NAMECALL, regs, selfreg, uint8_t(BytecodeBuilder::getStringHash(iname))); bytecode.emitAux(cid); - if (FFlag::LuauCompileTempTypeInfo) - hintTemporaryExprRegType(fi->expr, selfreg, LBC_TYPE_TABLE, /* instLength */ 2); + hintTemporaryExprRegType(fi->expr, selfreg, LBC_TYPE_TABLE, /* instLength */ 2); } else if (bfid >= 0) { @@ -1627,8 +1594,7 @@ struct Compiler bytecode.emitABC(getBinaryOpArith(expr->op, /* k= */ true), target, rl, uint8_t(rc)); - if (FFlag::LuauCompileTempTypeInfo) - hintTemporaryExprRegType(expr->left, rl, LBC_TYPE_NUMBER, /* instLength */ 1); + hintTemporaryExprRegType(expr->left, rl, LBC_TYPE_NUMBER, /* instLength */ 1); } else { @@ -1643,8 +1609,7 @@ struct Compiler bytecode.emitABC(op, target, uint8_t(lc), uint8_t(rr)); - if (FFlag::LuauCompileTempTypeInfo) - hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1); + hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1); return; } } @@ -1654,11 +1619,8 @@ struct Compiler bytecode.emitABC(getBinaryOpArith(expr->op), target, rl, rr); - if (FFlag::LuauCompileTempTypeInfo) - { - hintTemporaryExprRegType(expr->left, rl, LBC_TYPE_NUMBER, /* instLength */ 1); - hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1); - } + hintTemporaryExprRegType(expr->left, rl, LBC_TYPE_NUMBER, /* instLength */ 1); + hintTemporaryExprRegType(expr->right, rr, LBC_TYPE_NUMBER, /* instLength */ 1); } } break; @@ -2099,8 +2061,7 @@ struct Compiler bytecode.emitABC(LOP_GETTABLEKS, target, reg, uint8_t(BytecodeBuilder::getStringHash(iname))); bytecode.emitAux(cid); - if (FFlag::LuauCompileTempTypeInfo) - hintTemporaryExprRegType(expr->expr, reg, LBC_TYPE_TABLE, /* instLength */ 2); + hintTemporaryExprRegType(expr->expr, reg, LBC_TYPE_TABLE, /* instLength */ 2); } void compileExprIndexExpr(AstExprIndexExpr* expr, uint8_t target) @@ -2984,7 +2945,7 @@ struct Compiler // note: allocReg in this case allocates into parent block register - note that we don't have RegScope here uint8_t vars = allocReg(stat, unsigned(stat->vars.size)); - uint32_t allocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; + uint32_t allocpc = bytecode.getDebugPC(); compileExprListTemp(stat->values, vars, uint8_t(stat->vars.size), /* targetTop= */ true); @@ -3116,7 +3077,7 @@ struct Compiler // this makes sure the code inside the loop can't interfere with the iteration process (other than modifying the table we're iterating // through) uint8_t varreg = regs + 2; - uint32_t varregallocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; + uint32_t varregallocpc = bytecode.getDebugPC(); if (Variable* il = variables.find(stat->var); il && il->written) varreg = allocReg(stat, 1); @@ -3183,7 +3144,7 @@ struct Compiler // note that we reserve at least 2 variables; this allows our fast path to assume that we need 2 variables instead of 1 or 2 uint8_t vars = allocReg(stat, std::max(unsigned(stat->vars.size), 2u)); LUAU_ASSERT(vars == regs + 3); - uint32_t varsallocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; + uint32_t varsallocpc = bytecode.getDebugPC(); LuauOpcode skipOp = LOP_FORGPREP; @@ -3480,13 +3441,10 @@ struct Compiler bytecode.emitABC(getBinaryOpArith(stat->op), target, target, rr); - if (FFlag::LuauCompileTempTypeInfo) - { - if (var.kind != LValue::Kind_Local) - hintTemporaryRegType(stat->var, target, LBC_TYPE_NUMBER, /* instLength */ 1); + if (var.kind != LValue::Kind_Local) + hintTemporaryRegType(stat->var, target, LBC_TYPE_NUMBER, /* instLength */ 1); - hintTemporaryExprRegType(stat->value, rr, LBC_TYPE_NUMBER, /* instLength */ 1); - } + hintTemporaryExprRegType(stat->value, rr, LBC_TYPE_NUMBER, /* instLength */ 1); } } break; @@ -3720,9 +3678,7 @@ struct Compiler l.reg = reg; l.allocated = true; l.debugpc = bytecode.getDebugPC(); - - if (FFlag::LuauCompileTypeInfo) - l.allocpc = allocpc == kDefaultAllocPc ? l.debugpc : allocpc; + l.allocpc = allocpc == kDefaultAllocPc ? l.debugpc : allocpc; } bool areLocalsCaptured(size_t start) @@ -3785,7 +3741,7 @@ struct Compiler bytecode.pushDebugLocal(sref(localStack[i]->name), l->reg, l->debugpc, debugpc); } - if (FFlag::LuauCompileTypeInfo && options.typeInfoLevel >= 1 && i >= argCount) + if (options.typeInfoLevel >= 1 && i >= argCount) { uint32_t debugpc = bytecode.getDebugPC(); LuauBytecodeType ty = LBC_TYPE_ANY; @@ -3873,8 +3829,6 @@ struct Compiler void hintTemporaryRegType(AstExpr* expr, int reg, LuauBytecodeType expectedType, int instLength) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - // If we know the type of a temporary and it's not the type that would be expected by codegen, provide a hint if (LuauBytecodeType* ty = exprTypes.find(expr)) { @@ -3885,8 +3839,6 @@ struct Compiler void hintTemporaryExprRegType(AstExpr* expr, int reg, LuauBytecodeType expectedType, int instLength) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - // If we allocated a temporary register for the operation argument, try hinting its type if (!getExprLocal(expr)) hintTemporaryRegType(expr, reg, expectedType, instLength); @@ -4175,9 +4127,7 @@ struct Compiler static void setCompileOptionsForNativeCompilation(CompileOptions& options) { options.optimizationLevel = 2; // note: this might be removed in the future in favor of --!optimize - - if (FFlag::LuauCompileTypeInfo) - options.typeInfoLevel = 1; + options.typeInfoLevel = 1; } void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, const AstNameTable& names, const CompileOptions& inputOptions) @@ -4266,18 +4216,9 @@ void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, c } // computes type information for all functions based on type annotations - if (FFlag::LuauCompileTypeInfo) - { - if (options.typeInfoLevel >= 1) - buildTypeMap(compiler.functionTypes, compiler.localTypes, compiler.exprTypes, root, options.vectorType, compiler.userdataTypes, - compiler.builtinTypes, compiler.builtins, compiler.globals, bytecode); - } - else - { - if (functionVisitor.hasTypes) - buildTypeMap(compiler.functionTypes, compiler.localTypes, compiler.exprTypes, root, options.vectorType, compiler.userdataTypes, - compiler.builtinTypes, compiler.builtins, compiler.globals, bytecode); - } + if (options.typeInfoLevel >= 1) + buildTypeMap(compiler.functionTypes, compiler.localTypes, compiler.exprTypes, root, options.vectorType, compiler.userdataTypes, + compiler.builtinTypes, compiler.builtins, compiler.globals, bytecode); for (AstExprFunction* expr : functions) { diff --git a/Compiler/src/Types.cpp b/Compiler/src/Types.cpp index 4454114c9..447b51d32 100644 --- a/Compiler/src/Types.cpp +++ b/Compiler/src/Types.cpp @@ -3,8 +3,6 @@ #include "Luau/BytecodeBuilder.h" -LUAU_FASTFLAG(LuauCompileTypeInfo) -LUAU_FASTFLAG(LuauCompileTempTypeInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo) namespace Luau @@ -160,8 +158,6 @@ static std::string getFunctionType(const AstExprFunction* func, const DenseHashM static bool isMatchingGlobal(const DenseHashMap& globals, AstExpr* node, const char* name) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - if (AstExprGlobal* expr = node->as()) return Compile::getGlobalState(globals, expr->name) == Compile::Global::Default && expr->name == name; @@ -233,8 +229,6 @@ struct TypeMapVisitor : AstVisitor const AstType* resolveAliases(const AstType* ty) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - if (const AstTypeReference* ref = ty->as()) { if (ref->prefix) @@ -249,8 +243,6 @@ struct TypeMapVisitor : AstVisitor const AstTableIndexer* tryGetTableIndexer(AstExpr* expr) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - if (const AstType** typePtr = resolvedExprs.find(expr)) { if (const AstTypeTable* tableTy = (*typePtr)->as()) @@ -262,8 +254,6 @@ struct TypeMapVisitor : AstVisitor LuauBytecodeType recordResolvedType(AstExpr* expr, const AstType* ty) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - ty = resolveAliases(ty); resolvedExprs[expr] = ty; @@ -275,8 +265,6 @@ struct TypeMapVisitor : AstVisitor LuauBytecodeType recordResolvedType(AstLocal* local, const AstType* ty) { - LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo); - ty = resolveAliases(ty); resolvedLocals[local] = ty; @@ -319,9 +307,6 @@ struct TypeMapVisitor : AstVisitor // for...in statement can contain type annotations on locals (we might even infer some for ipairs/pairs/generalized iteration) bool visit(AstStatForIn* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - for (AstExpr* expr : node->values) expr->visit(this); @@ -382,51 +367,25 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprLocal* node) override { - if (FFlag::LuauCompileTempTypeInfo) - { - if (FFlag::LuauCompileTypeInfo) - { - AstLocal* local = node->local; - - if (AstType* annotation = local->annotation) - { - LuauBytecodeType ty = recordResolvedType(node, annotation); + AstLocal* local = node->local; - if (ty != LBC_TYPE_ANY) - localTypes[local] = ty; - } - else if (const AstType** typePtr = resolvedLocals.find(local)) - { - localTypes[local] = recordResolvedType(node, *typePtr); - } - } + if (AstType* annotation = local->annotation) + { + LuauBytecodeType ty = recordResolvedType(node, annotation); - return false; + if (ty != LBC_TYPE_ANY) + localTypes[local] = ty; } - else + else if (const AstType** typePtr = resolvedLocals.find(local)) { - if (FFlag::LuauCompileTypeInfo) - { - AstLocal* local = node->local; - - if (AstType* annotation = local->annotation) - { - LuauBytecodeType ty = getType(annotation, {}, typeAliases, /* resolveAliases= */ true, vectorType, userdataTypes, bytecode); - - if (ty != LBC_TYPE_ANY) - localTypes[local] = ty; - } - } - - return true; + localTypes[local] = recordResolvedType(node, *typePtr); } + + return false; } bool visit(AstStatLocal* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - for (AstExpr* expr : node->values) expr->visit(this); @@ -451,9 +410,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprIndexExpr* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->expr->visit(this); node->index->visit(this); @@ -465,9 +421,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprIndexName* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->expr->visit(this); if (const AstType** typePtr = resolvedExprs.find(node->expr)) @@ -499,9 +452,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprUnary* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->expr->visit(this); switch (node->op) @@ -534,9 +484,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprBinary* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->left->visit(this); node->right->visit(this); @@ -575,9 +522,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprGroup* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->expr->visit(this); if (const AstType** typePtr = resolvedExprs.find(node->expr)) @@ -588,9 +532,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprTypeAssertion* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->expr->visit(this); recordResolvedType(node, node->annotation); @@ -600,9 +541,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprConstantBool* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - recordResolvedType(node, &builtinTypes.booleanType); return false; @@ -610,9 +548,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprConstantNumber* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - recordResolvedType(node, &builtinTypes.numberType); return false; @@ -620,9 +555,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprConstantString* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - recordResolvedType(node, &builtinTypes.stringType); return false; @@ -630,9 +562,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprInterpString* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - recordResolvedType(node, &builtinTypes.stringType); return false; @@ -640,9 +569,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprIfElse* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - node->condition->visit(this); node->trueExpr->visit(this); node->falseExpr->visit(this); @@ -660,9 +586,6 @@ struct TypeMapVisitor : AstVisitor bool visit(AstExprCall* node) override { - if (!FFlag::LuauCompileTempTypeInfo) - return true; - if (const int* bfid = builtinCalls.find(node)) { switch (LuauBuiltinFunction(*bfid)) diff --git a/VM/src/lvm.h b/VM/src/lvm.h index 96bc37f3f..0b8690be7 100644 --- a/VM/src/lvm.h +++ b/VM/src/lvm.h @@ -15,7 +15,6 @@ LUAI_FUNC int luaV_strcmp(const TString* ls, const TString* rs); LUAI_FUNC int luaV_lessthan(lua_State* L, const TValue* l, const TValue* r); LUAI_FUNC int luaV_lessequal(lua_State* L, const TValue* l, const TValue* r); LUAI_FUNC int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2); -LUAI_FUNC void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op); template void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc); diff --git a/VM/src/lvmexecute.cpp b/VM/src/lvmexecute.cpp index bc89458e6..fb253c6a4 100644 --- a/VM/src/lvmexecute.cpp +++ b/VM/src/lvmexecute.cpp @@ -16,8 +16,6 @@ #include -LUAU_FASTFLAGVARIABLE(LuauVmSplitDoarith, false) - // Disable c99-designator to avoid the warning in CGOTO dispatch table #ifdef __clang__ #if __has_warning("-Wc99-designator") @@ -1489,14 +1487,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_ADD)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1542,14 +1533,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_SUB)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1610,14 +1594,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MUL)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1678,14 +1655,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_DIV)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1733,14 +1703,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_IDIV)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1764,14 +1727,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MOD)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1792,14 +1748,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_POW)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rc)); VM_NEXT(); } } @@ -1820,14 +1769,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_ADD)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -1848,14 +1790,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_SUB)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -1900,14 +1835,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MUL)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -1953,14 +1881,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_DIV)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -2007,14 +1928,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_IDIV)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -2038,14 +1952,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MOD)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -2072,14 +1979,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_POW)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, kv)); VM_NEXT(); } } @@ -2192,14 +2092,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, rb, rb)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, rb, rb, TM_UNM)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, rb, rb)); VM_NEXT(); } } @@ -2812,14 +2705,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, kv, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_SUB)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, kv, rc)); VM_NEXT(); } } @@ -2847,14 +2733,7 @@ static void luau_execute(lua_State* L) else { // slow-path, may invoke C/Lua via metamethods - if (FFlag::LuauVmSplitDoarith) - { - VM_PROTECT(luaV_doarithimpl(L, ra, kv, rc)); - } - else - { - VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_DIV)); - } + VM_PROTECT(luaV_doarithimpl(L, ra, kv, rc)); VM_NEXT(); } } diff --git a/VM/src/lvmutils.cpp b/VM/src/lvmutils.cpp index 6ee542b08..419907429 100644 --- a/VM/src/lvmutils.cpp +++ b/VM/src/lvmutils.cpp @@ -519,140 +519,6 @@ template void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, template void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc); template void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc); -void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op) -{ - TValue tempb, tempc; - const TValue *b, *c; - if ((b = luaV_tonumber(rb, &tempb)) != NULL && (c = luaV_tonumber(rc, &tempc)) != NULL) - { - double nb = nvalue(b), nc = nvalue(c); - switch (op) - { - case TM_ADD: - setnvalue(ra, luai_numadd(nb, nc)); - break; - case TM_SUB: - setnvalue(ra, luai_numsub(nb, nc)); - break; - case TM_MUL: - setnvalue(ra, luai_nummul(nb, nc)); - break; - case TM_DIV: - setnvalue(ra, luai_numdiv(nb, nc)); - break; - case TM_IDIV: - setnvalue(ra, luai_numidiv(nb, nc)); - break; - case TM_MOD: - setnvalue(ra, luai_nummod(nb, nc)); - break; - case TM_POW: - setnvalue(ra, luai_numpow(nb, nc)); - break; - case TM_UNM: - setnvalue(ra, luai_numunm(nb)); - break; - default: - LUAU_ASSERT(0); - break; - } - } - else - { - // vector operations that we support: - // v+v v-v -v (add/sub/neg) - // v*v s*v v*s (mul) - // v/v s/v v/s (div) - // v//v s//v v//s (floor div) - - const float* vb = luaV_tovector(rb); - const float* vc = luaV_tovector(rc); - - if (vb && vc) - { - switch (op) - { - case TM_ADD: - setvvalue(ra, vb[0] + vc[0], vb[1] + vc[1], vb[2] + vc[2], vb[3] + vc[3]); - return; - case TM_SUB: - setvvalue(ra, vb[0] - vc[0], vb[1] - vc[1], vb[2] - vc[2], vb[3] - vc[3]); - return; - case TM_MUL: - setvvalue(ra, vb[0] * vc[0], vb[1] * vc[1], vb[2] * vc[2], vb[3] * vc[3]); - return; - case TM_DIV: - setvvalue(ra, vb[0] / vc[0], vb[1] / vc[1], vb[2] / vc[2], vb[3] / vc[3]); - return; - case TM_IDIV: - setvvalue(ra, float(luai_numidiv(vb[0], vc[0])), float(luai_numidiv(vb[1], vc[1])), float(luai_numidiv(vb[2], vc[2])), - float(luai_numidiv(vb[3], vc[3]))); - return; - case TM_UNM: - setvvalue(ra, -vb[0], -vb[1], -vb[2], -vb[3]); - return; - default: - break; - } - } - else if (vb) - { - c = luaV_tonumber(rc, &tempc); - - if (c) - { - float nc = cast_to(float, nvalue(c)); - - switch (op) - { - case TM_MUL: - setvvalue(ra, vb[0] * nc, vb[1] * nc, vb[2] * nc, vb[3] * nc); - return; - case TM_DIV: - setvvalue(ra, vb[0] / nc, vb[1] / nc, vb[2] / nc, vb[3] / nc); - return; - case TM_IDIV: - setvvalue(ra, float(luai_numidiv(vb[0], nc)), float(luai_numidiv(vb[1], nc)), float(luai_numidiv(vb[2], nc)), - float(luai_numidiv(vb[3], nc))); - return; - default: - break; - } - } - } - else if (vc) - { - b = luaV_tonumber(rb, &tempb); - - if (b) - { - float nb = cast_to(float, nvalue(b)); - - switch (op) - { - case TM_MUL: - setvvalue(ra, nb * vc[0], nb * vc[1], nb * vc[2], nb * vc[3]); - return; - case TM_DIV: - setvvalue(ra, nb / vc[0], nb / vc[1], nb / vc[2], nb / vc[3]); - return; - case TM_IDIV: - setvvalue(ra, float(luai_numidiv(nb, vc[0])), float(luai_numidiv(nb, vc[1])), float(luai_numidiv(nb, vc[2])), - float(luai_numidiv(nb, vc[3]))); - return; - default: - break; - } - } - } - - if (!call_binTM(L, rb, rc, ra, op)) - { - luaG_aritherror(L, rb, rc, op); - } - } -} - void luaV_dolen(lua_State* L, StkId ra, const TValue* rb) { const TValue* tm = NULL; diff --git a/tests/AstJsonEncoder.test.cpp b/tests/AstJsonEncoder.test.cpp index 82e8f1393..1c8b2127b 100644 --- a/tests/AstJsonEncoder.test.cpp +++ b/tests/AstJsonEncoder.test.cpp @@ -9,6 +9,8 @@ #include #include +LUAU_FASTFLAG(LuauDeclarationExtraPropData) + using namespace Luau; struct JsonEncoderFixture @@ -408,16 +410,32 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatTypeAlias") TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction") { + ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true}; + AstStat* statement = expectParseStatement("declare function foo(x: number): string"); std::string_view expected = - R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","name":"foo","params":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,24 - 0,30","name":"number","nameLocation":"0,24 - 0,30","parameters":[]}]},"retTypes":{"type":"AstTypeList","types":[{"type":"AstTypeReference","location":"0,33 - 0,39","name":"string","nameLocation":"0,33 - 0,39","parameters":[]}]},"generics":[],"genericPacks":[]})"; + R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,39","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); +} + +TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction2") +{ + ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true}; + + AstStat* statement = expectParseStatement("declare function foo(x: number, ...: string): string"); + + std::string_view expected = + R"({"type":"AstStatDeclareFunction","location":"0,0 - 0,52","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); } TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass") { + ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true}; + AstStatBlock* root = expectParse(R"( declare class Foo prop: number @@ -432,11 +450,11 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass") REQUIRE(2 == root->body.size); std::string_view expected1 = - R"({"type":"AstStatDeclareClass","location":"1,22 - 4,11","name":"Foo","props":[{"name":"prop","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"2,18 - 2,24","name":"number","nameLocation":"2,18 - 2,24","parameters":[]}},{"name":"method","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeFunction","location":"3,21 - 4,11","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":[]}]}}}],"indexer":null})"; + 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","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 expected2 = - R"({"type":"AstStatDeclareClass","location":"6,22 - 8,11","name":"Bar","superName":"Foo","props":[{"name":"prop2","type":"AstDeclaredClassProp","luauType":{"type":"AstTypeReference","location":"7,19 - 7,25","name":"string","nameLocation":"7,19 - 7,25","parameters":[]}}],"indexer":null})"; + 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})"; CHECK(toJson(root->body.data[1]) == expected2); } diff --git a/tests/Autocomplete.test.cpp b/tests/Autocomplete.test.cpp index c220f30b5..4e8a04427 100644 --- a/tests/Autocomplete.test.cpp +++ b/tests/Autocomplete.test.cpp @@ -35,6 +35,7 @@ struct ACFixtureImpl : BaseType { FrontendOptions opts; opts.forAutocomplete = true; + opts.retainFullTypeGraphs = true; this->frontend.check("MainModule", opts); return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback); @@ -44,6 +45,7 @@ struct ACFixtureImpl : BaseType { FrontendOptions opts; opts.forAutocomplete = true; + opts.retainFullTypeGraphs = true; this->frontend.check("MainModule", opts); return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback); @@ -53,6 +55,7 @@ struct ACFixtureImpl : BaseType { FrontendOptions opts; opts.forAutocomplete = true; + opts.retainFullTypeGraphs = true; this->frontend.check(name, opts); return Luau::autocomplete(this->frontend, name, pos, callback); @@ -3681,6 +3684,8 @@ a.@1 auto ac = autocomplete('1'); + CHECK(2 == ac.entryMap.size()); + CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("y")); @@ -3733,11 +3738,13 @@ TEST_CASE_FIXTURE(ACFixture, "string_contents_is_available_to_callback") declare function require(path: string): any )"); - std::optional require = frontend.globalsForAutocomplete.globalScope->linearSearchForBinding("require"); + GlobalTypes& globals = FFlag::DebugLuauDeferredConstraintResolution ? frontend.globals : frontend.globalsForAutocomplete; + + std::optional require = globals.globalScope->linearSearchForBinding("require"); REQUIRE(require); - Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); + Luau::unfreeze(globals.globalTypes); attachTag(require->typeId, "RequireCall"); - Luau::freeze(frontend.globalsForAutocomplete.globalTypes); + Luau::freeze(globals.globalTypes); check(R"( local x = require("testing/@1") @@ -3837,11 +3844,13 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes") declare function require(path: string): any )"); - std::optional require = frontend.globalsForAutocomplete.globalScope->linearSearchForBinding("require"); + GlobalTypes& globals = FFlag::DebugLuauDeferredConstraintResolution ? frontend.globals : frontend.globalsForAutocomplete; + + std::optional require = globals.globalScope->linearSearchForBinding("require"); REQUIRE(require); - Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); + Luau::unfreeze(globals.globalTypes); attachTag(require->typeId, "RequireCall"); - Luau::freeze(frontend.globalsForAutocomplete.globalTypes); + Luau::freeze(globals.globalTypes); check(R"( local x = require(@1"@2"@3) diff --git a/tests/Compiler.test.cpp b/tests/Compiler.test.cpp index eeca416cc..250de6e4f 100644 --- a/tests/Compiler.test.cpp +++ b/tests/Compiler.test.cpp @@ -22,8 +22,6 @@ LUAU_FASTINT(LuauCompileLoopUnrollThreshold) LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost) LUAU_FASTINT(LuauRecursionLimit) -LUAU_FASTFLAG(LuauCompileTypeInfo) -LUAU_FASTFLAG(LuauCompileTempTypeInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauCompileFastcall3) @@ -3226,8 +3224,6 @@ RETURN R0 0 TEST_CASE("DebugTypes") { - ScopedFastFlag luauCompileTypeInfo{FFlag::LuauCompileTypeInfo, true}; - ScopedFastFlag luauCompileTempTypeInfo{FFlag::LuauCompileTempTypeInfo, true}; ScopedFastFlag luauCompileUserdataInfo{FFlag::LuauCompileUserdataInfo, true}; const char* source = R"( diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index 516e02f46..65af4e4d0 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -33,7 +33,6 @@ void luaC_validate(lua_State* L); LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTINT(CodegenHeuristicsInstructionLimit) -LUAU_FASTFLAG(LuauCodegenFixSplitStoreConstMismatch) LUAU_FASTFLAG(LuauAttributeSyntax) LUAU_FASTFLAG(LuauNativeAttribute) @@ -2358,8 +2357,6 @@ TEST_CASE("Native") if (!codegen || !luau_codegen_supported()) return; - ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true}; - SUBCASE("Checked") { FFlag::DebugLuauAbortingChecks.value = true; diff --git a/tests/Frontend.test.cpp b/tests/Frontend.test.cpp index 411d49148..967dea43a 100644 --- a/tests/Frontend.test.cpp +++ b/tests/Frontend.test.cpp @@ -1333,4 +1333,58 @@ TEST_CASE_FIXTURE(FrontendFixture, "checked_modules_have_the_correct_mode") CHECK(moduleC->mode == Mode::Strict); } +TEST_CASE_FIXTURE(FrontendFixture, "separate_caches_for_autocomplete") +{ + ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, false}; + + fileResolver.source["game/A"] = R"( + --!nonstrict + local exports = {} + function exports.hello() end + return exports + )"; + + FrontendOptions opts; + opts.forAutocomplete = true; + + frontend.check("game/A", opts); + + CHECK(nullptr == frontend.moduleResolver.getModule("game/A")); + + ModulePtr acModule = frontend.moduleResolverForAutocomplete.getModule("game/A"); + REQUIRE(acModule != nullptr); + CHECK(acModule->mode == Mode::Strict); + + frontend.check("game/A"); + + ModulePtr module = frontend.moduleResolver.getModule("game/A"); + + REQUIRE(module != nullptr); + CHECK(module->mode == Mode::Nonstrict); +} + +TEST_CASE_FIXTURE(FrontendFixture, "no_separate_caches_with_the_new_solver") +{ + ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + + fileResolver.source["game/A"] = R"( + --!nonstrict + local exports = {} + function exports.hello() end + return exports + )"; + + FrontendOptions opts; + opts.forAutocomplete = true; + + frontend.check("game/A", opts); + + CHECK(nullptr == frontend.moduleResolverForAutocomplete.getModule("game/A")); + + ModulePtr module = frontend.moduleResolver.getModule("game/A"); + + REQUIRE(module != nullptr); + CHECK(module->mode == Mode::Nonstrict); +} + TEST_SUITE_END(); diff --git a/tests/Generalization.test.cpp b/tests/Generalization.test.cpp index e93449115..901461ae0 100644 --- a/tests/Generalization.test.cpp +++ b/tests/Generalization.test.cpp @@ -7,6 +7,7 @@ #include "Luau/TypeArena.h" #include "Luau/Error.h" +#include "Fixture.h" #include "ScopedFlags.h" #include "doctest.h" @@ -172,4 +173,78 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "functions_containing_cyclic_tables_can CHECK(generalizedTypes->contains(builtinTypes.numberType)); } +TEST_CASE_FIXTURE(GeneralizationFixture, "union_type_traversal_doesnt_crash") +{ + // t1 where t1 = ('h <: (t1 <: 'i)) | ('j <: (t1 <: 'i)) + TypeId i = arena.addType(FreeType{NotNull{globalScope.get()}}); + TypeId h = arena.addType(FreeType{NotNull{globalScope.get()}}); + TypeId j = arena.addType(FreeType{NotNull{globalScope.get()}}); + TypeId unionType = arena.addType(UnionType{{h, j}}); + getMutable(h)->upperBound = i; + getMutable(h)->lowerBound = builtinTypes.neverType; + getMutable(i)->upperBound = builtinTypes.unknownType; + getMutable(i)->lowerBound = unionType; + getMutable(j)->upperBound = i; + getMutable(j)->lowerBound = builtinTypes.neverType; + + generalize(unionType); +} + +TEST_CASE_FIXTURE(GeneralizationFixture, "intersection_type_traversal_doesnt_crash") +{ + // t1 where t1 = ('h <: (t1 <: 'i)) & ('j <: (t1 <: 'i)) + TypeId i = arena.addType(FreeType{NotNull{globalScope.get()}}); + TypeId h = arena.addType(FreeType{NotNull{globalScope.get()}}); + TypeId j = arena.addType(FreeType{NotNull{globalScope.get()}}); + TypeId intersectionType = arena.addType(IntersectionType{{h, j}}); + + getMutable(h)->upperBound = i; + getMutable(h)->lowerBound = builtinTypes.neverType; + getMutable(i)->upperBound = builtinTypes.unknownType; + getMutable(i)->lowerBound = intersectionType; + getMutable(j)->upperBound = i; + getMutable(j)->lowerBound = builtinTypes.neverType; + + generalize(intersectionType); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "generalization_traversal_should_re_traverse_unions_if_they_change_type") +{ + // This test case should just not assert + CheckResult result = check(R"( +function byId(p) + return p.id +end + +function foo() + + local productButtonPairs = {} + local func = byId + local dir = -1 + + local function updateSearch() + for product, button in pairs(productButtonPairs) do + button.LayoutOrder = func(product) * dir + end + end + + function(mode) + if mode == 'Name'then + else + if mode == 'New'then + func = function(p) + return p.id + end + elseif mode == 'Price'then + func = function(p) + return p.price + end + end + + end + end +end +)"); +} + TEST_SUITE_END(); diff --git a/tests/IrBuilder.test.cpp b/tests/IrBuilder.test.cpp index bd7a02b2d..611eb7b5f 100644 --- a/tests/IrBuilder.test.cpp +++ b/tests/IrBuilder.test.cpp @@ -13,9 +13,9 @@ #include LUAU_FASTFLAG(DebugLuauAbortingChecks) -LUAU_FASTFLAG(LuauCodegenFixSplitStoreConstMismatch) LUAU_FASTFLAG(LuauCodegenInstG) LUAU_FASTFLAG(LuauCodegenFastcall3) +LUAU_FASTFLAG(LuauCodegenMathSign) using namespace Luau::CodeGen; @@ -335,6 +335,8 @@ TEST_SUITE_BEGIN("ConstantFolding"); TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric") { + ScopedFastFlag luauCodegenMathSign{FFlag::LuauCodegenMathSign, true}; + IrOp block = build.block(IrBlockKind::Internal); build.beginBlock(block); @@ -365,6 +367,8 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric") build.inst(IrCmd::STORE_INT, build.vmReg(20), build.inst(IrCmd::NOT_ANY, build.constTag(tboolean), build.constInt(0))); build.inst(IrCmd::STORE_INT, build.vmReg(21), build.inst(IrCmd::NOT_ANY, build.constTag(tboolean), build.constInt(1))); + build.inst(IrCmd::STORE_DOUBLE, build.vmReg(22), build.inst(IrCmd::SIGN_NUM, build.constDouble(-4))); + build.inst(IrCmd::RETURN, build.constUint(0)); updateUseCounts(build.function); @@ -393,6 +397,7 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric") STORE_INT R19, 0i STORE_INT R20, 1i STORE_INT R21, 0i + STORE_DOUBLE R22, -1 RETURN 0u )"); @@ -2662,8 +2667,6 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "InferNumberTagFromLimitedContext") TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1") { - ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); @@ -2690,8 +2693,6 @@ TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1") TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore2") { - ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true}; - IrOp entry = build.block(IrBlockKind::Internal); build.beginBlock(entry); diff --git a/tests/IrLowering.test.cpp b/tests/IrLowering.test.cpp index 33d5602b2..0ff8a12c8 100644 --- a/tests/IrLowering.test.cpp +++ b/tests/IrLowering.test.cpp @@ -15,9 +15,6 @@ #include #include -LUAU_FASTFLAG(LuauCompileTypeInfo) -LUAU_FASTFLAG(LuauCompileTempTypeInfo) -LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps) LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauCodegenUserdataOps) @@ -427,27 +424,6 @@ end )"); } -TEST_CASE("DseInitialStackState3") -{ - ScopedFastFlag luauCodegenFastcall3{FFlag::LuauCodegenFastcall3, true}; - - CHECK_EQ("\n" + getCodegenAssembly(R"( -local function foo(a) - math.sign(a) - return a -end -)"), - R"( -; function foo($arg0) line 2 -bb_bytecode_0: - CHECK_SAFE_ENV exit(1) - CHECK_TAG R0, tnumber, exit(1) - FASTCALL 47u, R1, R0, 1i - INTERRUPT 5u - RETURN R0, 1i -)"); -} - TEST_CASE("VectorConstantTag") { CHECK_EQ("\n" + getCodegenAssembly(R"( @@ -539,8 +515,6 @@ end TEST_CASE("VectorCustomAccess") { - ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function vec3magn(a: vector) return a.Magnitude * 2 @@ -573,8 +547,6 @@ end TEST_CASE("VectorCustomNamecall") { - ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function vec3dot(a: vector, b: vector) return (a:Dot(b)) @@ -611,8 +583,6 @@ end TEST_CASE("VectorCustomAccessChain") { - ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vector, b: vector) return a.Unit * b.Magnitude @@ -663,8 +633,6 @@ end TEST_CASE("VectorCustomNamecallChain") { - ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(n: vector, b: vector, t: vector) return n:Cross(t):Dot(b) + 1 @@ -722,8 +690,6 @@ end TEST_CASE("VectorCustomNamecallChain2") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {n: vector, b: vector} @@ -890,8 +856,6 @@ end TEST_CASE("ExplicitUpvalueAndLocalTypes") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local y: vector = ... @@ -933,7 +897,7 @@ end TEST_CASE("FastcallTypeInferThroughLocal") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function getsum(x, c) @@ -981,7 +945,7 @@ end TEST_CASE("FastcallTypeInferThroughUpvalue") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local v = ... @@ -1038,8 +1002,6 @@ end TEST_CASE("LoadAndMoveTypePropagation") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function getsum(n) local seqsum = 0 @@ -1105,7 +1067,7 @@ end TEST_CASE("ArgumentTypeRefinement") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function getsum(x, y) @@ -1141,8 +1103,6 @@ end TEST_CASE("InlineFunctionType") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function inl(v: vector, s: number) return v.Y * s @@ -1189,8 +1149,6 @@ end TEST_CASE("ResolveTablePathTypes") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {pos: vector, normal: vector} @@ -1243,8 +1201,6 @@ end TEST_CASE("ResolvableSimpleMath") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number } local mesh: { vertices: {Vertex}, indices: {number} } = ... @@ -1299,8 +1255,6 @@ end TEST_CASE("ResolveVectorNamecalls") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {pos: vector, normal: vector} @@ -1363,8 +1317,6 @@ end TEST_CASE("ImmediateTypeAnnotationHelp") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(arr, i) return (arr[i] :: vector) / 5 @@ -1401,8 +1353,7 @@ end TEST_CASE("UnaryTypeResolve") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, - {FFlag::LuauCodegenFastcall3, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( local function foo(a, b: vector, c) @@ -1424,8 +1375,6 @@ end TEST_CASE("ForInManualAnnotation") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenAssembly(R"( type Vertex = {pos: vector, normal: vector} @@ -1519,8 +1468,6 @@ end TEST_CASE("ForInAutoAnnotationIpairs") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = {pos: vector, normal: vector} @@ -1546,8 +1493,6 @@ end TEST_CASE("ForInAutoAnnotationPairs") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = {pos: vector, normal: vector} @@ -1573,8 +1518,6 @@ end TEST_CASE("ForInAutoAnnotationGeneric") { - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}}; - CHECK_EQ("\n" + getCodegenHeader(R"( type Vertex = {pos: vector, normal: vector} @@ -1605,8 +1548,7 @@ TEST_CASE("CustomUserdataTypesTemp") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, false}, - {FFlag::LuauLoadUserdataInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, false}, {FFlag::LuauLoadUserdataInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( local function foo(v: vec2, x: mat3) @@ -1626,8 +1568,7 @@ TEST_CASE("CustomUserdataTypes") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}}; CHECK_EQ("\n" + getCodegenHeader(R"( local function foo(v: vec2, x: mat3) @@ -1647,8 +1588,7 @@ TEST_CASE("CustomUserdataPropertyAccess") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(v: vec2) @@ -1683,8 +1623,7 @@ TEST_CASE("CustomUserdataPropertyAccess2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: mat3) @@ -1721,8 +1660,7 @@ TEST_CASE("CustomUserdataNamecall1") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vec2, b: vec2) @@ -1768,8 +1706,7 @@ TEST_CASE("CustomUserdataNamecall2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, {FFlag::LuauCodegenUserdataOps, true}, + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}, {FFlag::LuauCodegenUserdataAlloc, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( @@ -1819,8 +1756,7 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: mat3, b: mat3) @@ -1852,8 +1788,7 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow2") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: mat3) @@ -1883,8 +1818,7 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow3") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: sequence) @@ -1914,8 +1848,8 @@ TEST_CASE("CustomUserdataMetamethod") if (!Luau::CodeGen::isSupported()) return; - ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, - {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}, {FFlag::LuauCodegenUserdataAlloc, true}}; + ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}, + {FFlag::LuauCodegenUserdataAlloc, true}}; CHECK_EQ("\n" + getCodegenAssembly(R"( local function foo(a: vec2, b: vec2, c: vec2) diff --git a/tests/Parser.test.cpp b/tests/Parser.test.cpp index fabb897f4..972d0edd7 100644 --- a/tests/Parser.test.cpp +++ b/tests/Parser.test.cpp @@ -19,6 +19,7 @@ LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(LuauAttributeSyntax); LUAU_FASTFLAG(LuauLeadingBarAndAmpersand2); LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr); +LUAU_FASTFLAG(LuauDeclarationExtraPropData); namespace { @@ -1858,6 +1859,8 @@ function func():end TEST_CASE_FIXTURE(Fixture, "parse_declarations") { + ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true}; + AstStatBlock* stat = parseEx(R"( declare foo: number declare function bar(x: number): string @@ -1871,18 +1874,23 @@ TEST_CASE_FIXTURE(Fixture, "parse_declarations") AstStatDeclareGlobal* global = stat->body.data[0]->as(); REQUIRE(global); CHECK(global->name == "foo"); + CHECK(global->nameLocation == Location({1, 16}, {1, 19})); CHECK(global->type); AstStatDeclareFunction* func = stat->body.data[1]->as(); REQUIRE(func); CHECK(func->name == "bar"); + CHECK(func->nameLocation == Location({2, 25}, {2, 28})); REQUIRE_EQ(func->params.types.size, 1); REQUIRE_EQ(func->retTypes.types.size, 1); AstStatDeclareFunction* varFunc = stat->body.data[2]->as(); REQUIRE(varFunc); CHECK(varFunc->name == "var"); + CHECK(varFunc->nameLocation == Location({3, 25}, {3, 28})); CHECK(varFunc->params.tailType); + CHECK(varFunc->vararg); + CHECK(varFunc->varargLocation == Location({3, 29}, {3, 32})); matchParseError("declare function foo(x)", "All declaration parameters must be annotated"); matchParseError("declare foo", "Expected ':' when parsing global variable declaration, got "); @@ -1890,6 +1898,8 @@ TEST_CASE_FIXTURE(Fixture, "parse_declarations") TEST_CASE_FIXTURE(Fixture, "parse_class_declarations") { + ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true}; + AstStatBlock* stat = parseEx(R"( declare class Foo prop: number @@ -1913,11 +1923,16 @@ TEST_CASE_FIXTURE(Fixture, "parse_class_declarations") AstDeclaredClassProp& prop = declaredClass->props.data[0]; CHECK(prop.name == "prop"); + CHECK(prop.nameLocation == Location({2, 12}, {2, 16})); CHECK(prop.ty->is()); + CHECK(prop.location == Location({2, 12}, {2, 24})); AstDeclaredClassProp& method = declaredClass->props.data[1]; CHECK(method.name == "method"); + CHECK(method.nameLocation == Location({3, 21}, {3, 27})); CHECK(method.ty->is()); + CHECK(method.location == Location({3, 12}, {3, 54})); + CHECK(method.isMethod); AstStatDeclareClass* subclass = stat->body.data[1]->as(); REQUIRE(subclass); @@ -1928,7 +1943,9 @@ TEST_CASE_FIXTURE(Fixture, "parse_class_declarations") REQUIRE_EQ(subclass->props.size, 1); AstDeclaredClassProp& prop2 = subclass->props.data[0]; CHECK(prop2.name == "prop2"); + CHECK(prop2.nameLocation == Location({7, 12}, {7, 17})); CHECK(prop2.ty->is()); + CHECK(prop2.location == Location({7, 12}, {7, 25})); } TEST_CASE_FIXTURE(Fixture, "class_method_properties") diff --git a/tests/TypeFamily.test.cpp b/tests/TypeFamily.test.cpp index 063ed39c0..068e86842 100644 --- a/tests/TypeFamily.test.cpp +++ b/tests/TypeFamily.test.cpp @@ -1045,4 +1045,122 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "index_type_family_works_w_index_metatables") CHECK(toString(result.errors[0]) == "Property '\"Car\"' does not exist on type 'exampleClass2'"); } +TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + type MyObject = {a: string, b: number, c: boolean} + type RawAType = rawget + type RawBType = rawget> + local function ok(idx: RawAType): string return idx end + local function ok2(idx: RawBType): string | number | boolean return idx end + local function err(idx: RawAType): boolean return idx end + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + + TypePackMismatch* tpm = get(result.errors[0]); + REQUIRE(tpm); + CHECK_EQ("boolean", toString(tpm->wantedTp)); + CHECK_EQ("string", toString(tpm->givenTp)); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_array") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + local MyObject = {"hello", 1, true} + type RawAType = rawget + local function ok(idx: RawAType): string | number | boolean return idx end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_errors_w_var_indexer") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + type MyObject = {a: string, b: number, c: boolean} + local key = "a" + type errType1 = rawget + )"); + + LUAU_REQUIRE_ERROR_COUNT(2, result); + CHECK(toString(result.errors[0]) == "Second argument to rawget is not a valid index type"); + CHECK(toString(result.errors[1]) == "Unknown type 'key'"); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_union_type_indexer") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + type MyObject = {a: string, b: number, c: boolean} + type rawType = rawget + local function ok(idx: rawType): string | number return idx end + type errType = rawget + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(toString(result.errors[0]) == "Property '\"a\" | \"d\"' does not exist on type 'MyObject'"); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_union_type_indexee") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + type MyObject = {a: string, b: number, c: boolean} + type MyObject2 = {a: number} + type rawTypeA = rawget + local function ok(idx: rawTypeA): string | number return idx end + type errType = rawget + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(toString(result.errors[0]) == "Property '\"b\"' does not exist on type 'MyObject | MyObject2'"); +} + +TEST_CASE_FIXTURE(BuiltinsFixture, "rawget_type_family_works_w_index_metatables") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + local exampleClass = { Foo = "text", Bar = true } + local exampleClass2 = setmetatable({ Foo = 8 }, { __index = exampleClass }) + type exampleTy2 = rawget + local function ok(idx: exampleTy2): number return idx end + local exampleClass3 = setmetatable({ Bar = 5 }, { __index = exampleClass }) + type errType = rawget + type errType2 = rawget + )"); + + LUAU_REQUIRE_ERROR_COUNT(2, result); + CHECK(toString(result.errors[0]) == "Property '\"Foo\"' does not exist on type 'exampleClass3'"); + CHECK(toString(result.errors[1]) == "Property '\"Bar\" | \"Foo\"' does not exist on type 'exampleClass3'"); +} + +TEST_CASE_FIXTURE(ClassFixture, "rawget_type_family_errors_w_classes") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + CheckResult result = check(R"( + type PropsOfMyObject = rawget + )"); + + LUAU_REQUIRE_ERROR_COUNT(1, result); + CHECK(toString(result.errors[0]) == "Property '\"BaseField\"' does not exist on type 'BaseClass'"); +} + TEST_SUITE_END(); \ No newline at end of file diff --git a/tests/TypeInfer.anyerror.test.cpp b/tests/TypeInfer.anyerror.test.cpp index b305d97d9..c532c0695 100644 --- a/tests/TypeInfer.anyerror.test.cpp +++ b/tests/TypeInfer.anyerror.test.cpp @@ -401,4 +401,13 @@ end CHECK("(any, any) -> any" == toString(requireType("foo"))); } +TEST_CASE_FIXTURE(Fixture, "cast_to_table_of_any") +{ + CheckResult result = check(R"( + local v = {true} :: {any} + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.classes.test.cpp b/tests/TypeInfer.classes.test.cpp index 8f8aef84f..f90324d77 100644 --- a/tests/TypeInfer.classes.test.cpp +++ b/tests/TypeInfer.classes.test.cpp @@ -7,6 +7,7 @@ #include "Fixture.h" #include "ClassFixture.h" +#include "ScopedFlags.h" #include "doctest.h" using namespace Luau; @@ -507,6 +508,31 @@ Type 'ChildClass' could not be converted into 'BaseClass' in an invariant contex } } +TEST_CASE_FIXTURE(ClassFixture, "optional_class_casts_work_in_new_solver") +{ + ScopedFastFlag sff{FFlag::DebugLuauDeferredConstraintResolution, true}; + + CheckResult result = check(R"( + type A = { x: ChildClass } + type B = { x: BaseClass } + + local a = { x = ChildClass.New() } :: A + local opt_a = a :: A? + local b = { x = BaseClass.New() } :: B + local opt_b = b :: B? + local b_from_a = a :: B + local b_from_opt_a = opt_a :: B + local opt_b_from_a = a :: B? + local opt_b_from_opt_a = opt_a :: B? + local a_from_b = b :: A + local a_from_opt_b = opt_b :: A + local opt_a_from_b = b :: A? + local opt_a_from_opt_b = opt_b :: A? + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_CASE_FIXTURE(ClassFixture, "callable_classes") { CheckResult result = check(R"( diff --git a/tests/TypeInfer.definitions.test.cpp b/tests/TypeInfer.definitions.test.cpp index c57eab798..688f27b7b 100644 --- a/tests/TypeInfer.definitions.test.cpp +++ b/tests/TypeInfer.definitions.test.cpp @@ -7,6 +7,8 @@ #include "doctest.h" +LUAU_FASTFLAG(LuauDeclarationExtraPropData) + using namespace Luau; TEST_SUITE_BEGIN("DefinitionTests"); @@ -319,6 +321,8 @@ TEST_CASE_FIXTURE(Fixture, "definitions_documentation_symbols") TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_referenced_types") { + ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true}; + loadDefinition(R"( declare class MyClass function myMethod(self) @@ -330,6 +334,22 @@ TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_re std::optional myClassTy = frontend.globals.globalScope->lookupType("MyClass"); REQUIRE(bool(myClassTy)); CHECK_EQ(myClassTy->type->documentationSymbol, "@test/globaltype/MyClass"); + + ClassType* cls = getMutable(myClassTy->type); + REQUIRE(bool(cls)); + REQUIRE_EQ(cls->props.count("myMethod"), 1); + + const auto& method = cls->props["myMethod"]; + CHECK_EQ(method.documentationSymbol, "@test/globaltype/MyClass.myMethod"); + + FunctionType* function = getMutable(method.type()); + REQUIRE(function); + + REQUIRE(function->definition.has_value()); + CHECK(function->definition->definitionModuleName == "@test"); + CHECK(function->definition->definitionLocation == Location({2, 12}, {2, 35})); + CHECK(!function->definition->varargLocation.has_value()); + CHECK(function->definition->originalNameLocation == Location({2, 21}, {2, 29})); } TEST_CASE_FIXTURE(Fixture, "documentation_symbols_dont_attach_to_persistent_types") diff --git a/tests/TypeInfer.functions.test.cpp b/tests/TypeInfer.functions.test.cpp index 4f8ed3ebd..410a9859f 100644 --- a/tests/TypeInfer.functions.test.cpp +++ b/tests/TypeInfer.functions.test.cpp @@ -2379,6 +2379,28 @@ end CHECK("number" == toString(err->recommendedArgs[1].second)); } +TEST_CASE_FIXTURE(BuiltinsFixture, "tf_suggest_arg_type_2") +{ + if (!FFlag::DebugLuauDeferredConstraintResolution) + return; + + // Make sure the error types are cloned to module interface + frontend.options.retainFullTypeGraphs = false; + + CheckResult result = check(R"( +local function escape_fslash(pre) + return (#pre % 2 == 0 and '\\' or '') .. pre .. '.' +end +)"); + + LUAU_REQUIRE_ERRORS(result); + auto err = get(result.errors.back()); + LUAU_ASSERT(err); + CHECK("unknown" == toString(err->recommendedReturn)); + REQUIRE(err->recommendedArgs.size() == 1); + CHECK("a" == toString(err->recommendedArgs[0].second)); +} + TEST_CASE_FIXTURE(Fixture, "local_function_fwd_decl_doesnt_crash") { CheckResult result = check(R"( diff --git a/tests/TypeInfer.provisional.test.cpp b/tests/TypeInfer.provisional.test.cpp index a34af12d1..3072169c0 100644 --- a/tests/TypeInfer.provisional.test.cpp +++ b/tests/TypeInfer.provisional.test.cpp @@ -312,7 +312,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "bail_early_if_unification_is_too_complicated } } -// FIXME: Move this test to another source file when removing FFlag::LuauLowerBoundsCalculation TEST_CASE_FIXTURE(Fixture, "do_not_ice_when_trying_to_pick_first_of_generic_type_pack") { // In-place quantification causes these types to have the wrong types but only because of nasty interaction with prototyping. diff --git a/tests/TypeInfer.tables.test.cpp b/tests/TypeInfer.tables.test.cpp index 4dbedd51b..6f8b4f504 100644 --- a/tests/TypeInfer.tables.test.cpp +++ b/tests/TypeInfer.tables.test.cpp @@ -15,7 +15,6 @@ using namespace Luau; -LUAU_FASTFLAG(LuauLowerBoundsCalculation); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(LuauInstantiateInSubtyping); LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls); @@ -3257,7 +3256,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "dont_leak_free_table_props") TEST_CASE_FIXTURE(Fixture, "inferred_return_type_of_free_table") { ScopedFastFlag sff[] = { - // {FFlag::LuauLowerBoundsCalculation, true}, {FFlag::DebugLuauSharedSelf, true}, }; diff --git a/tests/TypeInfer.tryUnify.test.cpp b/tests/TypeInfer.tryUnify.test.cpp index 58ccea891..92f07c430 100644 --- a/tests/TypeInfer.tryUnify.test.cpp +++ b/tests/TypeInfer.tryUnify.test.cpp @@ -13,6 +13,7 @@ using namespace Luau; LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls); +LUAU_FASTFLAG(LuauUnifierRecursionOnRestart); struct TryUnifyFixture : Fixture { @@ -480,4 +481,34 @@ TEST_CASE_FIXTURE(TryUnifyFixture, "unifying_two_unions_under_dcr_does_not_creat } } +TEST_CASE_FIXTURE(BuiltinsFixture, "table_unification_full_restart_recursion") +{ + ScopedFastFlag luauUnifierRecursionOnRestart{FFlag::LuauUnifierRecursionOnRestart, true}; + + CheckResult result = check(R"( +local A, B, C, D + +E = function(a, b) + local mt = getmetatable(b) + if mt.tm:bar(A) == nil and mt.tm:bar(B) == nil then end + if mt.foo == true then D(b, 3) end + mt.foo:call(false, b) +end + +A = function(a, b) + local mt = getmetatable(b) + if mt.foo == true then D(b, 3) end + C(mt, 3) +end + +B = function(a, b) + local mt = getmetatable(b) + if mt.foo == true then D(b, 3) end + C(mt, 3) +end + )"); + + LUAU_REQUIRE_ERRORS(result); +} + TEST_SUITE_END(); diff --git a/tests/TypeInfer.unknownnever.test.cpp b/tests/TypeInfer.unknownnever.test.cpp index 8ec70d118..f1924b1c8 100644 --- a/tests/TypeInfer.unknownnever.test.cpp +++ b/tests/TypeInfer.unknownnever.test.cpp @@ -396,4 +396,15 @@ TEST_CASE_FIXTURE(Fixture, "lti_permit_explicit_never_annotation") LUAU_REQUIRE_NO_ERRORS(result); } +TEST_CASE_FIXTURE(Fixture, "cast_from_never_does_not_error") +{ + CheckResult result = check(R"( + local function f(x: never): number + return x :: number + end + )"); + + LUAU_REQUIRE_NO_ERRORS(result); +} + TEST_SUITE_END(); diff --git a/tests/conformance/math.lua b/tests/conformance/math.lua index b8fc882a9..98d5b3173 100644 --- a/tests/conformance/math.lua +++ b/tests/conformance/math.lua @@ -296,14 +296,26 @@ assert(math.max(ma, mc, mb) == 2) assert(math.max(ma, mb, mc) == 2) assert(math.max(ma, mb, mc, md) == 2) +local inf = math.huge * 2 +local nan = 0 / 0 + +assert(math.min(nan, 2) ~= math.min(nan, 2)) +assert(math.min(1, nan) == 1) +assert(math.max(nan, 2) ~= math.max(nan, 2)) +assert(math.max(1, nan) == 1) + +local function noinline(x, ...) local s, r = pcall(function(y) return y end, x) return r end + -- noise assert(math.noise(0.5) == 0) assert(math.noise(0.5, 0.5) == -0.25) assert(math.noise(0.5, 0.5, -0.5) == 0.125) assert(math.noise(455.7204209769105, 340.80410508750134, 121.80087666537628) == 0.5010709762573242) -local inf = math.huge * 2 -local nan = 0 / 0 +assert(math.noise(noinline(0.5)) == 0) +assert(math.noise(noinline(0.5), 0.5) == -0.25) +assert(math.noise(noinline(0.5), 0.5, -0.5) == 0.125) +assert(math.noise(noinline(455.7204209769105), 340.80410508750134, 121.80087666537628) == 0.5010709762573242) -- sign assert(math.sign(0) == 0) @@ -313,10 +325,12 @@ assert(math.sign(inf) == 1) assert(math.sign(-inf) == -1) assert(math.sign(nan) == 0) -assert(math.min(nan, 2) ~= math.min(nan, 2)) -assert(math.min(1, nan) == 1) -assert(math.max(nan, 2) ~= math.max(nan, 2)) -assert(math.max(1, nan) == 1) +assert(math.sign(noinline(0)) == 0) +assert(math.sign(noinline(42)) == 1) +assert(math.sign(noinline(-42)) == -1) +assert(math.sign(noinline(inf)) == 1) +assert(math.sign(noinline(-inf)) == -1) +assert(math.sign(noinline(nan)) == 0) -- clamp assert(math.clamp(-1, 0, 1) == 0) @@ -324,6 +338,11 @@ assert(math.clamp(0.5, 0, 1) == 0.5) assert(math.clamp(2, 0, 1) == 1) assert(math.clamp(4, 0, 0) == 0) +assert(math.clamp(noinline(-1), 0, 1) == 0) +assert(math.clamp(noinline(0.5), 0, 1) == 0.5) +assert(math.clamp(noinline(2), 0, 1) == 1) +assert(math.clamp(noinline(4), 0, 0) == 0) + -- round assert(math.round(0) == 0) assert(math.round(0.4) == 0) @@ -336,19 +355,58 @@ assert(math.round(math.huge) == math.huge) assert(math.round(0.49999999999999994) == 0) assert(math.round(-0.49999999999999994) == 0) +assert(math.round(noinline(0)) == 0) +assert(math.round(noinline(0.4)) == 0) +assert(math.round(noinline(0.5)) == 1) +assert(math.round(noinline(3.5)) == 4) +assert(math.round(noinline(-0.4)) == 0) +assert(math.round(noinline(-0.5)) == -1) +assert(math.round(noinline(-3.5)) == -4) +assert(math.round(noinline(math.huge)) == math.huge) +assert(math.round(noinline(0.49999999999999994)) == 0) +assert(math.round(noinline(-0.49999999999999994)) == 0) + -- fmod assert(math.fmod(3, 2) == 1) assert(math.fmod(-3, 2) == -1) assert(math.fmod(3, -2) == 1) assert(math.fmod(-3, -2) == -1) +assert(math.fmod(noinline(3), 2) == 1) +assert(math.fmod(noinline(-3), 2) == -1) +assert(math.fmod(noinline(3), -2) == 1) +assert(math.fmod(noinline(-3), -2) == -1) + -- pow assert(math.pow(2, 0) == 1) assert(math.pow(2, 2) == 4) assert(math.pow(4, 0.5) == 2) assert(math.pow(-2, 2) == 4) + +assert(math.pow(noinline(2), 0) == 1) +assert(math.pow(noinline(2), 2) == 4) +assert(math.pow(noinline(4), 0.5) == 2) +assert(math.pow(noinline(-2), 2) == 4) + assert(tostring(math.pow(-2, 0.5)) == "nan") +-- test that fastcalls return correct number of results +assert(select('#', math.floor(1.4)) == 1) +assert(select('#', math.ceil(1.6)) == 1) +assert(select('#', math.sqrt(9)) == 1) +assert(select('#', math.deg(9)) == 1) +assert(select('#', math.rad(9)) == 1) +assert(select('#', math.sin(1.5)) == 1) +assert(select('#', math.atan2(1.5, 0.5)) == 1) +assert(select('#', math.modf(1.5)) == 2) +assert(select('#', math.frexp(1.5)) == 2) + +-- test that fastcalls that return variadic results return them correctly in variadic position +assert(select(1, math.modf(1.5)) == 1) +assert(select(2, math.modf(1.5)) == 0.5) +assert(select(1, math.frexp(1.5)) == 0.75) +assert(select(2, math.frexp(1.5)) == 1) + -- most of the tests above go through fastcall path -- to make sure the basic implementations are also correct we test these functions with string->number coercions assert(math.abs("-4") == 4) @@ -393,21 +451,4 @@ assert(math.sign("-2") == -1) assert(math.sign("0") == 0) assert(math.round("1.8") == 2) --- test that fastcalls return correct number of results -assert(select('#', math.floor(1.4)) == 1) -assert(select('#', math.ceil(1.6)) == 1) -assert(select('#', math.sqrt(9)) == 1) -assert(select('#', math.deg(9)) == 1) -assert(select('#', math.rad(9)) == 1) -assert(select('#', math.sin(1.5)) == 1) -assert(select('#', math.atan2(1.5, 0.5)) == 1) -assert(select('#', math.modf(1.5)) == 2) -assert(select('#', math.frexp(1.5)) == 2) - --- test that fastcalls that return variadic results return them correctly in variadic position -assert(select(1, math.modf(1.5)) == 1) -assert(select(2, math.modf(1.5)) == 0.5) -assert(select(1, math.frexp(1.5)) == 0.75) -assert(select(2, math.frexp(1.5)) == 1) - return('OK')