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 std::vector<ModuleName>& names);
void queueModuleCheck(const ModuleName& name); void queueModuleCheck(const ModuleName& name);
std::vector<ModuleName> checkQueuedModules(std::optional<FrontendOptions> optionOverride = {}, 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); std::optional<CheckResult> getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete = false);

View File

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

View File

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

View File

@ -8,6 +8,8 @@
#include <math.h> #include <math.h>
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
namespace Luau namespace Luau
{ {
@ -735,8 +737,21 @@ struct AstJsonEncoder : public AstVisitor
void write(class AstStatDeclareFunction* node) void write(class AstStatDeclareFunction* node)
{ {
writeNode(node, "AstStatDeclareFunction", [&]() { writeNode(node, "AstStatDeclareFunction", [&]() {
// TODO: attributes
PROP(name); PROP(name);
if (FFlag::LuauDeclarationExtraPropData)
PROP(nameLocation);
PROP(params); PROP(params);
if (FFlag::LuauDeclarationExtraPropData)
{
PROP(paramNames);
PROP(vararg);
PROP(varargLocation);
}
PROP(retTypes); PROP(retTypes);
PROP(generics); PROP(generics);
PROP(genericPacks); PROP(genericPacks);
@ -747,6 +762,10 @@ struct AstJsonEncoder : public AstVisitor
{ {
writeNode(node, "AstStatDeclareGlobal", [&]() { writeNode(node, "AstStatDeclareGlobal", [&]() {
PROP(name); PROP(name);
if (FFlag::LuauDeclarationExtraPropData)
PROP(nameLocation);
PROP(type); PROP(type);
}); });
} }
@ -756,8 +775,16 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("{"); writeRaw("{");
bool c = pushComma(); bool c = pushComma();
write("name", prop.name); write("name", prop.name);
if (FFlag::LuauDeclarationExtraPropData)
write("nameLocation", prop.nameLocation);
writeType("AstDeclaredClassProp"); writeType("AstDeclaredClassProp");
write("luauType", prop.ty); write("luauType", prop.ty);
if (FFlag::LuauDeclarationExtraPropData)
write("location", prop.location);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
} }

View File

@ -1830,12 +1830,21 @@ AutocompleteResult autocomplete(Frontend& frontend, const ModuleName& moduleName
if (!sourceModule) if (!sourceModule)
return {}; 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) if (!module)
return {}; return {};
NotNull<BuiltinTypes> builtinTypes = frontend.builtinTypes; 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; TypeArena typeArena;
return autocomplete(*sourceModule, module, builtinTypes, &typeArena, globalScope, position, callback); 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; NotNull<BuiltinTypes> builtinTypes = globals.builtinTypes;
if (FFlag::DebugLuauDeferredConstraintResolution) if (FFlag::DebugLuauDeferredConstraintResolution)
kBuiltinTypeFamilies.addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()}); builtinTypeFunctions().addToScope(NotNull{&arena}, NotNull{globals.globalScope.get()});
LoadDefinitionFileResult loadResult = frontend.loadDefinitionFile( LoadDefinitionFileResult loadResult = frontend.loadDefinitionFile(
globals, globals.globalScope, getBuiltinDefinitionSource(), "@luau", /* captureComments */ false, typeCheckForAutocomplete); 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?)> // declare function assert<T>(value: T, errorMessage: string?): intersect<T, ~(false?)>
TypeId genericT = arena.addType(GenericType{"T"}); TypeId genericT = arena.addType(GenericType{"T"});
TypeId refinedTy = arena.addType(TypeFamilyInstanceType{ 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{ TypeId assertTy = arena.addType(FunctionType{
{genericT}, {}, arena.addTypePack(TypePack{{genericT, builtinTypes->optionalStringType}}), arena.addTypePack(TypePack{{refinedTy}})}); {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(DebugLuauLogSolverToJson);
LUAU_FASTFLAG(DebugLuauMagicTypes); LUAU_FASTFLAG(DebugLuauMagicTypes);
LUAU_FASTFLAG(LuauAttributeSyntax); LUAU_FASTFLAG(LuauAttributeSyntax);
LUAU_FASTFLAG(LuauDeclarationExtraPropData);
namespace Luau namespace Luau
{ {
@ -431,7 +432,7 @@ void ConstraintGenerator::computeRefinement(const ScopePtr& scope, Location loca
discriminantTy = arena->addType(NegationType{discriminantTy}); discriminantTy = arena->addType(NegationType{discriminantTy});
if (eq) 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) 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)) 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; ty = resultType;
} }
@ -1389,6 +1390,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
ftv->argTypes = addTypePack({classTy}, ftv->argTypes); ftv->argTypes = addTypePack({classTy}, ftv->argTypes);
ftv->hasSelf = true; 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) 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 else
{ {
@ -1453,7 +1497,18 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareFunc
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false); TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* 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); FunctionType* ftv = getMutable<FunctionType>(fnType);
ftv->isCheckedFunction = FFlag::LuauAttributeSyntax ? global->isCheckedFunction() : false; ftv->isCheckedFunction = FFlag::LuauAttributeSyntax ? global->isCheckedFunction() : false;
@ -2032,17 +2087,17 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprUnary* unary)
{ {
case AstExprUnary::Op::Not: 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)}; return Inference{resultType, refinementArena.negation(refinement)};
} }
case AstExprUnary::Op::Len: 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)}; return Inference{resultType, refinementArena.negation(refinement)};
} }
case AstExprUnary::Op::Minus: 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)}; return Inference{resultType, refinementArena.negation(refinement)};
} }
default: // msvc can't prove that this is exhaustive. 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: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Sub: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Mul: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Div: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::FloorDiv: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Pow: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Mod: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Concat: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::And: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Or: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareLt: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareGe: 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)` {rightType, leftType}, // lua decided that `__ge(a, b)` is instead just `__lt(b, a)`
{}, scope, binary->location); {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareLe: 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::CompareGt: 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)` {rightType, leftType}, // lua decided that `__gt(a, b)` is instead just `__le(b, a)`
{}, scope, binary->location); {}, scope, binary->location);
return Inference{resultType, std::move(refinement)}; return Inference{resultType, std::move(refinement)};
@ -2147,7 +2203,7 @@ Inference ConstraintGenerator::check(const ScopePtr& scope, AstExprBinary* binar
else if (rightSubscripted) else if (rightSubscripted)
rightType = makeUnion(scope, binary->location, rightType, builtinTypes->nilType); 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)}; return Inference{resultType, std::move(refinement)};
} }
case AstExprBinary::Op::Op__Count: case AstExprBinary::Op::Op__Count:
@ -3100,14 +3156,14 @@ TypeId ConstraintGenerator::makeUnion(const ScopePtr& scope, Location location,
if (get<NeverType>(follow(rhs))) if (get<NeverType>(follow(rhs)))
return lhs; return lhs;
TypeId resultType = createTypeFamilyInstance(kBuiltinTypeFamilies.unionFamily, {lhs, rhs}, {}, scope, location); TypeId resultType = createTypeFamilyInstance(builtinTypeFunctions().unionFamily, {lhs, rhs}, {}, scope, location);
return resultType; return resultType;
} }
TypeId ConstraintGenerator::makeIntersect(const ScopePtr& scope, Location location, TypeId lhs, TypeId rhs) 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; return resultType;
} }
@ -3225,7 +3281,7 @@ void ConstraintGenerator::fillInInferredBindings(const ScopePtr& globalScope, As
scope->bindings[symbol] = Binding{tys.front(), location}; scope->bindings[symbol] = Binding{tys.front(), location};
else 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}; 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"; 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) if (tfit->typeArguments.size() != 2)
return "Type family instance " + Luau::toString(e.ty) + " is ill-formed, and thus invalid"; 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 if (auto errType = get<ErrorType>(tfit->typeArguments[1])) // Second argument to (index | rawget)<_,_> is not a type
return "Second argument to index<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type"; return "Second argument to " + tfit->family->name + "<" + Luau::toString(tfit->typeArguments[0]) + ", _> is not a valid index type";
else // Second argument to index<_,_> is not a property of the first argument 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]) + 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>) else if constexpr (std::is_same_v<T, ExplicitFunctionAnnotationRecommended>)
{ {
e.recommendedReturn = clone(e.recommendedReturn); e.recommendedReturn = clone(e.recommendedReturn);
for (auto [_, t] : e.recommendedArgs) for (auto& [_, t] : e.recommendedArgs)
t = clone(t); t = clone(t);
} }
else if constexpr (std::is_same_v<T, UninhabitedTypePackFamily>) else if constexpr (std::is_same_v<T, UninhabitedTypePackFamily>)

View File

@ -34,6 +34,7 @@ LUAU_FASTINT(LuauTypeInferRecursionLimit)
LUAU_FASTINT(LuauTarjanChildLimit) LUAU_FASTINT(LuauTarjanChildLimit)
LUAU_FASTFLAG(LuauInferInNoCheckMode) LUAU_FASTFLAG(LuauInferInNoCheckMode)
LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false) LUAU_FASTFLAGVARIABLE(LuauKnowsTheDataModel3, false)
LUAU_FASTFLAGVARIABLE(LuauCancelFromProgress, false)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJsonFile, 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()); LUAU_TIMETRACE_ARGUMENT("name", name.c_str());
FrontendOptions frontendOptions = optionOverride.value_or(options); FrontendOptions frontendOptions = optionOverride.value_or(options);
if (FFlag::DebugLuauDeferredConstraintResolution)
frontendOptions.forAutocomplete = false;
if (std::optional<CheckResult> result = getCheckResult(name, true, frontendOptions.forAutocomplete)) if (std::optional<CheckResult> result = getCheckResult(name, true, frontendOptions.forAutocomplete))
return std::move(*result); return std::move(*result);
@ -492,9 +495,11 @@ void Frontend::queueModuleCheck(const ModuleName& name)
} }
std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptions> optionOverride, 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); 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 // 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; std::vector<ModuleName> currModuleQueue;
@ -673,7 +678,17 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
} }
if (progress) 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 // Items cannot be submitted while holding the lock
for (size_t i : nextItems) 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) std::optional<CheckResult> Frontend::getCheckResult(const ModuleName& name, bool accumulateNested, bool forAutocomplete)
{ {
if (FFlag::DebugLuauDeferredConstraintResolution)
forAutocomplete = false;
auto it = sourceNodes.find(name); auto it = sourceNodes.find(name);
if (it == sourceNodes.end() || it->second->hasDirtyModule(forAutocomplete)) if (it == sourceNodes.end() || it->second->hasDirtyModule(forAutocomplete))
@ -1006,9 +1024,7 @@ void Frontend::checkBuildQueueItem(BuildQueueItem& item)
module->astCompoundAssignResultTypes.clear(); module->astCompoundAssignResultTypes.clear();
module->astScopes.clear(); module->astScopes.clear();
module->upperBoundContributors.clear(); module->upperBoundContributors.clear();
module->scopes.clear();
if (!FFlag::DebugLuauDeferredConstraintResolution)
module->scopes.clear();
} }
if (mode != Mode::NoCheck) if (mode != Mode::NoCheck)

View File

@ -2138,24 +2138,39 @@ struct TypeChecker2
TypeId annotationType = lookupAnnotation(expr->annotation); TypeId annotationType = lookupAnnotation(expr->annotation);
TypeId computedType = lookupType(expr->expr); 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))) switch (shouldSuppressErrors(NotNull{&normalizer}, computedType).orElse(shouldSuppressErrors(NotNull{&normalizer}, annotationType)))
{ {
case ErrorSuppression::Suppress: case ErrorSuppression::Suppress:
return; return;
case ErrorSuppression::NormalizationFailed: case ErrorSuppression::NormalizationFailed:
reportError(NormalizationTooComplex{}, expr->location); reportError(NormalizationTooComplex{}, expr->location);
return;
case ErrorSuppression::DoNotSuppress: case ErrorSuppression::DoNotSuppress:
break; 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) void visit(AstExprIfElse* expr)

View File

@ -180,7 +180,10 @@ struct FamilyReducer
void replace(T subject, T replacement) void replace(T subject, T replacement)
{ {
if (subject->owningArena != ctx.arena.get()) 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) if (FFlag::DebugLuauLogTypeFamilies)
printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str()); printf("%s -> %s\n", toString(subject, {true}).c_str(), toString(replacement, {true}).c_str());
@ -514,7 +517,7 @@ static std::optional<TypeFamilyReductionResult<TypeId>> tryDistributeTypeFamilyA
return {{results[0], false, {}, {}}}; return {{results[0], false, {}, {}}};
TypeId resultTy = ctx->arena->addType(TypeFamilyInstanceType{ TypeId resultTy = ctx->arena->addType(TypeFamilyInstanceType{
NotNull{&kBuiltinTypeFamilies.unionFamily}, NotNull{&builtinTypeFunctions().unionFamily},
std::move(results), 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, /* Vocabulary note: indexee refers to the type that contains the properties,
indexer refers to the type that is used to access indexee indexer refers to the type that is used to access indexee
Example: index<Person, "name"> => `Person` is the indexee and `"name"` is the indexer */ Example: index<Person, "name"> => `Person` is the indexee and `"name"` is the indexer */
TypeFamilyReductionResult<TypeId> indexFamilyFn( TypeFamilyReductionResult<TypeId> indexFamilyImpl(
TypeId instance, const std::vector<TypeId>& typeParams, const std::vector<TypePackId>& packParams, NotNull<TypeFamilyContext> ctx) 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)); TypeId indexeeTy = follow(typeParams.at(0));
std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy); std::shared_ptr<const NormalizedType> indexeeNormTy = ctx->normalizer->normalize(indexeeTy);
@ -2003,12 +2000,14 @@ TypeFamilyReductionResult<TypeId> indexFamilyFn(
typesToFind = &singleType; typesToFind = &singleType;
DenseHashSet<TypeId> properties{{}}; // vector of types that will be returned DenseHashSet<TypeId> properties{{}}; // vector of types that will be returned
bool isRaw = false;
if (indexeeNormTy->hasClasses()) if (indexeeNormTy->hasClasses())
{ {
LUAU_ASSERT(!indexeeNormTy->hasTables()); 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() // 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) for (auto classesIter = indexeeNormTy->classes.ordering.begin(); classesIter != indexeeNormTy->classes.ordering.end(); ++classesIter)
{ {
@ -2021,7 +2020,7 @@ TypeFamilyReductionResult<TypeId> indexFamilyFn(
for (TypeId ty : *typesToFind) 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)) if (searchPropsAndIndexer(ty, classTy->props, classTy->indexer, properties, ctx))
continue; // Indexer was found in this class, so we can move on to the next 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, {}, {}}; 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() BuiltinTypeFamilies::BuiltinTypeFamilies()
: notFamily{"not", notFamilyFn} : notFamily{"not", notFamilyFn}
, lenFamily{"len", lenFamilyFn} , lenFamily{"len", lenFamilyFn}
@ -2089,6 +2112,7 @@ BuiltinTypeFamilies::BuiltinTypeFamilies()
, keyofFamily{"keyof", keyofFamilyFn} , keyofFamily{"keyof", keyofFamilyFn}
, rawkeyofFamily{"rawkeyof", rawkeyofFamilyFn} , rawkeyofFamily{"rawkeyof", rawkeyofFamilyFn}
, indexFamily{"index", indexFamilyFn} , 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[rawkeyofFamily.name] = mkUnaryTypeFamily(&rawkeyofFamily);
scope->exportedTypeBindings[indexFamily.name] = mkBinaryTypeFamily(&indexFamily); 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 } // namespace Luau

View File

@ -38,6 +38,7 @@ LUAU_FASTFLAGVARIABLE(LuauAlwaysCommitInferencesOfFunctionCalls, false)
LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false) LUAU_FASTFLAGVARIABLE(LuauRemoveBadRelationalOperatorWarning, false)
LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false) LUAU_FASTFLAGVARIABLE(LuauOkWithIteratingOverTableProperties, false)
LUAU_FASTFLAGVARIABLE(LuauReusableSubstitutions, false) LUAU_FASTFLAGVARIABLE(LuauReusableSubstitutions, false)
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
namespace Luau namespace Luau
{ {
@ -1783,12 +1784,55 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareClass&
ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}}); ftv->argNames.insert(ftv->argNames.begin(), FunctionArgument{"self", {}});
ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes}); ftv->argTypes = addTypePack(TypePack{{classTy}, ftv->argTypes});
ftv->hasSelf = true; 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) 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 else
{ {
@ -1840,7 +1884,18 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatDeclareFuncti
TypePackId argPack = resolveTypePack(funScope, global.params); TypePackId argPack = resolveTypePack(funScope, global.params);
TypePackId retPack = resolveTypePack(funScope, global.retTypes); 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); FunctionType* ftv = getMutable<FunctionType>(fnType);
ftv->argNames.reserve(global.paramNames.size); ftv->argNames.reserve(global.paramNames.size);

View File

@ -23,6 +23,7 @@ LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution) LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution)
LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false) LUAU_FASTFLAGVARIABLE(LuauFixIndexerSubtypingOrdering, false)
LUAU_FASTFLAGVARIABLE(LuauUnifierShouldNotCopyError, false) LUAU_FASTFLAGVARIABLE(LuauUnifierShouldNotCopyError, false)
LUAU_FASTFLAGVARIABLE(LuauUnifierRecursionOnRestart, false)
namespace Luau 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 one of the types stopped being a table altogether, we need to restart from the top
if ((superTy != superTyNew || activeSubTy != subTyNew) && errors.empty()) 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 // Otherwise, restart only the table unification
TableType* newSuperTable = log.getMutable<TableType>(superTyNew); 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 one of the types stopped being a table altogether, we need to restart from the top
if ((superTy != superTyNew || activeSubTy != subTyNew) && errors.empty()) 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 // 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 // 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: public:
LUAU_RTTI(AstStatDeclareGlobal) 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; void visit(AstVisitor* visitor) override;
AstName name; AstName name;
Location nameLocation;
AstType* type; AstType* type;
}; };
@ -839,13 +840,13 @@ class AstStatDeclareFunction : public AstStat
public: public:
LUAU_RTTI(AstStatDeclareFunction) LUAU_RTTI(AstStatDeclareFunction)
AstStatDeclareFunction(const Location& location, const AstName& name, const AstArray<AstGenericType>& generics, 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, const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params, const AstArray<AstArgumentName>& paramNames, bool vararg,
const AstTypeList& retTypes); 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<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; void visit(AstVisitor* visitor) override;
@ -854,18 +855,23 @@ public:
AstArray<AstAttr*> attributes; AstArray<AstAttr*> attributes;
AstName name; AstName name;
Location nameLocation;
AstArray<AstGenericType> generics; AstArray<AstGenericType> generics;
AstArray<AstGenericTypePack> genericPacks; AstArray<AstGenericTypePack> genericPacks;
AstTypeList params; AstTypeList params;
AstArray<AstArgumentName> paramNames; AstArray<AstArgumentName> paramNames;
bool vararg = false;
Location varargLocation;
AstTypeList retTypes; AstTypeList retTypes;
}; };
struct AstDeclaredClassProp struct AstDeclaredClassProp
{ {
AstName name; AstName name;
Location nameLocation;
AstType* ty = nullptr; AstType* ty = nullptr;
bool isMethod = false; bool isMethod = false;
Location location;
}; };
enum class AstTableAccess enum class AstTableAccess

View File

@ -134,6 +134,14 @@ struct ThreadContext
static constexpr size_t kEventFlushLimit = 8192; static constexpr size_t kEventFlushLimit = 8192;
}; };
using ThreadContextProvider = ThreadContext& (*)();
inline ThreadContextProvider& threadContextProvider()
{
static ThreadContextProvider handler = nullptr;
return handler;
}
ThreadContext& getThreadContext(); ThreadContext& getThreadContext();
struct Scope 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) : AstStat(ClassIndex(), location)
, name(name) , name(name)
, nameLocation(nameLocation)
, type(type) , type(type)
{ {
} }
@ -718,30 +719,36 @@ void AstStatDeclareGlobal::visit(AstVisitor* visitor)
type->visit(visitor); type->visit(visitor);
} }
AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstName& name, const AstArray<AstGenericType>& generics, AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstName& name, const Location& nameLocation,
const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params, const AstArray<AstArgumentName>& paramNames, const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params,
const AstTypeList& retTypes) const AstArray<AstArgumentName>& paramNames, bool vararg, const Location& varargLocation, const AstTypeList& retTypes)
: AstStat(ClassIndex(), location) : AstStat(ClassIndex(), location)
, attributes() , attributes()
, name(name) , name(name)
, nameLocation(nameLocation)
, generics(generics) , generics(generics)
, genericPacks(genericPacks) , genericPacks(genericPacks)
, params(params) , params(params)
, paramNames(paramNames) , paramNames(paramNames)
, vararg(vararg)
, varargLocation(varargLocation)
, retTypes(retTypes) , retTypes(retTypes)
{ {
} }
AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstArray<AstAttr*>& attributes, const AstName& name, AstStatDeclareFunction::AstStatDeclareFunction(const Location& location, const AstArray<AstAttr*>& attributes, const AstName& name,
const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks, const AstTypeList& params, const Location& nameLocation, const AstArray<AstGenericType>& generics, const AstArray<AstGenericTypePack>& genericPacks,
const AstArray<AstArgumentName>& paramNames, const AstTypeList& retTypes) const AstTypeList& params, const AstArray<AstArgumentName>& paramNames, bool vararg, const Location& varargLocation, const AstTypeList& retTypes)
: AstStat(ClassIndex(), location) : AstStat(ClassIndex(), location)
, attributes(attributes) , attributes(attributes)
, name(name) , name(name)
, nameLocation(nameLocation)
, generics(generics) , generics(generics)
, genericPacks(genericPacks) , genericPacks(genericPacks)
, params(params) , params(params)
, paramNames(paramNames) , paramNames(paramNames)
, vararg(vararg)
, varargLocation(varargLocation)
, retTypes(retTypes) , retTypes(retTypes)
{ {
} }

View File

@ -21,6 +21,7 @@ LUAU_FASTFLAG(LuauAttributeSyntax)
LUAU_FASTFLAGVARIABLE(LuauLeadingBarAndAmpersand2, false) LUAU_FASTFLAGVARIABLE(LuauLeadingBarAndAmpersand2, false)
LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false) LUAU_FASTFLAGVARIABLE(LuauNativeAttribute, false)
LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false) LUAU_FASTFLAGVARIABLE(LuauAttributeSyntaxFunExpr, false)
LUAU_FASTFLAGVARIABLE(LuauDeclarationExtraPropData, false)
namespace Luau namespace Luau
{ {
@ -909,8 +910,16 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
AstDeclaredClassProp Parser::parseDeclaredClassMethod() AstDeclaredClassProp Parser::parseDeclaredClassMethod()
{ {
Location start;
if (FFlag::LuauDeclarationExtraPropData)
start = lexer.current().location;
nextLexeme(); nextLexeme();
Location start = lexer.current().location;
if (!FFlag::LuauDeclarationExtraPropData)
start = lexer.current().location;
Name fnName = parseName("function name"); Name fnName = parseName("function name");
// TODO: generic method declarations CLI-39909 // TODO: generic method declarations CLI-39909
@ -935,15 +944,15 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
expectMatchAndConsume(')', matchParen); expectMatchAndConsume(')', matchParen);
AstTypeList retTypes = parseOptionalReturnType().value_or(AstTypeList{copy<AstType*>(nullptr, 0), nullptr}); 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<AstType*> vars(scratchType);
TempVector<std::optional<AstArgumentName>> varNames(scratchOptArgName); TempVector<std::optional<AstArgumentName>> varNames(scratchOptArgName);
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr) if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
{ {
return AstDeclaredClassProp{ return AstDeclaredClassProp{fnName.name, FFlag::LuauDeclarationExtraPropData ? fnName.location : Location{},
fnName.name, reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true}; reportTypeError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true};
} }
// Skip the first index. // Skip the first index.
@ -963,7 +972,8 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
AstType* fnType = allocator.alloc<AstTypeFunction>( AstType* fnType = allocator.alloc<AstTypeFunction>(
Location(start, end), generics, genericPacks, AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes); 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) 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) if (vararg && !varargAnnotation)
return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated"); return reportStatError(Location(start, end), {}, {}, "All declaration parameters must be annotated");
return allocator.alloc<AstStatDeclareFunction>(Location(start, end), attributes, globalName.name, generics, genericPacks, if (FFlag::LuauDeclarationExtraPropData)
AstTypeList{copy(vars), varargAnnotation}, copy(varNames), retTypes); 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") 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(); const Lexeme begin = lexer.current();
nextLexeme(); // [ nextLexeme(); // [
std::optional<AstArray<char>> chars = parseCharArray(); if (FFlag::LuauDeclarationExtraPropData)
{
const Location nameBegin = lexer.current().location;
std::optional<AstArray<char>> chars = parseCharArray();
expectMatchAndConsume(']', begin); const Location nameEnd = lexer.previousLocation();
expectAndConsume(':', "property type annotation");
AstType* type = parseType();
// since AstName contains a char*, it can't contain null expectMatchAndConsume(']', begin);
bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size); expectAndConsume(':', "property type annotation");
AstType* type = parseType();
if (chars && !containsNull) // since AstName contains a char*, it can't contain null
props.push_back(AstDeclaredClassProp{AstName(chars->data), type, false}); 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 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 == '[') else if (lexer.current().type == '[')
{ {
@ -1075,12 +1112,21 @@ AstStat* Parser::parseDeclaration(const Location& start, const AstArray<AstAttr*
indexer = parseTableIndexer(AstTableAccess::ReadWrite, std::nullopt); 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 else
{ {
Name propName = parseName("property name"); Name propName = parseName("property name");
expectAndConsume(':', "property type annotation"); expectAndConsume(':', "property type annotation");
AstType* propType = parseType(); 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"); expectAndConsume(':', "global variable declaration");
AstType* type = parseType(/* in declaration context */ true); 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 else
{ {

View File

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

View File

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

View File

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

View File

@ -11,8 +11,6 @@
#include <algorithm> #include <algorithm>
LUAU_FASTFLAGVARIABLE(LuauCodegenAnalyzeHostVectorOps, false)
LUAU_FASTFLAGVARIABLE(LuauCodegenLoadTypeUpvalCheck, false)
LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOps, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOps, false)
LUAU_FASTFLAGVARIABLE(LuauCodegenFastcall3, false) LUAU_FASTFLAGVARIABLE(LuauCodegenFastcall3, false)
@ -72,11 +70,6 @@ void loadBytecodeTypeInfo(IrFunction& function)
uint32_t upvalCount = readVarInt(data, offset); uint32_t upvalCount = readVarInt(data, offset);
uint32_t localCount = readVarInt(data, offset); uint32_t localCount = readVarInt(data, offset);
if (!FFlag::LuauCodegenLoadTypeUpvalCheck)
{
CODEGEN_ASSERT(upvalCount == unsigned(proto->nups));
}
if (typeSize != 0) if (typeSize != 0)
{ {
uint8_t* types = (uint8_t*)data + offset; uint8_t* types = (uint8_t*)data + offset;
@ -94,10 +87,7 @@ void loadBytecodeTypeInfo(IrFunction& function)
if (upvalCount != 0) if (upvalCount != 0)
{ {
if (FFlag::LuauCodegenLoadTypeUpvalCheck) CODEGEN_ASSERT(upvalCount == unsigned(proto->nups));
{
CODEGEN_ASSERT(upvalCount == unsigned(proto->nups));
}
typeInfo.upvalueTypes.resize(upvalCount); typeInfo.upvalueTypes.resize(upvalCount);
@ -775,7 +765,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
regTags[ra] = LBC_TYPE_NUMBER; 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); regTags[ra] = hostHooks.vectorAccessBytecodeType(field, str->len);
} }
else if (isCustomUserdataBytecodeType(bcType.a)) else if (isCustomUserdataBytecodeType(bcType.a))
@ -800,7 +790,7 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
regTags[ra] = LBC_TYPE_NUMBER; 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); 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); TString* str = gco2ts(function.proto->k[kc].value.gc);
const char* field = getstr(str); 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)); knownNextCallResult = LuauBytecodeType(hostHooks.vectorNamecallBytecodeType(field, str->len));
else if (isCustomUserdataBytecodeType(bcType.a) && hostHooks.userdataNamecallBytecodeType) else if (isCustomUserdataBytecodeType(bcType.a) && hostHooks.userdataNamecallBytecodeType)
knownNextCallResult = LuauBytecodeType(hostHooks.userdataNamecallBytecodeType(bcType.a, field, str->len)); knownNextCallResult = LuauBytecodeType(hostHooks.userdataNamecallBytecodeType(bcType.a, field, str->len));
} }
else 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); TString* str = gco2ts(function.proto->k[kc].value.gc);
const char* field = getstr(str); const char* field = getstr(str);
@ -1237,21 +1227,18 @@ void analyzeBytecodeTypes(IrFunction& function, const HostIrHooks& hostHooks)
} }
case LOP_CALL: 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) knownNextCallResult = LBC_TYPE_ANY;
{
bcType.result = knownNextCallResult;
knownNextCallResult = LBC_TYPE_ANY; regTags[ra] = bcType.result;
regTags[ra] = bcType.result;
}
refineRegType(bcTypeInfo, ra, i, bcType.result);
} }
refineRegType(bcTypeInfo, ra, i, bcType.result);
break; break;
} }
case LOP_GETUPVAL: case LOP_GETUPVAL:

View File

@ -12,8 +12,6 @@
#include "lapi.h" #include "lapi.h"
LUAU_FASTFLAGVARIABLE(LuauCodegenCheckNullContext, false)
LUAU_FASTINTVARIABLE(LuauCodeGenBlockSize, 4 * 1024 * 1024) LUAU_FASTINTVARIABLE(LuauCodeGenBlockSize, 4 * 1024 * 1024)
LUAU_FASTINTVARIABLE(LuauCodeGenMaxTotalSize, 256 * 1024 * 1024) LUAU_FASTINTVARIABLE(LuauCodeGenMaxTotalSize, 256 * 1024 * 1024)
LUAU_FASTFLAG(LuauNativeAttribute) 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 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; lua_ExecutionCallbacks* ecb = &L->global->ecb;

View File

@ -12,6 +12,8 @@
#include "lstate.h" #include "lstate.h"
LUAU_FASTFLAG(LuauCodegenMathSign)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -57,6 +59,8 @@ static void emitBuiltinMathModf(IrRegAllocX64& regs, AssemblyBuilderX64& build,
static void emitBuiltinMathSign(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int arg) static void emitBuiltinMathSign(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int arg)
{ {
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
ScopedRegX64 tmp0{regs, SizeX64::xmmword}; ScopedRegX64 tmp0{regs, SizeX64::xmmword};
ScopedRegX64 tmp1{regs, SizeX64::xmmword}; ScopedRegX64 tmp1{regs, SizeX64::xmmword};
ScopedRegX64 tmp2{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); CODEGEN_ASSERT(nresults == 1 || nresults == 2);
return emitBuiltinMathModf(regs, build, ra, arg, nresults); return emitBuiltinMathModf(regs, build, ra, arg, nresults);
case LBF_MATH_SIGN: case LBF_MATH_SIGN:
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
CODEGEN_ASSERT(nresults == 1); CODEGEN_ASSERT(nresults == 1);
return emitBuiltinMathSign(regs, build, ra, arg); return emitBuiltinMathSign(regs, build, ra, arg);
default: default:

View File

@ -14,8 +14,6 @@
#include <utility> #include <utility>
LUAU_FASTFLAGVARIABLE(LuauCodegenSplitDoarith, false)
namespace Luau namespace Luau
{ {
namespace CodeGen namespace CodeGen
@ -158,43 +156,35 @@ void callArithHelper(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, Ope
callWrap.addArgument(SizeX64::qword, b); callWrap.addArgument(SizeX64::qword, b);
callWrap.addArgument(SizeX64::qword, c); callWrap.addArgument(SizeX64::qword, c);
if (FFlag::LuauCodegenSplitDoarith) switch (tm)
{ {
switch (tm) case TM_ADD:
{ callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithadd)]);
case TM_ADD: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithadd)]); case TM_SUB:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithsub)]);
case TM_SUB: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithsub)]); case TM_MUL:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmul)]);
case TM_MUL: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmul)]); case TM_DIV:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithdiv)]);
case TM_DIV: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithdiv)]); case TM_IDIV:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithidiv)]);
case TM_IDIV: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithidiv)]); case TM_MOD:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmod)]);
case TM_MOD: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithmod)]); case TM_POW:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithpow)]);
case TM_POW: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithpow)]); case TM_UNM:
break; callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithunm)]);
case TM_UNM: break;
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarithunm)]); default:
break; CODEGEN_ASSERT(!"Invalid doarith helper operation tag");
default: break;
CODEGEN_ASSERT(!"Invalid doarith helper operation tag");
break;
}
}
else
{
callWrap.addArgument(SizeX64::dword, tm);
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaV_doarith)]);
} }
emitUpdateBase(build); emitUpdateBase(build);

View File

@ -13,7 +13,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps)
LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauLoadUserdataInfo)
LUAU_FASTFLAG(LuauCodegenInstG) LUAU_FASTFLAG(LuauCodegenInstG)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
@ -534,15 +533,8 @@ void IrBuilder::translateInst(LuauOpcode op, const Instruction* pc, int i)
translateInstCapture(*this, pc, i); translateInstCapture(*this, pc, i);
break; break;
case LOP_NAMECALL: case LOP_NAMECALL:
if (FFlag::LuauCodegenAnalyzeHostVectorOps) if (translateInstNamecall(*this, pc, i))
{ cmdSkipTarget = i + 3;
if (translateInstNamecall(*this, pc, i))
cmdSkipTarget = i + 3;
}
else
{
translateInstNamecall(*this, pc, i);
}
break; break;
case LOP_PREPVARARGS: case LOP_PREPVARARGS:
inst(IrCmd::FALLBACK_PREPVARARGS, constUint(i), constInt(LUAU_INSN_A(*pc))); 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"; return "SQRT_NUM";
case IrCmd::ABS_NUM: case IrCmd::ABS_NUM:
return "ABS_NUM"; return "ABS_NUM";
case IrCmd::SIGN_NUM:
return "SIGN_NUM";
case IrCmd::ADD_VEC: case IrCmd::ADD_VEC:
return "ADD_VEC"; return "ADD_VEC";
case IrCmd::SUB_VEC: case IrCmd::SUB_VEC:

View File

@ -11,11 +11,11 @@
#include "lstate.h" #include "lstate.h"
#include "lgc.h" #include "lgc.h"
LUAU_FASTFLAG(LuauCodegenSplitDoarith)
LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataOps)
LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataAlloc, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataAlloc, false)
LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOpsFixA64, false) LUAU_FASTFLAGVARIABLE(LuauCodegenUserdataOpsFixA64, false)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
LUAU_FASTFLAG(LuauCodegenMathSign)
namespace Luau namespace Luau
{ {
@ -240,6 +240,7 @@ static bool emitBuiltin(AssemblyBuilderA64& build, IrFunction& function, IrRegAl
} }
case LBF_MATH_SIGN: case LBF_MATH_SIGN:
{ {
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
CODEGEN_ASSERT(nresults == 1); CODEGEN_ASSERT(nresults == 1);
build.ldr(d0, mem(rBase, arg * sizeof(TValue) + offsetof(TValue, value.n))); build.ldr(d0, mem(rBase, arg * sizeof(TValue) + offsetof(TValue, value.n)));
build.fcmpz(d0); build.fcmpz(d0);
@ -697,6 +698,24 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
build.fabs(inst.regA64, temp); build.fabs(inst.regA64, temp);
break; 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: case IrCmd::ADD_VEC:
{ {
inst.regA64 = regs.allocReuse(KindA64::q, index, {inst.a, inst.b}); 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 else
build.add(x3, rBase, uint16_t(vmRegOp(inst.c) * sizeof(TValue))); 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)));
case TM_ADD: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithadd))); case TM_SUB:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithsub)));
case TM_SUB: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithsub))); case TM_MUL:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmul)));
case TM_MUL: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmul))); case TM_DIV:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithdiv)));
case TM_DIV: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithdiv))); case TM_IDIV:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithidiv)));
case TM_IDIV: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithidiv))); case TM_MOD:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmod)));
case TM_MOD: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithmod))); case TM_POW:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithpow)));
case TM_POW: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithpow))); case TM_UNM:
break; build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithunm)));
case TM_UNM: break;
build.ldr(x4, mem(rNativeContext, offsetof(NativeContext, luaV_doarithunm))); default:
break; CODEGEN_ASSERT(!"Invalid doarith helper operation tag");
default: break;
CODEGEN_ASSERT(!"Invalid doarith helper operation tag"); }
break;
}
build.blr(x4); build.blr(x4);
}
else
{
build.mov(w4, TMS(intOp(inst.d)));
build.ldr(x5, mem(rNativeContext, offsetof(NativeContext, luaV_doarith)));
build.blr(x5);
}
emitUpdateBase(build); emitUpdateBase(build);
break; break;

View File

@ -18,6 +18,7 @@
LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataOps)
LUAU_FASTFLAG(LuauCodegenUserdataAlloc) LUAU_FASTFLAG(LuauCodegenUserdataAlloc)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
LUAU_FASTFLAG(LuauCodegenMathSign)
namespace Luau 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))); build.vandpd(inst.regX64, inst.regX64, build.i64(~(1LL << 63)));
break; 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: case IrCmd::ADD_VEC:
{ {
inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b}); inst.regX64 = regs.allocRegOrReuse(SizeX64::xmmword, index, {inst.a, inst.b});

View File

@ -9,6 +9,7 @@
#include <math.h> #include <math.h>
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
LUAU_FASTFLAGVARIABLE(LuauCodegenMathSign, false)
// TODO: when nresults is less than our actual result count, we can skip computing/writing unused results // 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( static BuiltinImplResult translateBuiltinNumberToNumber(
IrBuilder& build, LuauBuiltinFunction bfid, int nparams, int ra, int arg, IrOp args, int nresults, int pcpos) 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) if (nparams < 1 || nresults > 1)
return {BuiltinImplType::None, -1}; return {BuiltinImplType::None, -1};
@ -845,7 +848,10 @@ BuiltinImplResult translateBuiltin(
case LBF_MATH_LOG10: case LBF_MATH_LOG10:
return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos); return translateBuiltinNumberToNumberLibm(build, LuauBuiltinFunction(bfid), nparams, ra, arg, nresults, pcpos);
case LBF_MATH_SIGN: 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_POW:
case LBF_MATH_FMOD: case LBF_MATH_FMOD:
case LBF_MATH_ATAN2: case LBF_MATH_ATAN2:

View File

@ -13,7 +13,6 @@
#include "lstate.h" #include "lstate.h"
#include "ltm.h" #include "ltm.h"
LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps)
LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataOps)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
@ -1285,8 +1284,7 @@ void translateInstGetTableKS(IrBuilder& build, const Instruction* pc, int pcpos)
} }
else else
{ {
if (FFlag::LuauCodegenAnalyzeHostVectorOps && build.hostHooks.vectorAccess && if (build.hostHooks.vectorAccess && build.hostHooks.vectorAccess(build, field, str->len, ra, rb, pcpos))
build.hostHooks.vectorAccess(build, field, str->len, ra, rb, pcpos))
return; return;
build.inst(IrCmd::FALLBACK_GETTABLEKS, build.constUint(pcpos), build.vmReg(ra), build.vmReg(rb), build.vmConst(aux)); 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)); build.loadAndCheckTag(build.vmReg(rb), LUA_TVECTOR, build.vmExit(pcpos));
if (FFlag::LuauCodegenAnalyzeHostVectorOps && build.hostHooks.vectorNamecall) if (build.hostHooks.vectorNamecall)
{ {
Instruction call = pc[2]; Instruction call = pc[2];
CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL); CODEGEN_ASSERT(LUAU_INSN_OP(call) == LOP_CALL);

View File

@ -69,6 +69,7 @@ IrValueKind getCmdValueKind(IrCmd cmd)
case IrCmd::ROUND_NUM: case IrCmd::ROUND_NUM:
case IrCmd::SQRT_NUM: case IrCmd::SQRT_NUM:
case IrCmd::ABS_NUM: case IrCmd::ABS_NUM:
case IrCmd::SIGN_NUM:
return IrValueKind::Double; return IrValueKind::Double;
case IrCmd::ADD_VEC: case IrCmd::ADD_VEC:
case IrCmd::SUB_VEC: case IrCmd::SUB_VEC:
@ -658,6 +659,14 @@ void foldConstants(IrBuilder& build, IrFunction& function, IrBlock& block, uint3
if (inst.a.kind == IrOpKind::Constant) if (inst.a.kind == IrOpKind::Constant)
substitute(function, inst, build.constDouble(fabs(function.doubleOp(inst.a)))); substitute(function, inst, build.constDouble(fabs(function.doubleOp(inst.a))));
break; 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: case IrCmd::NOT_ANY:
if (inst.a.kind == IrOpKind::Constant) if (inst.a.kind == IrOpKind::Constant)
{ {

View File

@ -29,7 +29,6 @@ void initFunctions(NativeContext& context)
context.luaV_lessthan = luaV_lessthan; context.luaV_lessthan = luaV_lessthan;
context.luaV_lessequal = luaV_lessequal; context.luaV_lessequal = luaV_lessequal;
context.luaV_equalval = luaV_equalval; context.luaV_equalval = luaV_equalval;
context.luaV_doarith = luaV_doarith;
context.luaV_doarithadd = luaV_doarithimpl<TM_ADD>; context.luaV_doarithadd = luaV_doarithimpl<TM_ADD>;
context.luaV_doarithsub = luaV_doarithimpl<TM_SUB>; 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_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_lessequal)(lua_State* L, const TValue* l, const TValue* r) = nullptr;
int (*luaV_equalval)(lua_State* L, const TValue* t1, const TValue* t2) = 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_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_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; 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(LuauCodeGenReuseSlotLimit, 64)
LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64) LUAU_FASTINTVARIABLE(LuauCodeGenReuseUdataTagLimit, 64)
LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false) LUAU_FASTFLAGVARIABLE(DebugLuauAbortingChecks, false)
LUAU_FASTFLAGVARIABLE(LuauCodegenFixSplitStoreConstMismatch, false)
LUAU_FASTFLAG(LuauCodegenUserdataOps) LUAU_FASTFLAG(LuauCodegenUserdataOps)
LUAU_FASTFLAG(LuauCodegenUserdataAlloc) LUAU_FASTFLAG(LuauCodegenUserdataAlloc)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
LUAU_FASTFLAG(LuauCodegenMathSign)
namespace Luau 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 replace(function, block, index, {IrCmd::STORE_SPLIT_TVALUE, inst.a, build.constTag(tag), value, inst.c});
bool canSplitTvalueStore = false;
if (tag == LUA_TBOOLEAN && // Value can be propagated to future loads of the same register
(value.kind == IrOpKind::Inst || (value.kind == IrOpKind::Constant && function.constOp(value).kind == IrConstKind::Int))) if (inst.a.kind == IrOpKind::VmReg && activeLoadValue != kInvalidInstIdx)
canSplitTvalueStore = true; state.valueMap[state.versionedVmRegLoad(activeLoadCmd, inst.a)] = activeLoadValue;
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);
}
} }
else else if (inst.a.kind == IrOpKind::VmReg)
{ {
// If we have constant tag and value, replace TValue store with tag/value pair store state.forwardVmRegStoreToLoad(inst, IrCmd::LOAD_TVALUE);
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);
}
} }
} }
break; break;
@ -1160,6 +1141,7 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER); state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg + 1)}, LUA_TNUMBER);
break; break;
case LBF_MATH_SIGN: case LBF_MATH_SIGN:
CODEGEN_ASSERT(!FFlag::LuauCodegenMathSign);
state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER); state.updateTag(IrOp{IrOpKind::VmReg, uint8_t(firstReturnReg)}, LUA_TNUMBER);
break; break;
default: default:
@ -1225,6 +1207,7 @@ static void constPropInInst(ConstPropState& state, IrBuilder& build, IrFunction&
case IrCmd::ROUND_NUM: case IrCmd::ROUND_NUM:
case IrCmd::SQRT_NUM: case IrCmd::SQRT_NUM:
case IrCmd::ABS_NUM: case IrCmd::ABS_NUM:
case IrCmd::SIGN_NUM:
case IrCmd::NOT_ANY: case IrCmd::NOT_ANY:
state.substituteOrRecord(inst, index); state.substituteOrRecord(inst, index);
break; break;

View File

@ -443,7 +443,6 @@ enum LuauBytecodeTag
LBC_VERSION_MAX = 6, LBC_VERSION_MAX = 6,
LBC_VERSION_TARGET = 5, LBC_VERSION_TARGET = 5,
// Type encoding version // Type encoding version
LBC_TYPE_VERSION_DEPRECATED = 1,
LBC_TYPE_VERSION_MIN = 1, LBC_TYPE_VERSION_MIN = 1,
LBC_TYPE_VERSION_MAX = 3, LBC_TYPE_VERSION_MAX = 3,
LBC_TYPE_VERSION_TARGET = 3, LBC_TYPE_VERSION_TARGET = 3,

View File

@ -44,7 +44,7 @@ struct lua_CompileOptions
const char* const* mutableGlobals; const char* const* mutableGlobals;
// null-terminated array of userdata types that will be included in the type information // 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 // 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 <algorithm>
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauCompileTypeInfo, false)
LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo)
LUAU_FASTFLAG(LuauCompileFastcall3) LUAU_FASTFLAG(LuauCompileFastcall3)
@ -283,11 +282,8 @@ void BytecodeBuilder::endFunction(uint8_t maxstacksize, uint8_t numupvalues, uin
debugLocals.clear(); debugLocals.clear();
debugUpvals.clear(); debugUpvals.clear();
if (FFlag::LuauCompileTypeInfo) typedLocals.clear();
{ typedUpvals.clear();
typedLocals.clear();
typedUpvals.clear();
}
constantMap.clear(); constantMap.clear();
tableShapeMap.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) void BytecodeBuilder::pushLocalTypeInfo(LuauBytecodeType type, uint8_t reg, uint32_t startpc, uint32_t endpc)
{ {
LUAU_ASSERT(FFlag::LuauCompileTypeInfo);
TypedLocal local; TypedLocal local;
local.type = type; local.type = type;
local.reg = reg; local.reg = reg;
@ -572,8 +566,6 @@ void BytecodeBuilder::pushLocalTypeInfo(LuauBytecodeType type, uint8_t reg, uint
void BytecodeBuilder::pushUpvalTypeInfo(LuauBytecodeType type) void BytecodeBuilder::pushUpvalTypeInfo(LuauBytecodeType type)
{ {
LUAU_ASSERT(FFlag::LuauCompileTypeInfo);
TypedUpval upval; TypedUpval upval;
upval.type = type; upval.type = type;
@ -712,7 +704,7 @@ void BytecodeBuilder::finalize()
writeStringTable(bytecode); writeStringTable(bytecode);
if (FFlag::LuauCompileTypeInfo && FFlag::LuauCompileUserdataInfo) if (FFlag::LuauCompileUserdataInfo)
{ {
// Write the mapping between used type name indices and their name // Write the mapping between used type name indices and their name
for (uint32_t i = 0; i < uint32_t(userdataTypes.size()); i++) 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); 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 writeByte(tempTypeInfo, l.type);
tempTypeInfo.clear(); writeByte(tempTypeInfo, l.reg);
writeVarInt(tempTypeInfo, uint32_t(func.typeinfo.size())); writeVarInt(tempTypeInfo, l.startpc);
writeVarInt(tempTypeInfo, uint32_t(typedUpvals.size())); LUAU_ASSERT(l.endpc >= l.startpc);
writeVarInt(tempTypeInfo, uint32_t(typedLocals.size())); writeVarInt(tempTypeInfo, l.endpc - l.startpc);
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);
} }
writeVarInt(ss, uint32_t(tempTypeInfo.size()));
ss.append(tempTypeInfo);
} }
else else
{ {
writeVarInt(ss, uint32_t(func.typeinfo.size())); writeVarInt(ss, 0);
ss.append(func.typeinfo);
} }
// instructions // instructions
@ -1251,10 +1235,10 @@ uint8_t BytecodeBuilder::getVersion()
uint8_t BytecodeBuilder::getTypeEncodingVersion() uint8_t BytecodeBuilder::getTypeEncodingVersion()
{ {
if (FFlag::LuauCompileTypeInfo && FFlag::LuauCompileUserdataInfo) if (FFlag::LuauCompileUserdataInfo)
return LBC_TYPE_VERSION_TARGET; return LBC_TYPE_VERSION_TARGET;
return FFlag::LuauCompileTypeInfo ? 2 : LBC_TYPE_VERSION_DEPRECATED; return 2;
} }
#ifdef LUAU_ASSERTENABLED #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; // Arguments start from third byte in function typeinfo string
for (uint8_t i = 2; i < typeinfo.size(); ++i)
if (FFlag::LuauCompileUserdataInfo)
{ {
// Arguments start from third byte in function typeinfo string uint8_t et = typeinfo[i];
for (uint8_t i = 2; i < typeinfo.size(); ++i)
{
uint8_t et = typeinfo[i];
const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et)); const char* userdata = tryGetUserdataTypeName(LuauBytecodeType(et));
const char* name = userdata ? userdata : getBaseTypeString(et); const char* name = userdata ? userdata : getBaseTypeString(et);
const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : "";
formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional); formatAppend(result, "R%d: %s%s [argument]\n", i - 2, name, optional);
}
for (size_t i = 0; i < typedUpvals.size(); ++i)
{
const TypedUpval& l = typedUpvals[i];
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);
}
} }
else
for (size_t i = 0; i < typedUpvals.size(); ++i)
{ {
// Arguments start from third byte in function typeinfo string const TypedUpval& l = typedUpvals[i];
for (uint8_t i = 2; i < typeinfo.size(); ++i)
{
uint8_t et = typeinfo[i];
const char* base = getBaseTypeString(et); const char* userdata = tryGetUserdataTypeName(l.type);
const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; 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) for (size_t i = 0; i < typedLocals.size(); ++i)
{ {
const TypedUpval& l = typedUpvals[i]; const TypedLocal& l = typedLocals[i];
const char* base = getBaseTypeString(l.type); const char* userdata = tryGetUserdataTypeName(l.type);
const char* optional = (l.type & LBC_TYPE_OPTIONAL_BIT) ? "?" : ""; 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 char* base = getBaseTypeString(et);
{ const char* optional = (et & LBC_TYPE_OPTIONAL_BIT) ? "?" : "";
const TypedLocal& l = typedLocals[i];
const char* base = getBaseTypeString(l.type); formatAppend(result, "R%d: %s%s [argument]\n", i - 2, base, optional);
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); 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(LuauCompileInlineThresholdMaxBoost, 300)
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5) LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
LUAU_FASTFLAG(LuauCompileTypeInfo)
LUAU_FASTFLAGVARIABLE(LuauCompileTempTypeInfo, false)
LUAU_FASTFLAGVARIABLE(LuauCompileUserdataInfo, false) LUAU_FASTFLAGVARIABLE(LuauCompileUserdataInfo, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFastcall3, false) LUAU_FASTFLAGVARIABLE(LuauCompileFastcall3, false)
@ -215,13 +213,6 @@ struct Compiler
setDebugLine(func); 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) if (func->vararg)
bytecode.emitABC(LOP_PREPVARARGS, uint8_t(self + func->args.size), 0, 0); 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) for (size_t i = 0; i < func->args.size; ++i)
pushLocal(func->args.data[i], uint8_t(args + self + i), kDefaultAllocPc); pushLocal(func->args.data[i], uint8_t(args + self + i), kDefaultAllocPc);
if (FFlag::LuauCompileTypeInfo) argCount = localStack.size();
argCount = localStack.size();
AstStatBlock* stat = func->body; AstStatBlock* stat = func->body;
@ -266,7 +256,7 @@ struct Compiler
bytecode.pushDebugUpval(sref(l->name)); bytecode.pushDebugUpval(sref(l->name));
} }
if (FFlag::LuauCompileTypeInfo && options.typeInfoLevel >= 1) if (options.typeInfoLevel >= 1)
{ {
for (AstLocal* l : upvals) for (AstLocal* l : upvals)
{ {
@ -289,12 +279,9 @@ struct Compiler
if (bytecode.getInstructionCount() > kMaxInstructionCount) if (bytecode.getInstructionCount() > kMaxInstructionCount)
CompileError::raise(func->location, "Exceeded function instruction limit; split the function into parts to compile"); 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))
// note: we move types out of typeMap which is safe because compileFunction is only called once per function bytecode.setFunctionTypeInfo(std::move(*funcType));
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 // 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) 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 upvals.clear(); // note: instead of std::move above, we copy & clear to preserve capacity for future pushes
stackSize = 0; stackSize = 0;
if (FFlag::LuauCompileTypeInfo) argCount = 0;
argCount = 0;
hasLoops = false; 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 // 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; unsigned int tail = unsigned(func->args.size - expr->args.size) + 1;
uint8_t reg = allocReg(arg, tail); 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>()) if (AstExprCall* expr = arg->as<AstExprCall>())
compileExprCall(expr, reg, tail, /* targetTop= */ true); compileExprCall(expr, reg, tail, /* targetTop= */ true);
@ -669,12 +655,7 @@ struct Compiler
LUAU_ASSERT(!"Unexpected expression type"); LUAU_ASSERT(!"Unexpected expression type");
for (size_t j = i; j < func->args.size; ++j) for (size_t j = i; j < func->args.size; ++j)
{ args.push_back({func->args.data[j], uint8_t(reg + (j - i)), {Constant::Type_Unknown}, allocpc});
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))});
}
// all remaining function arguments have been allocated and assigned to // all remaining function arguments have been allocated and assigned to
break; 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 // if the argument is mutated, we need to allocate a fresh register even if it's a constant
uint8_t reg = allocReg(arg, 1); uint8_t reg = allocReg(arg, 1);
uint32_t allocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; uint32_t allocpc = bytecode.getDebugPC();
if (arg) if (arg)
compileExprTemp(arg, reg); compileExprTemp(arg, reg);
else else
bytecode.emitABC(LOP_LOADNIL, reg, 0, 0); bytecode.emitABC(LOP_LOADNIL, reg, 0, 0);
if (FFlag::LuauCompileTypeInfo) args.push_back({var, reg, {Constant::Type_Unknown}, allocpc});
args.push_back({var, reg, {Constant::Type_Unknown}, allocpc});
else
args.push_back({var, reg});
} }
else if (arg == nullptr) else if (arg == nullptr)
{ {
@ -718,14 +696,11 @@ struct Compiler
else else
{ {
uint8_t temp = allocReg(arg, 1); uint8_t temp = allocReg(arg, 1);
uint32_t allocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; uint32_t allocpc = bytecode.getDebugPC();
compileExprTemp(arg, temp); compileExprTemp(arg, temp);
if (FFlag::LuauCompileTypeInfo) args.push_back({var, temp, {Constant::Type_Unknown}, allocpc});
args.push_back({var, temp, {Constant::Type_Unknown}, allocpc});
else
args.push_back({var, temp});
} }
} }
} }
@ -739,16 +714,9 @@ struct Compiler
for (InlineArg& arg : args) for (InlineArg& arg : args)
{ {
if (arg.value.type == Constant::Type_Unknown) if (arg.value.type == Constant::Type_Unknown)
{ pushLocal(arg.local, arg.reg, arg.allocpc);
if (FFlag::LuauCompileTypeInfo)
pushLocal(arg.local, arg.reg, arg.allocpc);
else
pushLocal(arg.local, arg.reg, kDefaultAllocPc);
}
else else
{
locstants[arg.local] = arg.value; locstants[arg.local] = arg.value;
}
} }
// the inline frame will be used to compile return statements as well as to reject recursive inlining attempts // 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.emitABC(LOP_NAMECALL, regs, selfreg, uint8_t(BytecodeBuilder::getStringHash(iname)));
bytecode.emitAux(cid); 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) else if (bfid >= 0)
{ {
@ -1627,8 +1594,7 @@ struct Compiler
bytecode.emitABC(getBinaryOpArith(expr->op, /* k= */ true), target, rl, uint8_t(rc)); 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 else
{ {
@ -1643,8 +1609,7 @@ struct Compiler
bytecode.emitABC(op, target, uint8_t(lc), uint8_t(rr)); 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; return;
} }
} }
@ -1654,11 +1619,8 @@ struct Compiler
bytecode.emitABC(getBinaryOpArith(expr->op), target, rl, rr); 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; break;
@ -2099,8 +2061,7 @@ struct Compiler
bytecode.emitABC(LOP_GETTABLEKS, target, reg, uint8_t(BytecodeBuilder::getStringHash(iname))); bytecode.emitABC(LOP_GETTABLEKS, target, reg, uint8_t(BytecodeBuilder::getStringHash(iname)));
bytecode.emitAux(cid); 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) 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 // 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)); 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); 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 // this makes sure the code inside the loop can't interfere with the iteration process (other than modifying the table we're iterating
// through) // through)
uint8_t varreg = regs + 2; 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) if (Variable* il = variables.find(stat->var); il && il->written)
varreg = allocReg(stat, 1); 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 // 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)); uint8_t vars = allocReg(stat, std::max(unsigned(stat->vars.size), 2u));
LUAU_ASSERT(vars == regs + 3); LUAU_ASSERT(vars == regs + 3);
uint32_t varsallocpc = FFlag::LuauCompileTypeInfo ? bytecode.getDebugPC() : kDefaultAllocPc; uint32_t varsallocpc = bytecode.getDebugPC();
LuauOpcode skipOp = LOP_FORGPREP; LuauOpcode skipOp = LOP_FORGPREP;
@ -3480,13 +3441,10 @@ struct Compiler
bytecode.emitABC(getBinaryOpArith(stat->op), target, target, rr); 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; break;
@ -3720,9 +3678,7 @@ struct Compiler
l.reg = reg; l.reg = reg;
l.allocated = true; l.allocated = true;
l.debugpc = bytecode.getDebugPC(); l.debugpc = bytecode.getDebugPC();
l.allocpc = allocpc == kDefaultAllocPc ? l.debugpc : allocpc;
if (FFlag::LuauCompileTypeInfo)
l.allocpc = allocpc == kDefaultAllocPc ? l.debugpc : allocpc;
} }
bool areLocalsCaptured(size_t start) bool areLocalsCaptured(size_t start)
@ -3785,7 +3741,7 @@ struct Compiler
bytecode.pushDebugLocal(sref(localStack[i]->name), l->reg, l->debugpc, debugpc); 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(); uint32_t debugpc = bytecode.getDebugPC();
LuauBytecodeType ty = LBC_TYPE_ANY; LuauBytecodeType ty = LBC_TYPE_ANY;
@ -3873,8 +3829,6 @@ struct Compiler
void hintTemporaryRegType(AstExpr* expr, int reg, LuauBytecodeType expectedType, int instLength) 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 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)) if (LuauBytecodeType* ty = exprTypes.find(expr))
{ {
@ -3885,8 +3839,6 @@ struct Compiler
void hintTemporaryExprRegType(AstExpr* expr, int reg, LuauBytecodeType expectedType, int instLength) 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 we allocated a temporary register for the operation argument, try hinting its type
if (!getExprLocal(expr)) if (!getExprLocal(expr))
hintTemporaryRegType(expr, reg, expectedType, instLength); hintTemporaryRegType(expr, reg, expectedType, instLength);
@ -4175,9 +4127,7 @@ struct Compiler
static void setCompileOptionsForNativeCompilation(CompileOptions& options) static void setCompileOptionsForNativeCompilation(CompileOptions& options)
{ {
options.optimizationLevel = 2; // note: this might be removed in the future in favor of --!optimize options.optimizationLevel = 2; // note: this might be removed in the future in favor of --!optimize
options.typeInfoLevel = 1;
if (FFlag::LuauCompileTypeInfo)
options.typeInfoLevel = 1;
} }
void compileOrThrow(BytecodeBuilder& bytecode, const ParseResult& parseResult, const AstNameTable& names, const CompileOptions& inputOptions) 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 // 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,
if (options.typeInfoLevel >= 1) compiler.builtinTypes, compiler.builtins, compiler.globals, bytecode);
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);
}
for (AstExprFunction* expr : functions) for (AstExprFunction* expr : functions)
{ {

View File

@ -3,8 +3,6 @@
#include "Luau/BytecodeBuilder.h" #include "Luau/BytecodeBuilder.h"
LUAU_FASTFLAG(LuauCompileTypeInfo)
LUAU_FASTFLAG(LuauCompileTempTypeInfo)
LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo)
namespace Luau 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) static bool isMatchingGlobal(const DenseHashMap<AstName, Compile::Global>& globals, AstExpr* node, const char* name)
{ {
LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo);
if (AstExprGlobal* expr = node->as<AstExprGlobal>()) if (AstExprGlobal* expr = node->as<AstExprGlobal>())
return Compile::getGlobalState(globals, expr->name) == Compile::Global::Default && expr->name == name; 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) const AstType* resolveAliases(const AstType* ty)
{ {
LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo);
if (const AstTypeReference* ref = ty->as<AstTypeReference>()) if (const AstTypeReference* ref = ty->as<AstTypeReference>())
{ {
if (ref->prefix) if (ref->prefix)
@ -249,8 +243,6 @@ struct TypeMapVisitor : AstVisitor
const AstTableIndexer* tryGetTableIndexer(AstExpr* expr) const AstTableIndexer* tryGetTableIndexer(AstExpr* expr)
{ {
LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo);
if (const AstType** typePtr = resolvedExprs.find(expr)) if (const AstType** typePtr = resolvedExprs.find(expr))
{ {
if (const AstTypeTable* tableTy = (*typePtr)->as<AstTypeTable>()) if (const AstTypeTable* tableTy = (*typePtr)->as<AstTypeTable>())
@ -262,8 +254,6 @@ struct TypeMapVisitor : AstVisitor
LuauBytecodeType recordResolvedType(AstExpr* expr, const AstType* ty) LuauBytecodeType recordResolvedType(AstExpr* expr, const AstType* ty)
{ {
LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo);
ty = resolveAliases(ty); ty = resolveAliases(ty);
resolvedExprs[expr] = ty; resolvedExprs[expr] = ty;
@ -275,8 +265,6 @@ struct TypeMapVisitor : AstVisitor
LuauBytecodeType recordResolvedType(AstLocal* local, const AstType* ty) LuauBytecodeType recordResolvedType(AstLocal* local, const AstType* ty)
{ {
LUAU_ASSERT(FFlag::LuauCompileTempTypeInfo);
ty = resolveAliases(ty); ty = resolveAliases(ty);
resolvedLocals[local] = 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) // for...in statement can contain type annotations on locals (we might even infer some for ipairs/pairs/generalized iteration)
bool visit(AstStatForIn* node) override bool visit(AstStatForIn* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
for (AstExpr* expr : node->values) for (AstExpr* expr : node->values)
expr->visit(this); expr->visit(this);
@ -382,51 +367,25 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprLocal* node) override bool visit(AstExprLocal* node) override
{ {
if (FFlag::LuauCompileTempTypeInfo) AstLocal* local = node->local;
if (AstType* annotation = local->annotation)
{ {
if (FFlag::LuauCompileTypeInfo) LuauBytecodeType ty = recordResolvedType(node, annotation);
{
AstLocal* local = node->local;
if (AstType* annotation = local->annotation) if (ty != LBC_TYPE_ANY)
{ localTypes[local] = ty;
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;
} }
else else if (const AstType** typePtr = resolvedLocals.find(local))
{ {
if (FFlag::LuauCompileTypeInfo) localTypes[local] = recordResolvedType(node, *typePtr);
{
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;
} }
return false;
} }
bool visit(AstStatLocal* node) override bool visit(AstStatLocal* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
for (AstExpr* expr : node->values) for (AstExpr* expr : node->values)
expr->visit(this); expr->visit(this);
@ -451,9 +410,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprIndexExpr* node) override bool visit(AstExprIndexExpr* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->expr->visit(this); node->expr->visit(this);
node->index->visit(this); node->index->visit(this);
@ -465,9 +421,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprIndexName* node) override bool visit(AstExprIndexName* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->expr->visit(this); node->expr->visit(this);
if (const AstType** typePtr = resolvedExprs.find(node->expr)) if (const AstType** typePtr = resolvedExprs.find(node->expr))
@ -499,9 +452,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprUnary* node) override bool visit(AstExprUnary* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->expr->visit(this); node->expr->visit(this);
switch (node->op) switch (node->op)
@ -534,9 +484,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprBinary* node) override bool visit(AstExprBinary* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->left->visit(this); node->left->visit(this);
node->right->visit(this); node->right->visit(this);
@ -575,9 +522,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprGroup* node) override bool visit(AstExprGroup* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->expr->visit(this); node->expr->visit(this);
if (const AstType** typePtr = resolvedExprs.find(node->expr)) if (const AstType** typePtr = resolvedExprs.find(node->expr))
@ -588,9 +532,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprTypeAssertion* node) override bool visit(AstExprTypeAssertion* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->expr->visit(this); node->expr->visit(this);
recordResolvedType(node, node->annotation); recordResolvedType(node, node->annotation);
@ -600,9 +541,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprConstantBool* node) override bool visit(AstExprConstantBool* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
recordResolvedType(node, &builtinTypes.booleanType); recordResolvedType(node, &builtinTypes.booleanType);
return false; return false;
@ -610,9 +548,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprConstantNumber* node) override bool visit(AstExprConstantNumber* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
recordResolvedType(node, &builtinTypes.numberType); recordResolvedType(node, &builtinTypes.numberType);
return false; return false;
@ -620,9 +555,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprConstantString* node) override bool visit(AstExprConstantString* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
recordResolvedType(node, &builtinTypes.stringType); recordResolvedType(node, &builtinTypes.stringType);
return false; return false;
@ -630,9 +562,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprInterpString* node) override bool visit(AstExprInterpString* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
recordResolvedType(node, &builtinTypes.stringType); recordResolvedType(node, &builtinTypes.stringType);
return false; return false;
@ -640,9 +569,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprIfElse* node) override bool visit(AstExprIfElse* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
node->condition->visit(this); node->condition->visit(this);
node->trueExpr->visit(this); node->trueExpr->visit(this);
node->falseExpr->visit(this); node->falseExpr->visit(this);
@ -660,9 +586,6 @@ struct TypeMapVisitor : AstVisitor
bool visit(AstExprCall* node) override bool visit(AstExprCall* node) override
{ {
if (!FFlag::LuauCompileTempTypeInfo)
return true;
if (const int* bfid = builtinCalls.find(node)) if (const int* bfid = builtinCalls.find(node))
{ {
switch (LuauBuiltinFunction(*bfid)) 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_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_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 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> template<TMS op>
void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc); void luaV_doarithimpl(lua_State* L, StkId ra, const TValue* rb, const TValue* rc);

View File

@ -16,8 +16,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauVmSplitDoarith, false)
// Disable c99-designator to avoid the warning in CGOTO dispatch table // Disable c99-designator to avoid the warning in CGOTO dispatch table
#ifdef __clang__ #ifdef __clang__
#if __has_warning("-Wc99-designator") #if __has_warning("-Wc99-designator")
@ -1489,14 +1487,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_ADD));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1542,14 +1533,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_SUB));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1610,14 +1594,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MUL));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1678,14 +1655,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_DIV));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1733,14 +1703,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_IDIV));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1764,14 +1727,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_MOD));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1792,14 +1748,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rc, TM_POW));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1820,14 +1769,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_ADD>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_ADD));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1848,14 +1790,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_SUB));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1900,14 +1835,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_MUL>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MUL));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -1953,14 +1881,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_DIV));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -2007,14 +1928,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_IDIV>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_IDIV));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -2038,14 +1952,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_MOD>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_MOD));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -2072,14 +1979,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, kv));
{
VM_PROTECT(luaV_doarithimpl<TM_POW>(L, ra, rb, kv));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, kv, TM_POW));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -2192,14 +2092,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_UNM>(L, ra, rb, rb));
{
VM_PROTECT(luaV_doarithimpl<TM_UNM>(L, ra, rb, rb));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, rb, rb, TM_UNM));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -2812,14 +2705,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, kv, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_SUB>(L, ra, kv, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_SUB));
}
VM_NEXT(); VM_NEXT();
} }
} }
@ -2847,14 +2733,7 @@ reentry:
else else
{ {
// slow-path, may invoke C/Lua via metamethods // slow-path, may invoke C/Lua via metamethods
if (FFlag::LuauVmSplitDoarith) VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, kv, rc));
{
VM_PROTECT(luaV_doarithimpl<TM_DIV>(L, ra, kv, rc));
}
else
{
VM_PROTECT(luaV_doarith(L, ra, kv, rc, TM_DIV));
}
VM_NEXT(); 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_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); 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) void luaV_dolen(lua_State* L, StkId ra, const TValue* rb)
{ {
const TValue* tm = NULL; const TValue* tm = NULL;

View File

@ -9,6 +9,8 @@
#include <math.h> #include <math.h>
#include <ostream> #include <ostream>
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
using namespace Luau; using namespace Luau;
struct JsonEncoderFixture struct JsonEncoderFixture
@ -408,16 +410,32 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatTypeAlias")
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction") TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareFunction")
{ {
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
AstStat* statement = expectParseStatement("declare function foo(x: number): string"); AstStat* statement = expectParseStatement("declare function foo(x: number): string");
std::string_view expected = 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); CHECK(toJson(statement) == expected);
} }
TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass") TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
{ {
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
AstStatBlock* root = expectParse(R"( AstStatBlock* root = expectParse(R"(
declare class Foo declare class Foo
prop: number prop: number
@ -432,11 +450,11 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstStatDeclareClass")
REQUIRE(2 == root->body.size); REQUIRE(2 == root->body.size);
std::string_view expected1 = 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); CHECK(toJson(root->body.data[0]) == expected1);
std::string_view expected2 = 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); CHECK(toJson(root->body.data[1]) == expected2);
} }

View File

@ -35,6 +35,7 @@ struct ACFixtureImpl : BaseType
{ {
FrontendOptions opts; FrontendOptions opts;
opts.forAutocomplete = true; opts.forAutocomplete = true;
opts.retainFullTypeGraphs = true;
this->frontend.check("MainModule", opts); this->frontend.check("MainModule", opts);
return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback); return Luau::autocomplete(this->frontend, "MainModule", Position{row, column}, nullCallback);
@ -44,6 +45,7 @@ struct ACFixtureImpl : BaseType
{ {
FrontendOptions opts; FrontendOptions opts;
opts.forAutocomplete = true; opts.forAutocomplete = true;
opts.retainFullTypeGraphs = true;
this->frontend.check("MainModule", opts); this->frontend.check("MainModule", opts);
return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback); return Luau::autocomplete(this->frontend, "MainModule", getPosition(marker), callback);
@ -53,6 +55,7 @@ struct ACFixtureImpl : BaseType
{ {
FrontendOptions opts; FrontendOptions opts;
opts.forAutocomplete = true; opts.forAutocomplete = true;
opts.retainFullTypeGraphs = true;
this->frontend.check(name, opts); this->frontend.check(name, opts);
return Luau::autocomplete(this->frontend, name, pos, callback); return Luau::autocomplete(this->frontend, name, pos, callback);
@ -3681,6 +3684,8 @@ a.@1
auto ac = autocomplete('1'); auto ac = autocomplete('1');
CHECK(2 == ac.entryMap.size());
CHECK(ac.entryMap.count("x")); CHECK(ac.entryMap.count("x"));
CHECK(ac.entryMap.count("y")); 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 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); REQUIRE(require);
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); Luau::unfreeze(globals.globalTypes);
attachTag(require->typeId, "RequireCall"); attachTag(require->typeId, "RequireCall");
Luau::freeze(frontend.globalsForAutocomplete.globalTypes); Luau::freeze(globals.globalTypes);
check(R"( check(R"(
local x = require("testing/@1") local x = require("testing/@1")
@ -3837,11 +3844,13 @@ TEST_CASE_FIXTURE(ACFixture, "string_completion_outside_quotes")
declare function require(path: string): any 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); REQUIRE(require);
Luau::unfreeze(frontend.globalsForAutocomplete.globalTypes); Luau::unfreeze(globals.globalTypes);
attachTag(require->typeId, "RequireCall"); attachTag(require->typeId, "RequireCall");
Luau::freeze(frontend.globalsForAutocomplete.globalTypes); Luau::freeze(globals.globalTypes);
check(R"( check(R"(
local x = require(@1"@2"@3) local x = require(@1"@2"@3)

View File

@ -22,8 +22,6 @@ LUAU_FASTINT(LuauCompileLoopUnrollThreshold)
LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost) LUAU_FASTINT(LuauCompileLoopUnrollThresholdMaxBoost)
LUAU_FASTINT(LuauRecursionLimit) LUAU_FASTINT(LuauRecursionLimit)
LUAU_FASTFLAG(LuauCompileTypeInfo)
LUAU_FASTFLAG(LuauCompileTempTypeInfo)
LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo)
LUAU_FASTFLAG(LuauCompileFastcall3) LUAU_FASTFLAG(LuauCompileFastcall3)
@ -3226,8 +3224,6 @@ RETURN R0 0
TEST_CASE("DebugTypes") TEST_CASE("DebugTypes")
{ {
ScopedFastFlag luauCompileTypeInfo{FFlag::LuauCompileTypeInfo, true};
ScopedFastFlag luauCompileTempTypeInfo{FFlag::LuauCompileTempTypeInfo, true};
ScopedFastFlag luauCompileUserdataInfo{FFlag::LuauCompileUserdataInfo, true}; ScopedFastFlag luauCompileUserdataInfo{FFlag::LuauCompileUserdataInfo, true};
const char* source = R"( const char* source = R"(

View File

@ -33,7 +33,6 @@ void luaC_validate(lua_State* L);
LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTINT(CodegenHeuristicsInstructionLimit) LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
LUAU_FASTFLAG(LuauCodegenFixSplitStoreConstMismatch)
LUAU_FASTFLAG(LuauAttributeSyntax) LUAU_FASTFLAG(LuauAttributeSyntax)
LUAU_FASTFLAG(LuauNativeAttribute) LUAU_FASTFLAG(LuauNativeAttribute)
@ -2358,8 +2357,6 @@ TEST_CASE("Native")
if (!codegen || !luau_codegen_supported()) if (!codegen || !luau_codegen_supported())
return; return;
ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true};
SUBCASE("Checked") SUBCASE("Checked")
{ {
FFlag::DebugLuauAbortingChecks.value = true; 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); 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(); TEST_SUITE_END();

View File

@ -7,6 +7,7 @@
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
#include "Luau/Error.h" #include "Luau/Error.h"
#include "Fixture.h"
#include "ScopedFlags.h" #include "ScopedFlags.h"
#include "doctest.h" #include "doctest.h"
@ -172,4 +173,78 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "functions_containing_cyclic_tables_can
CHECK(generalizedTypes->contains(builtinTypes.numberType)); 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(); TEST_SUITE_END();

View File

@ -13,9 +13,9 @@
#include <limits.h> #include <limits.h>
LUAU_FASTFLAG(DebugLuauAbortingChecks) LUAU_FASTFLAG(DebugLuauAbortingChecks)
LUAU_FASTFLAG(LuauCodegenFixSplitStoreConstMismatch)
LUAU_FASTFLAG(LuauCodegenInstG) LUAU_FASTFLAG(LuauCodegenInstG)
LUAU_FASTFLAG(LuauCodegenFastcall3) LUAU_FASTFLAG(LuauCodegenFastcall3)
LUAU_FASTFLAG(LuauCodegenMathSign)
using namespace Luau::CodeGen; using namespace Luau::CodeGen;
@ -335,6 +335,8 @@ TEST_SUITE_BEGIN("ConstantFolding");
TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric") TEST_CASE_FIXTURE(IrBuilderFixture, "Numeric")
{ {
ScopedFastFlag luauCodegenMathSign{FFlag::LuauCodegenMathSign, true};
IrOp block = build.block(IrBlockKind::Internal); IrOp block = build.block(IrBlockKind::Internal);
build.beginBlock(block); 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(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_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)); build.inst(IrCmd::RETURN, build.constUint(0));
updateUseCounts(build.function); updateUseCounts(build.function);
@ -393,6 +397,7 @@ bb_0:
STORE_INT R19, 0i STORE_INT R19, 0i
STORE_INT R20, 1i STORE_INT R20, 1i
STORE_INT R21, 0i STORE_INT R21, 0i
STORE_DOUBLE R22, -1
RETURN 0u RETURN 0u
)"); )");
@ -2662,8 +2667,6 @@ bb_0:
TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1") TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore1")
{ {
ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true};
IrOp entry = build.block(IrBlockKind::Internal); IrOp entry = build.block(IrBlockKind::Internal);
build.beginBlock(entry); build.beginBlock(entry);
@ -2690,8 +2693,6 @@ bb_0:
TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore2") TEST_CASE_FIXTURE(IrBuilderFixture, "DoNotProduceInvalidSplitStore2")
{ {
ScopedFastFlag luauCodegenFixSplitStoreConstMismatch{FFlag::LuauCodegenFixSplitStoreConstMismatch, true};
IrOp entry = build.block(IrBlockKind::Internal); IrOp entry = build.block(IrBlockKind::Internal);
build.beginBlock(entry); build.beginBlock(entry);

View File

@ -15,9 +15,6 @@
#include <memory> #include <memory>
#include <string_view> #include <string_view>
LUAU_FASTFLAG(LuauCompileTypeInfo)
LUAU_FASTFLAG(LuauCompileTempTypeInfo)
LUAU_FASTFLAG(LuauCodegenAnalyzeHostVectorOps)
LUAU_FASTFLAG(LuauCompileUserdataInfo) LUAU_FASTFLAG(LuauCompileUserdataInfo)
LUAU_FASTFLAG(LuauLoadUserdataInfo) LUAU_FASTFLAG(LuauLoadUserdataInfo)
LUAU_FASTFLAG(LuauCodegenUserdataOps) 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") TEST_CASE("VectorConstantTag")
{ {
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
@ -539,8 +515,6 @@ bb_6:
TEST_CASE("VectorCustomAccess") TEST_CASE("VectorCustomAccess")
{ {
ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3magn(a: vector) local function vec3magn(a: vector)
return a.Magnitude * 2 return a.Magnitude * 2
@ -573,8 +547,6 @@ bb_bytecode_1:
TEST_CASE("VectorCustomNamecall") TEST_CASE("VectorCustomNamecall")
{ {
ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function vec3dot(a: vector, b: vector) local function vec3dot(a: vector, b: vector)
return (a:Dot(b)) return (a:Dot(b))
@ -611,8 +583,6 @@ bb_bytecode_1:
TEST_CASE("VectorCustomAccessChain") TEST_CASE("VectorCustomAccessChain")
{ {
ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: vector, b: vector) local function foo(a: vector, b: vector)
return a.Unit * b.Magnitude return a.Unit * b.Magnitude
@ -663,8 +633,6 @@ bb_bytecode_1:
TEST_CASE("VectorCustomNamecallChain") TEST_CASE("VectorCustomNamecallChain")
{ {
ScopedFastFlag luauCodegenAnalyzeHostVectorOps{FFlag::LuauCodegenAnalyzeHostVectorOps, true};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(n: vector, b: vector, t: vector) local function foo(n: vector, b: vector, t: vector)
return n:Cross(t):Dot(b) + 1 return n:Cross(t):Dot(b) + 1
@ -722,8 +690,6 @@ bb_bytecode_1:
TEST_CASE("VectorCustomNamecallChain2") TEST_CASE("VectorCustomNamecallChain2")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
type Vertex = {n: vector, b: vector} type Vertex = {n: vector, b: vector}
@ -890,8 +856,6 @@ bb_4:
TEST_CASE("ExplicitUpvalueAndLocalTypes") TEST_CASE("ExplicitUpvalueAndLocalTypes")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local y: vector = ... local y: vector = ...
@ -933,7 +897,7 @@ bb_bytecode_0:
TEST_CASE("FastcallTypeInferThroughLocal") 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"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function getsum(x, c) local function getsum(x, c)
@ -981,7 +945,7 @@ bb_bytecode_1:
TEST_CASE("FastcallTypeInferThroughUpvalue") 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"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local v = ... local v = ...
@ -1038,8 +1002,6 @@ bb_bytecode_1:
TEST_CASE("LoadAndMoveTypePropagation") TEST_CASE("LoadAndMoveTypePropagation")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function getsum(n) local function getsum(n)
local seqsum = 0 local seqsum = 0
@ -1105,7 +1067,7 @@ bb_bytecode_4:
TEST_CASE("ArgumentTypeRefinement") 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"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function getsum(x, y) local function getsum(x, y)
@ -1141,8 +1103,6 @@ bb_bytecode_0:
TEST_CASE("InlineFunctionType") TEST_CASE("InlineFunctionType")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function inl(v: vector, s: number) local function inl(v: vector, s: number)
return v.Y * s return v.Y * s
@ -1189,8 +1149,6 @@ bb_bytecode_0:
TEST_CASE("ResolveTablePathTypes") TEST_CASE("ResolveTablePathTypes")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
type Vertex = {pos: vector, normal: vector} type Vertex = {pos: vector, normal: vector}
@ -1243,8 +1201,6 @@ bb_6:
TEST_CASE("ResolvableSimpleMath") TEST_CASE("ResolvableSimpleMath")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number } type Vertex = { p: vector, uv: vector, n: vector, t: vector, b: vector, h: number }
local mesh: { vertices: {Vertex}, indices: {number} } = ... local mesh: { vertices: {Vertex}, indices: {number} } = ...
@ -1299,8 +1255,6 @@ end
TEST_CASE("ResolveVectorNamecalls") TEST_CASE("ResolveVectorNamecalls")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
type Vertex = {pos: vector, normal: vector} type Vertex = {pos: vector, normal: vector}
@ -1363,8 +1317,6 @@ bb_6:
TEST_CASE("ImmediateTypeAnnotationHelp") TEST_CASE("ImmediateTypeAnnotationHelp")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(arr, i) local function foo(arr, i)
return (arr[i] :: vector) / 5 return (arr[i] :: vector) / 5
@ -1401,8 +1353,7 @@ bb_2:
TEST_CASE("UnaryTypeResolve") TEST_CASE("UnaryTypeResolve")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileFastcall3, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileFastcall3, true}, {FFlag::LuauCodegenFastcall3, true}};
{FFlag::LuauCodegenFastcall3, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
local function foo(a, b: vector, c) local function foo(a, b: vector, c)
@ -1424,8 +1375,6 @@ end
TEST_CASE("ForInManualAnnotation") TEST_CASE("ForInManualAnnotation")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
type Vertex = {pos: vector, normal: vector} type Vertex = {pos: vector, normal: vector}
@ -1519,8 +1468,6 @@ bb_12:
TEST_CASE("ForInAutoAnnotationIpairs") TEST_CASE("ForInAutoAnnotationIpairs")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
type Vertex = {pos: vector, normal: vector} type Vertex = {pos: vector, normal: vector}
@ -1546,8 +1493,6 @@ end
TEST_CASE("ForInAutoAnnotationPairs") TEST_CASE("ForInAutoAnnotationPairs")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
type Vertex = {pos: vector, normal: vector} type Vertex = {pos: vector, normal: vector}
@ -1573,8 +1518,6 @@ end
TEST_CASE("ForInAutoAnnotationGeneric") TEST_CASE("ForInAutoAnnotationGeneric")
{ {
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
type Vertex = {pos: vector, normal: vector} type Vertex = {pos: vector, normal: vector}
@ -1605,8 +1548,7 @@ TEST_CASE("CustomUserdataTypesTemp")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, false}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, false}, {FFlag::LuauLoadUserdataInfo, true}};
{FFlag::LuauLoadUserdataInfo, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
local function foo(v: vec2, x: mat3) local function foo(v: vec2, x: mat3)
@ -1626,8 +1568,7 @@ TEST_CASE("CustomUserdataTypes")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}};
{FFlag::LuauLoadUserdataInfo, true}};
CHECK_EQ("\n" + getCodegenHeader(R"( CHECK_EQ("\n" + getCodegenHeader(R"(
local function foo(v: vec2, x: mat3) local function foo(v: vec2, x: mat3)
@ -1647,8 +1588,7 @@ TEST_CASE("CustomUserdataPropertyAccess")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(v: vec2) local function foo(v: vec2)
@ -1683,8 +1623,7 @@ TEST_CASE("CustomUserdataPropertyAccess2")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: mat3) local function foo(a: mat3)
@ -1721,8 +1660,7 @@ TEST_CASE("CustomUserdataNamecall1")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, {FFlag::LuauCodegenUserdataOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: vec2, b: vec2) local function foo(a: vec2, b: vec2)
@ -1768,8 +1706,7 @@ TEST_CASE("CustomUserdataNamecall2")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true},
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenAnalyzeHostVectorOps, true}, {FFlag::LuauCodegenUserdataOps, true},
{FFlag::LuauCodegenUserdataAlloc, true}}; {FFlag::LuauCodegenUserdataAlloc, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
@ -1819,8 +1756,7 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: mat3, b: mat3) local function foo(a: mat3, b: mat3)
@ -1852,8 +1788,7 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow2")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: mat3) local function foo(a: mat3)
@ -1883,8 +1818,7 @@ TEST_CASE("CustomUserdataMetamethodDirectFlow3")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: sequence) local function foo(a: sequence)
@ -1914,8 +1848,8 @@ TEST_CASE("CustomUserdataMetamethod")
if (!Luau::CodeGen::isSupported()) if (!Luau::CodeGen::isSupported())
return; return;
ScopedFastFlag sffs[]{{FFlag::LuauCompileTypeInfo, true}, {FFlag::LuauCompileTempTypeInfo, true}, {FFlag::LuauCompileUserdataInfo, true}, ScopedFastFlag sffs[]{{FFlag::LuauCompileUserdataInfo, true}, {FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true},
{FFlag::LuauLoadUserdataInfo, true}, {FFlag::LuauCodegenUserdataOps, true}, {FFlag::LuauCodegenUserdataAlloc, true}}; {FFlag::LuauCodegenUserdataAlloc, true}};
CHECK_EQ("\n" + getCodegenAssembly(R"( CHECK_EQ("\n" + getCodegenAssembly(R"(
local function foo(a: vec2, b: vec2, c: vec2) local function foo(a: vec2, b: vec2, c: vec2)

View File

@ -19,6 +19,7 @@ LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAG(LuauAttributeSyntax); LUAU_FASTFLAG(LuauAttributeSyntax);
LUAU_FASTFLAG(LuauLeadingBarAndAmpersand2); LUAU_FASTFLAG(LuauLeadingBarAndAmpersand2);
LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr); LUAU_FASTFLAG(LuauAttributeSyntaxFunExpr);
LUAU_FASTFLAG(LuauDeclarationExtraPropData);
namespace namespace
{ {
@ -1858,6 +1859,8 @@ function func():end
TEST_CASE_FIXTURE(Fixture, "parse_declarations") TEST_CASE_FIXTURE(Fixture, "parse_declarations")
{ {
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
AstStatBlock* stat = parseEx(R"( AstStatBlock* stat = parseEx(R"(
declare foo: number declare foo: number
declare function bar(x: number): string declare function bar(x: number): string
@ -1871,18 +1874,23 @@ TEST_CASE_FIXTURE(Fixture, "parse_declarations")
AstStatDeclareGlobal* global = stat->body.data[0]->as<AstStatDeclareGlobal>(); AstStatDeclareGlobal* global = stat->body.data[0]->as<AstStatDeclareGlobal>();
REQUIRE(global); REQUIRE(global);
CHECK(global->name == "foo"); CHECK(global->name == "foo");
CHECK(global->nameLocation == Location({1, 16}, {1, 19}));
CHECK(global->type); CHECK(global->type);
AstStatDeclareFunction* func = stat->body.data[1]->as<AstStatDeclareFunction>(); AstStatDeclareFunction* func = stat->body.data[1]->as<AstStatDeclareFunction>();
REQUIRE(func); REQUIRE(func);
CHECK(func->name == "bar"); CHECK(func->name == "bar");
CHECK(func->nameLocation == Location({2, 25}, {2, 28}));
REQUIRE_EQ(func->params.types.size, 1); REQUIRE_EQ(func->params.types.size, 1);
REQUIRE_EQ(func->retTypes.types.size, 1); REQUIRE_EQ(func->retTypes.types.size, 1);
AstStatDeclareFunction* varFunc = stat->body.data[2]->as<AstStatDeclareFunction>(); AstStatDeclareFunction* varFunc = stat->body.data[2]->as<AstStatDeclareFunction>();
REQUIRE(varFunc); REQUIRE(varFunc);
CHECK(varFunc->name == "var"); CHECK(varFunc->name == "var");
CHECK(varFunc->nameLocation == Location({3, 25}, {3, 28}));
CHECK(varFunc->params.tailType); 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 function foo(x)", "All declaration parameters must be annotated");
matchParseError("declare foo", "Expected ':' when parsing global variable declaration, got <eof>"); 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") TEST_CASE_FIXTURE(Fixture, "parse_class_declarations")
{ {
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
AstStatBlock* stat = parseEx(R"( AstStatBlock* stat = parseEx(R"(
declare class Foo declare class Foo
prop: number prop: number
@ -1913,11 +1923,16 @@ TEST_CASE_FIXTURE(Fixture, "parse_class_declarations")
AstDeclaredClassProp& prop = declaredClass->props.data[0]; AstDeclaredClassProp& prop = declaredClass->props.data[0];
CHECK(prop.name == "prop"); CHECK(prop.name == "prop");
CHECK(prop.nameLocation == Location({2, 12}, {2, 16}));
CHECK(prop.ty->is<AstTypeReference>()); CHECK(prop.ty->is<AstTypeReference>());
CHECK(prop.location == Location({2, 12}, {2, 24}));
AstDeclaredClassProp& method = declaredClass->props.data[1]; AstDeclaredClassProp& method = declaredClass->props.data[1];
CHECK(method.name == "method"); CHECK(method.name == "method");
CHECK(method.nameLocation == Location({3, 21}, {3, 27}));
CHECK(method.ty->is<AstTypeFunction>()); CHECK(method.ty->is<AstTypeFunction>());
CHECK(method.location == Location({3, 12}, {3, 54}));
CHECK(method.isMethod);
AstStatDeclareClass* subclass = stat->body.data[1]->as<AstStatDeclareClass>(); AstStatDeclareClass* subclass = stat->body.data[1]->as<AstStatDeclareClass>();
REQUIRE(subclass); REQUIRE(subclass);
@ -1928,7 +1943,9 @@ TEST_CASE_FIXTURE(Fixture, "parse_class_declarations")
REQUIRE_EQ(subclass->props.size, 1); REQUIRE_EQ(subclass->props.size, 1);
AstDeclaredClassProp& prop2 = subclass->props.data[0]; AstDeclaredClassProp& prop2 = subclass->props.data[0];
CHECK(prop2.name == "prop2"); CHECK(prop2.name == "prop2");
CHECK(prop2.nameLocation == Location({7, 12}, {7, 17}));
CHECK(prop2.ty->is<AstTypeReference>()); CHECK(prop2.ty->is<AstTypeReference>());
CHECK(prop2.location == Location({7, 12}, {7, 25}));
} }
TEST_CASE_FIXTURE(Fixture, "class_method_properties") 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'"); 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(); TEST_SUITE_END();

View File

@ -401,4 +401,13 @@ end
CHECK("(any, any) -> any" == toString(requireType("foo"))); 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(); TEST_SUITE_END();

View File

@ -7,6 +7,7 @@
#include "Fixture.h" #include "Fixture.h"
#include "ClassFixture.h" #include "ClassFixture.h"
#include "ScopedFlags.h"
#include "doctest.h" #include "doctest.h"
using namespace Luau; 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") TEST_CASE_FIXTURE(ClassFixture, "callable_classes")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(

View File

@ -7,6 +7,8 @@
#include "doctest.h" #include "doctest.h"
LUAU_FASTFLAG(LuauDeclarationExtraPropData)
using namespace Luau; using namespace Luau;
TEST_SUITE_BEGIN("DefinitionTests"); 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") TEST_CASE_FIXTURE(Fixture, "definitions_symbols_are_generated_for_recursively_referenced_types")
{ {
ScopedFastFlag luauDeclarationExtraPropData{FFlag::LuauDeclarationExtraPropData, true};
loadDefinition(R"( loadDefinition(R"(
declare class MyClass declare class MyClass
function myMethod(self) 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"); std::optional<TypeFun> myClassTy = frontend.globals.globalScope->lookupType("MyClass");
REQUIRE(bool(myClassTy)); REQUIRE(bool(myClassTy));
CHECK_EQ(myClassTy->type->documentationSymbol, "@test/globaltype/MyClass"); 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") 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)); 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") TEST_CASE_FIXTURE(Fixture, "local_function_fwd_decl_doesnt_crash")
{ {
CheckResult result = check(R"( 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") 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. // 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; using namespace Luau;
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAG(LuauInstantiateInSubtyping); LUAU_FASTFLAG(LuauInstantiateInSubtyping);
LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls); 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") TEST_CASE_FIXTURE(Fixture, "inferred_return_type_of_free_table")
{ {
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
// {FFlag::LuauLowerBoundsCalculation, true},
{FFlag::DebugLuauSharedSelf, true}, {FFlag::DebugLuauSharedSelf, true},
}; };

View File

@ -13,6 +13,7 @@ using namespace Luau;
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls); LUAU_FASTFLAG(LuauAlwaysCommitInferencesOfFunctionCalls);
LUAU_FASTFLAG(LuauUnifierRecursionOnRestart);
struct TryUnifyFixture : Fixture 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(); TEST_SUITE_END();

View File

@ -396,4 +396,15 @@ TEST_CASE_FIXTURE(Fixture, "lti_permit_explicit_never_annotation")
LUAU_REQUIRE_NO_ERRORS(result); 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(); 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) == 2)
assert(math.max(ma, mb, mc, md) == 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 -- noise
assert(math.noise(0.5) == 0) assert(math.noise(0.5) == 0)
assert(math.noise(0.5, 0.5) == -0.25) assert(math.noise(0.5, 0.5) == -0.25)
assert(math.noise(0.5, 0.5, -0.5) == 0.125) assert(math.noise(0.5, 0.5, -0.5) == 0.125)
assert(math.noise(455.7204209769105, 340.80410508750134, 121.80087666537628) == 0.5010709762573242) assert(math.noise(455.7204209769105, 340.80410508750134, 121.80087666537628) == 0.5010709762573242)
local inf = math.huge * 2 assert(math.noise(noinline(0.5)) == 0)
local nan = 0 / 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 -- sign
assert(math.sign(0) == 0) assert(math.sign(0) == 0)
@ -313,10 +325,12 @@ assert(math.sign(inf) == 1)
assert(math.sign(-inf) == -1) assert(math.sign(-inf) == -1)
assert(math.sign(nan) == 0) assert(math.sign(nan) == 0)
assert(math.min(nan, 2) ~= math.min(nan, 2)) assert(math.sign(noinline(0)) == 0)
assert(math.min(1, nan) == 1) assert(math.sign(noinline(42)) == 1)
assert(math.max(nan, 2) ~= math.max(nan, 2)) assert(math.sign(noinline(-42)) == -1)
assert(math.max(1, nan) == 1) assert(math.sign(noinline(inf)) == 1)
assert(math.sign(noinline(-inf)) == -1)
assert(math.sign(noinline(nan)) == 0)
-- clamp -- clamp
assert(math.clamp(-1, 0, 1) == 0) 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(2, 0, 1) == 1)
assert(math.clamp(4, 0, 0) == 0) 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 -- round
assert(math.round(0) == 0) assert(math.round(0) == 0)
assert(math.round(0.4) == 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(-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 -- 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(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 -- pow
assert(math.pow(2, 0) == 1) assert(math.pow(2, 0) == 1)
assert(math.pow(2, 2) == 4) assert(math.pow(2, 2) == 4)
assert(math.pow(4, 0.5) == 2) assert(math.pow(4, 0.5) == 2)
assert(math.pow(-2, 2) == 4) 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") 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 -- 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 -- to make sure the basic implementations are also correct we test these functions with string->number coercions
assert(math.abs("-4") == 4) assert(math.abs("-4") == 4)
@ -393,21 +451,4 @@ assert(math.sign("-2") == -1)
assert(math.sign("0") == 0) assert(math.sign("0") == 0)
assert(math.round("1.8") == 2) 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') return('OK')