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