Sync to upstream/release/632 (#1307)

# What's Changed?

- Fix #1137 by appropriately retaining additional metadata from
definition files throughout the type system.
- Improve Frontend for LSPs by appropriately allowing the cancellation
of typechecking while running its destructor.

## New Solver

- Added support for the `rawget` type function.
- Reduced overall static memory usage of builtin type functions.
- Fixed a crash where visitors could mutate a union or intersection type
and fail to invalidate iteration over them in doing so.
- Revised autocomplete functionality to not rely on a separate run of
the type solver when using the new solver.
- Implemented a more relaxed semantic rule for casting.
- Fixed some smaller crashes in the new solver.

## Native Code Generation

- Add additional codegen specialization for `math.sign`
- Cleaned up a large number of outstanding fflags in the code.

### Internal Contributors

Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: James McNellis <jmcnellis@roblox.com>
Co-authored-by: Jeremy Yoo <jyoo@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

Co-authored-by: Alexander McCord <amccord@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Vighnesh <vvijay@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
aaron 2024-06-28 20:34:49 -04:00 committed by GitHub
parent caee04d82d
commit 0d2688844a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
61 changed files with 1243 additions and 942 deletions

View File

@ -191,7 +191,7 @@ struct Frontend
void queueModuleCheck(const std::vector<ModuleName>& names);
void queueModuleCheck(const ModuleName& name);
std::vector<ModuleName> checkQueuedModules(std::optional<FrontendOptions> optionOverride = {},
std::function<void(std::function<void()> task)> executeTask = {}, std::function<void(size_t done, size_t total)> progress = {});
std::function<void(std::function<void()> task)> executeTask = {}, std::function<bool(size_t done, size_t total)> progress = {});
std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);

View File

@ -180,12 +180,11 @@ struct BuiltinTypeFamilies
TypeFamily rawkeyofFamily;
TypeFamily indexFamily;
TypeFamily rawgetFamily;
void addToScope(NotNull<TypeArena> arena, NotNull<Scope> scope) const;
};
const BuiltinTypeFamilies kBuiltinTypeFamilies{};
const BuiltinTypeFamilies& builtinTypeFunctions();
} // namespace Luau

View File

@ -348,16 +348,38 @@ struct GenericTypeVisitor
{
if (visit(ty, *utv))
{
bool unionChanged = false;
for (TypeId optTy : utv->options)
{
traverse(optTy);
if (!get<UnionType>(follow(ty)))
{
unionChanged = true;
break;
}
}
if (unionChanged)
traverse(ty);
}
}
else if (auto itv = get<IntersectionType>(ty))
{
if (visit(ty, *itv))
{
bool intersectionChanged = false;
for (TypeId partTy : itv->parts)
{
traverse(partTy);
if (!get<IntersectionType>(follow(ty)))
{
intersectionChanged = true;
break;
}
}
if (intersectionChanged)
traverse(ty);
}
}
else if (auto ltv = get<LazyType>(ty))

View File

@ -8,6 +8,8 @@
#include <math.h>
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("}");
}

View File

@ -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> 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);

View File

@ -216,7 +216,7 @@ void registerBuiltinGlobals(Frontend& frontend, GlobalTypes& globals, bool typeC
NotNull<BuiltinTypes> 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<T>(value: T, errorMessage: string?): intersect<T, ~(false?)>
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}})});

View File

@ -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<IntersectionType>(currentTy))
{
std::vector<TypeId> options = itv->parts;
options.push_back(propTy);
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
prop.readTy = newItv;
prop.writeTy = newItv;
}
else if (get<FunctionType>(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<FunctionType>(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<NeverType>(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};
}

View File

@ -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<ErrorType>(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<ErrorType>(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<T, ExplicitFunctionAnnotationRecommended>)
{
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<T, UninhabitedTypePackFamily>)

View File

@ -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<FrontendOption
LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
FrontendOptions frontendOptions = optionOverride.value_or(options);
if (FFlag::DebugLuauDeferredConstraintResolution)
frontendOptions.forAutocomplete = false;
if (std::optional<CheckResult> result = getCheckResult(name, true, frontendOptions.forAutocomplete))
return std::move(*result);
@ -492,9 +495,11 @@ void Frontend::queueModuleCheck(const ModuleName& name)
}
std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptions> optionOverride,
std::function<void(std::function<void()> task)> executeTask, std::function<void(size_t done, size_t total)> progress)
std::function<void(std::function<void()> task)> executeTask, std::function<bool(size_t done, size_t total)> 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<ModuleName> currModuleQueue;
@ -673,7 +678,17 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
}
if (progress)
progress(buildQueueItems.size() - remaining, buildQueueItems.size());
{
if (FFlag::LuauCancelFromProgress)
{
if (!progress(buildQueueItems.size() - remaining, buildQueueItems.size()))
cancelled = true;
}
else
{
progress(buildQueueItems.size() - remaining, buildQueueItems.size());
}
}
// Items cannot be submitted while holding the lock
for (size_t i : nextItems)
@ -707,6 +722,9 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
std::optional<CheckResult> 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)

View File

@ -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)

View File

@ -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<TypeFamilyReductionResult<TypeId>> 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<TypeId>& 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, "name"> => `Person` is the indexee and `"name"` is the indexer */
TypeFamilyReductionResult<TypeId> indexFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx)
TypeFamilyReductionResult<TypeId> indexFamilyImpl(
const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> 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<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
@ -2003,12 +2000,14 @@ TypeFamilyReductionResult<TypeId> indexFamilyFn(
typesToFind = &singleType;
DenseHashSet<TypeId> 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<TypeId> 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<TypeId> indexFamilyFn(
return {ctx->arena->addType(UnionType{std::vector<TypeId>(properties.begin(), properties.end())}), false, {}, {}};
}
TypeFamilyReductionResult<TypeId> indexFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> 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<TypeId> rawgetFamilyFn(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> 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<TypeArena> arena, NotNull<Scope> 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<const BuiltinTypeFamilies> result = std::make_unique<BuiltinTypeFamilies>();
return *result;
}
} // namespace Luau

View File

@ -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<IntersectionType>(currentTy))
{
std::vector<TypeId> options = itv->parts;
options.push_back(propTy);
TypeId newItv = addType(IntersectionType{std::move(options)});
prop.readTy = newItv;
prop.writeTy = newItv;
}
else if (get<FunctionType>(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<FunctionType>(fnType);
ftv->argNames.reserve(global.paramNames.size);

View File

@ -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<TableType>(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

View File

@ -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<AstGenericType>& generics,
const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params, const AstArray<AstArgumentName>& paramNames,
const AstTypeList& retTypes);
AstStatDeclareFunction(const Location& location, const AstName& name, const Location& nameLocation, const AstArray<AstGenericType>& generics,
const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params, const AstArray<AstArgumentName>& paramNames, bool vararg,
const Location& varargLocation, const AstTypeList& retTypes);
AstStatDeclareFunction(const Location& location, const AstArray<AstAttr*>& attributes, const AstName& name,
AstStatDeclareFunction(const Location& location, const AstArray<AstAttr*>& attributes, const AstName& name, const Location& nameLocation,
const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params,
const AstArray<AstArgumentName>& paramNames, const AstTypeList& retTypes);
const AstArray<AstArgumentName>& paramNames, bool vararg, const Location& varargLocation, const AstTypeList& retTypes);
void visit(AstVisitor* visitor) override;
@ -854,18 +855,23 @@ public:
AstArray<AstAttr*> attributes;
AstName name;
Location nameLocation;
AstArray<AstGenericType> generics;
AstArray<AstGenericTypePack> genericPacks;
AstTypeList params;
AstArray<AstArgumentName> 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

View File

@ -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

View File

@ -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<AstGenericType>& generics,
const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params, const AstArray<AstArgumentName>& paramNames,
const AstTypeList& retTypes)
AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstName& name, const Location& nameLocation,
const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params,
const AstArray<AstArgumentName>& 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<AstAttr*>& attributes, const AstName& name,
const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params,
const AstArray<AstArgumentName>& paramNames, const AstTypeList& retTypes)
const Location& nameLocation, const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks,
const AstTypeList& params, const AstArray<AstArgumentName>& 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)
{
}

View File

@ -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<AstType*>(nullptr, 0), nullptr});
Location end = lexer.current().location;
Location end = FFlag::LuauDeclarationExtraPropData ? lexer.previousLocation() : lexer.current().location;
TempVector<AstType*> vars(scratchType);
TempVector<std::optional<AstArgumentName>> 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<AstTypeFunction>(
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<AstAttr*>& attributes)
@ -1014,8 +1024,12 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
if (vararg && !varargAnnotation)
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
return allocator.alloc<AstStatDeclareFunction>(Location(start, end), attributes, globalName.name, generics, genericPacks,
AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes);
if (FFlag::LuauDeclarationExtraPropData)
return allocator.alloc<AstStatDeclareFunction>(Location(start, end), attributes, globalName.name, globalName.location, generics,
genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), vararg, varargLocation, retTypes);
else
return allocator.alloc<AstStatDeclareFunction>(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<AstAttr*
const Lexeme begin = lexer.current();
nextLexeme(); // [
std::optional<AstArray<char>> chars = parseCharArray();
if (FFlag::LuauDeclarationExtraPropData)
{
const Location nameBegin = lexer.current().location;
std::optional<AstArray<char>> 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<AstArray<char>> 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<AstAttr*
indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt);
}
}
else if (FFlag::LuauDeclarationExtraPropData)
{
Location propStart = lexer.current().location;
Name propName = parseName("property name");
expectAndConsume(':', "property type annotation");
AstType* propType = parseType();
props.push_back(
AstDeclaredClassProp{propName.name, propName.location, propType, false, Location(propStart, lexer.previousLocation())});
}
else
{
Name propName = parseName("property name");
expectAndConsume(':', "property type annotation");
AstType* propType = parseType();
props.push_back(AstDeclaredClassProp{propName.name, propType, false});
props.push_back(AstDeclaredClassProp{propName.name, Location{}, propType, false});
}
}
@ -1094,7 +1140,8 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
expectAndConsume(':', "global variable declaration");
AstType* type = parseType(/* in declaration context */ true);
return allocator.alloc<AstStatDeclareGlobal>(Location(start, type->location), globalName->name, type);
return allocator.alloc<AstStatDeclareGlobal>(
Location(start, type->location), globalName->name, FFlag::LuauDeclarationExtraPropData ? globalName->location : Location{}, type);
}
else
{

View File

@ -250,6 +250,10 @@ void flushEvents(GlobalContext& context, uint32_t threadId, const std::vector<Ev
ThreadContext& getThreadContext()
{
// Check custom provider that which might implement a custom TLS
if (auto provider = threadContextProvider())
return provider();
thread_local ThreadContext context;
return context;
}

View File

@ -179,6 +179,10 @@ enum class IrCmd : uint8_t
// A: double
ABS_NUM,
// Get the sign of the argument (math.sign)
// A: double
SIGN_NUM,
// Add/Sub/Mul/Div/Idiv two vectors
// A, B: TValue
ADD_VEC,

View File

@ -171,6 +171,7 @@ inline bool hasResult(IrCmd cmd)
case IrCmd::ROUND_NUM:
case IrCmd::SQRT_NUM:
case IrCmd::ABS_NUM:
case IrCmd::SIGN_NUM:
case IrCmd::ADD_VEC:
case IrCmd::SUB_VEC:
case IrCmd::MUL_VEC:

View File

@ -11,8 +11,6 @@
#include <algorithm>
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);
if (knownNextCallResult != LBC_TYPE_ANY)
{
int ra = LUAU_INSN_A(*pc);
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:

View File

@ -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;

View File

@ -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:

View File

@ -14,8 +14,6 @@
#include <utility>
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)
{
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
{
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);

View File

@ -13,7 +13,6 @@
#include <string.h>
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)));

View File

@ -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:

View File

@ -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,47 +1302,38 @@ 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)))
{
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;
}
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);
}
build.blr(x4);
emitUpdateBase(build);
break;

View File

@ -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});

View File

@ -9,6 +9,7 @@
#include <math.h>
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:

View File

@ -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);

View File

@ -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)
{

View File

@ -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<TM_ADD>;
context.luaV_doarithsub = luaV_doarithimpl<TM_SUB>;

View File

@ -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;

View File

@ -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;
replace(function, block, index, {IrCmd::STORE_SPLIT_TVALUE, inst.a, build.constTag(tag), value, inst.c});
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});
// 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;

View File

@ -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,

View File

@ -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

View File

@ -7,7 +7,6 @@
#include <algorithm>
#include <string.h>
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);
for (const TypedUpval& l : typedUpvals)
writeByte(tempTypeInfo, l.type);
for (const TypedLocal& l : typedLocals)
{
// 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);
for (const TypedUpval& l : typedUpvals)
writeByte(tempTypeInfo, l.type);
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);
}
writeVarInt(ss, uint32_t(tempTypeInfo.size()));
ss.append(tempTypeInfo);
}
else
{
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<int>& dumpinstoffs)
}
}
if (FFlag::LuauCompileTypeInfo)
if (dumpFlags & Dump_Types)
{
if (dumpFlags & Dump_Types)
const std::string& typeinfo = functions.back().typeinfo;
if (FFlag::LuauCompileUserdataInfo)
{
const std::string& typeinfo = functions.back().typeinfo;
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);
}
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) ? "?" : "";
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];
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 [argument]\n", i - 2, name, optional);
}
else
for (size_t i = 0; i < typedUpvals.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];
const TypedUpval& l = typedUpvals[i];
const char* base = getBaseTypeString(et);
const char* optional = (et & 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 [argument]\n", i - 2, base, optional);
}
formatAppend(result, "U%d: %s%s\n", int(i), name, optional);
}
for (size_t i = 0; i < typedUpvals.size(); ++i)
{
const TypedUpval& l = typedUpvals[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* 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), base, optional);
}
formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, name, optional, l.startpc, l.endpc);
}
}
else
{
// Arguments start from third byte in function typeinfo string
for (uint8_t i = 2; i < typeinfo.size(); ++i)
{
uint8_t et = typeinfo[i];
for (size_t i = 0; i < typedLocals.size(); ++i)
{
const TypedLocal& l = typedLocals[i];
const char* base = getBaseTypeString(et);
const char* optional = (et & 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 [argument]\n", i - 2, base, optional);
}
formatAppend(result, "R%d: %s%s from %d to %d\n", l.reg, base, optional, l.startpc, l.endpc);
}
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) ? "?" : "";
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];
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);
}
}
}

View File

@ -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<AstExprCall>())
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)
{

View File

@ -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<AstName, Compile::Global>& globals, AstExpr* node, const char* name)
{
LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo);
if (AstExprGlobal* expr = node->as<AstExprGlobal>())
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<AstTypeReference>())
{
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<AstTypeTable>())
@ -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)
AstLocal* local = node->local;
if (AstType* annotation = local->annotation)
{
if (FFlag::LuauCompileTypeInfo)
{
AstLocal* local = node->local;
LuauBytecodeType ty = recordResolvedType(node, annotation);
if (AstType* annotation = local->annotation)
{
LuauBytecodeType ty = recordResolvedType(node, annotation);
if (ty != LBC_TYPE_ANY)
localTypes[local] = ty;
}
else if (const AstType** typePtr = resolvedLocals.find(local))
{
localTypes[local] = recordResolvedType(node, *typePtr);
}
}
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))

View File

@ -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<TMS op>
void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);

View File

@ -16,8 +16,6 @@
#include <string.h>
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 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_ADD));
}
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1542,14 +1533,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_SUB));
}
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1610,14 +1594,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MUL));
}
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1678,14 +1655,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_DIV));
}
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1733,14 +1703,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_IDIV));
}
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1764,14 +1727,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MOD));
}
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1792,14 +1748,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_POW));
}
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, rc));
VM_NEXT();
}
}
@ -1820,14 +1769,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_ADD));
}
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -1848,14 +1790,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_SUB));
}
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -1900,14 +1835,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MUL));
}
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -1953,14 +1881,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_DIV));
}
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -2007,14 +1928,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_IDIV));
}
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -2038,14 +1952,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MOD));
}
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -2072,14 +1979,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_POW));
}
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, kv));
VM_NEXT();
}
}
@ -2192,14 +2092,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_UNM>(L, ra, rb, rb));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rb, TM_UNM));
}
VM_PROTECT(luaV_doarithimpl<TM_UNM>(L, ra, rb, rb));
VM_NEXT();
}
}
@ -2812,14 +2705,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, kv, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_SUB));
}
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, kv, rc));
VM_NEXT();
}
}
@ -2847,14 +2733,7 @@ reentry:
else
{
// slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith)
{
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, kv, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_DIV));
}
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, kv, rc));
VM_NEXT();
}
}

View File

@ -519,140 +519,6 @@ template void luaV_doarithimpl<TM_MOD>(lua_State* L, StkId ra, const TValue* rb,
template void luaV_doarithimpl<TM_POW>(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);
template void luaV_doarithimpl<TM_UNM>(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;

View File

@ -9,6 +9,8 @@
#include <math.h>
#include <ostream>
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);
}

View File

@ -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<Binding> require = frontend.globalsForAutocomplete.globalScope->linearSearchForBinding("require");
GlobalTypes& globals = FFlag::DebugLuauDeferredConstraintResolution ? frontend.globals : frontend.globalsForAutocomplete;
std::optional<Binding> 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<Binding> require = frontend.globalsForAutocomplete.globalScope->linearSearchForBinding("require");
GlobalTypes& globals = FFlag::DebugLuauDeferredConstraintResolution ? frontend.globals : frontend.globalsForAutocomplete;
std::optional<Binding> 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)

View File

@ -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"(

View File

@ -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;

View File

@ -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();

View File

@ -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<FreeType>(h)->upperBound = i;
getMutable<FreeType>(h)->lowerBound = builtinTypes.neverType;
getMutable<FreeType>(i)->upperBound = builtinTypes.unknownType;
getMutable<FreeType>(i)->lowerBound = unionType;
getMutable<FreeType>(j)->upperBound = i;
getMutable<FreeType>(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<FreeType>(h)->upperBound = i;
getMutable<FreeType>(h)->lowerBound = builtinTypes.neverType;
getMutable<FreeType>(i)->upperBound = builtinTypes.unknownType;
getMutable<FreeType>(i)->lowerBound = intersectionType;
getMutable<FreeType>(j)->upperBound = i;
getMutable<FreeType>(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();

View File

@ -13,9 +13,9 @@
#include <limits.h>
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 @@ bb_0:
STORE_INT R19, 0i
STORE_INT R20, 1i
STORE_INT R21, 0i
STORE_DOUBLE R22, -1
RETURN 0u
)");
@ -2662,8 +2667,6 @@ bb_0:
TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1")
{
ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true};
IrOp entry = build.block(IrBlockKind::Internal);
build.beginBlock(entry);
@ -2690,8 +2693,6 @@ bb_0:
TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore2")
{
ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true};
IrOp entry = build.block(IrBlockKind::Internal);
build.beginBlock(entry);

View File

@ -15,9 +15,6 @@
#include <memory>
#include <string_view>
LUAU_FASTFLAG(LuauCompileTypeInfo)
LUAU_FASTFLAG(LuauCompileTempTypeInfo)
LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps)
LUAU_FASTFLAG(LuauCompileUserdataInfo)
LUAU_FASTFLAG(LuauLoadUserdataInfo)
LUAU_FASTFLAG(LuauCodegenUserdataOps)
@ -427,27 +424,6 @@ bb_bytecode_0:
)");
}
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 @@ bb_6:
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 @@ bb_bytecode_1:
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 @@ bb_bytecode_1:
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 @@ bb_bytecode_1:
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 @@ bb_bytecode_1:
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 @@ bb_4:
TEST_CASE("ExplicitUpvalueAndLocalTypes")
{
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local y: vector = ...
@ -933,7 +897,7 @@ bb_bytecode_0:
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 @@ bb_bytecode_1:
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 @@ bb_bytecode_1:
TEST_CASE("LoadAndMoveTypePropagation")
{
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"(
local function getsum(n)
local seqsum = 0
@ -1105,7 +1067,7 @@ bb_bytecode_4:
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 @@ bb_bytecode_0:
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 @@ bb_bytecode_0:
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 @@ bb_6:
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 @@ bb_6:
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 @@ bb_2:
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 @@ bb_12:
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)

View File

@ -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<AstStatDeclareGlobal>();
REQUIRE(global);
CHECK(global->name == "foo");
CHECK(global->nameLocation == Location({1, 16}, {1, 19}));
CHECK(global->type);
AstStatDeclareFunction* func = stat->body.data[1]->as<AstStatDeclareFunction>();
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<AstStatDeclareFunction>();
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 <eof>");
@ -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<AstTypeReference>());
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<AstTypeFunction>());
CHECK(method.location == Location({3, 12}, {3, 54}));
CHECK(method.isMethod);
AstStatDeclareClass* subclass = stat->body.data[1]->as<AstStatDeclareClass>();
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<AstTypeReference>());
CHECK(prop2.location == Location({7, 12}, {7, 25}));
}
TEST_CASE_FIXTURE(Fixture, "class_method_properties")

View File

@ -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<MyObject, "a">
type RawBType = rawget<MyObject, keyof<MyObject>>
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<TypePackMismatch>(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<typeof(MyObject), number>
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<MyObject, key>
)");
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK(toString(result.errors[0]) == "Second argument to rawget<MyObject, _> 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<MyObject, "a" | "b">
local function ok(idx: rawType): string | number return idx end
type errType = rawget<MyObject, "a" | "d">
)");
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<MyObject | MyObject2, "a">
local function ok(idx: rawTypeA): string | number return idx end
type errType = rawget<MyObject | MyObject2, "b">
)");
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<typeof(exampleClass2), "Foo">
local function ok(idx: exampleTy2): number return idx end
local exampleClass3 = setmetatable({ Bar = 5 }, { __index = exampleClass })
type errType = rawget<typeof(exampleClass3), "Foo">
type errType2 = rawget<typeof(exampleClass3), "Bar" | "Foo">
)");
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<BaseClass, "BaseField">
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) == "Property '\"BaseField\"' does not exist on type 'BaseClass'");
}
TEST_SUITE_END();

View File

@ -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();

View File

@ -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"(

View File

@ -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<TypeFun> myClassTy = frontend.globals.globalScope->lookupType("MyClass");
REQUIRE(bool(myClassTy));
CHECK_EQ(myClassTy->type->documentationSymbol, "@test/globaltype/MyClass");
ClassType* cls = getMutable<ClassType>(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<FunctionType>(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")

View File

@ -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<ExplicitFunctionAnnotationRecommended>(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"(

View File

@ -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.

View File

@ -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},
};

View File

@ -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();

View File

@ -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();

View File

@ -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')