Sync to upstream/release/537 (#607)

This commit is contained in:
Arseny Kapoulkine 2022-07-21 14:16:54 -07:00 committed by GitHub
parent a824b05c9e
commit b1cfaf5305
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1484 additions and 285 deletions

View File

@ -2,12 +2,15 @@
#pragma once
#include <string>
#include <vector>
namespace Luau
{
class AstNode;
struct Comment;
std::string toJson(AstNode* node);
std::string toJson(AstNode* node, const std::vector<Comment>& commentLocations);
} // namespace Luau

View File

@ -10,6 +10,7 @@
LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
LUAU_FASTFLAG(LuauCompleteVisitor);
namespace Luau
{
@ -129,11 +130,11 @@ struct GenericTypeVarVisitor
{
return visit(ty);
}
virtual bool visit(TypeId ty, const UnknownTypeVar& atv)
virtual bool visit(TypeId ty, const UnknownTypeVar& utv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const NeverTypeVar& atv)
virtual bool visit(TypeId ty, const NeverTypeVar& ntv)
{
return visit(ty);
}
@ -145,6 +146,14 @@ struct GenericTypeVarVisitor
{
return visit(ty);
}
virtual bool visit(TypeId ty, const BlockedTypeVar& btv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const SingletonTypeVar& stv)
{
return visit(ty);
}
virtual bool visit(TypePackId tp)
{
@ -190,16 +199,12 @@ struct GenericTypeVarVisitor
if (visit(ty, *btv))
traverse(btv->boundTo);
}
else if (auto ftv = get<FreeTypeVar>(ty))
visit(ty, *ftv);
else if (auto gtv = get<GenericTypeVar>(ty))
visit(ty, *gtv);
else if (auto etv = get<ErrorTypeVar>(ty))
visit(ty, *etv);
else if (auto ctv = get<ConstrainedTypeVar>(ty))
{
if (visit(ty, *ctv))
@ -208,10 +213,8 @@ struct GenericTypeVarVisitor
traverse(part);
}
}
else if (auto ptv = get<PrimitiveTypeVar>(ty))
visit(ty, *ptv);
else if (auto ftv = get<FunctionTypeVar>(ty))
{
if (visit(ty, *ftv))
@ -220,7 +223,6 @@ struct GenericTypeVarVisitor
traverse(ftv->retTypes);
}
}
else if (auto ttv = get<TableTypeVar>(ty))
{
// Some visitors want to see bound tables, that's why we traverse the original type
@ -243,7 +245,6 @@ struct GenericTypeVarVisitor
}
}
}
else if (auto mtv = get<MetatableTypeVar>(ty))
{
if (visit(ty, *mtv))
@ -252,7 +253,6 @@ struct GenericTypeVarVisitor
traverse(mtv->metatable);
}
}
else if (auto ctv = get<ClassTypeVar>(ty))
{
if (visit(ty, *ctv))
@ -267,10 +267,8 @@ struct GenericTypeVarVisitor
traverse(*ctv->metatable);
}
}
else if (auto atv = get<AnyTypeVar>(ty))
visit(ty, *atv);
else if (auto utv = get<UnionTypeVar>(ty))
{
if (visit(ty, *utv))
@ -279,7 +277,6 @@ struct GenericTypeVarVisitor
traverse(optTy);
}
}
else if (auto itv = get<IntersectionTypeVar>(ty))
{
if (visit(ty, *itv))
@ -288,6 +285,24 @@ struct GenericTypeVarVisitor
traverse(partTy);
}
}
else if (!FFlag::LuauCompleteVisitor)
return visit_detail::unsee(seen, ty);
else if (get<LazyTypeVar>(ty))
{
// Visiting into LazyTypeVar may necessarily cause infinite expansion, so we don't do that on purpose.
// Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassTypeVar
// that doesn't need to be expanded.
}
else if (auto stv = get<SingletonTypeVar>(ty))
visit(ty, *stv);
else if (auto btv = get<BlockedTypeVar>(ty))
visit(ty, *btv);
else if (auto utv = get<UnknownTypeVar>(ty))
visit(ty, *utv);
else if (auto ntv = get<NeverTypeVar>(ty))
visit(ty, *ntv);
else
LUAU_ASSERT(!"GenericTypeVarVisitor::traverse(TypeId) is not exhaustive!");
visit_detail::unsee(seen, ty);
}

View File

@ -2,6 +2,7 @@
#include "Luau/JsonEncoder.h"
#include "Luau/Ast.h"
#include "Luau/ParseResult.h"
#include "Luau/StringUtils.h"
#include "Luau/Common.h"
@ -75,6 +76,11 @@ struct AstJsonEncoder : public AstVisitor
writeRaw(std::string_view{&c, 1});
}
void writeType(std::string_view propValue)
{
write("type", propValue);
}
template<typename T>
void write(std::string_view propName, const T& value)
{
@ -98,7 +104,7 @@ struct AstJsonEncoder : public AstVisitor
void write(double d)
{
char b[256];
sprintf(b, "%g", d);
sprintf(b, "%.17g", d);
writeRaw(b);
}
@ -111,8 +117,12 @@ struct AstJsonEncoder : public AstVisitor
{
if (c == '"')
writeRaw("\\\"");
else if (c == '\0')
writeRaw("\\\0");
else if (c == '\\')
writeRaw("\\\\");
else if (c < ' ')
writeRaw(format("\\u%04x", c));
else if (c == '\n')
writeRaw("\\n");
else
writeRaw(c);
}
@ -189,10 +199,11 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("{");
bool c = pushComma();
if (local->annotation != nullptr)
write("type", local->annotation);
write("luauType", local->annotation);
else
write("type", nullptr);
write("luauType", nullptr);
write("name", local->name);
writeType("AstLocal");
write("location", local->location);
popComma(c);
writeRaw("}");
@ -208,7 +219,7 @@ struct AstJsonEncoder : public AstVisitor
{
writeRaw("{");
bool c = pushComma();
write("type", name);
writeType(name);
writeNode(node);
f();
popComma(c);
@ -358,6 +369,7 @@ struct AstJsonEncoder : public AstVisitor
{
writeRaw("{");
bool c = pushComma();
writeType("AstTypeList");
write("types", typeList.types);
if (typeList.tailType)
write("tailType", typeList.tailType);
@ -369,9 +381,10 @@ struct AstJsonEncoder : public AstVisitor
{
writeRaw("{");
bool c = pushComma();
writeType("AstGenericType");
write("name", genericType.name);
if (genericType.defaultValue)
write("type", genericType.defaultValue);
write("luauType", genericType.defaultValue);
popComma(c);
writeRaw("}");
}
@ -380,9 +393,10 @@ struct AstJsonEncoder : public AstVisitor
{
writeRaw("{");
bool c = pushComma();
writeType("AstGenericTypePack");
write("name", genericTypePack.name);
if (genericTypePack.defaultValue)
write("type", genericTypePack.defaultValue);
write("luauType", genericTypePack.defaultValue);
popComma(c);
writeRaw("}");
}
@ -404,6 +418,7 @@ struct AstJsonEncoder : public AstVisitor
{
writeRaw("{");
bool c = pushComma();
writeType("AstExprTableItem");
write("kind", item.kind);
switch (item.kind)
{
@ -419,6 +434,17 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("}");
}
void write(class AstExprIfElse* node)
{
writeNode(node, "AstExprIfElse", [&]() {
PROP(condition);
PROP(hasThen);
PROP(trueExpr);
PROP(hasElse);
PROP(falseExpr);
});
}
void write(class AstExprTable* node)
{
writeNode(node, "AstExprTable", [&]() {
@ -431,11 +457,11 @@ struct AstJsonEncoder : public AstVisitor
switch (op)
{
case AstExprUnary::Not:
return writeString("not");
return writeString("Not");
case AstExprUnary::Minus:
return writeString("minus");
return writeString("Minus");
case AstExprUnary::Len:
return writeString("len");
return writeString("Len");
}
}
@ -541,7 +567,7 @@ struct AstJsonEncoder : public AstVisitor
void write(class AstStatWhile* node)
{
writeNode(node, "AtStatWhile", [&]() {
writeNode(node, "AstStatWhile", [&]() {
PROP(condition);
PROP(body);
PROP(hasDo);
@ -684,7 +710,8 @@ struct AstJsonEncoder : public AstVisitor
writeRaw("{");
bool c = pushComma();
write("name", prop.name);
write("type", prop.ty);
writeType("AstDeclaredClassProp");
write("luauType", prop.ty);
popComma(c);
writeRaw("}");
}
@ -731,8 +758,9 @@ struct AstJsonEncoder : public AstVisitor
bool c = pushComma();
write("name", prop.name);
writeType("AstTableProp");
write("location", prop.location);
write("type", prop.type);
write("propType", prop.type);
popComma(c);
writeRaw("}");
@ -745,6 +773,24 @@ struct AstJsonEncoder : public AstVisitor
PROP(indexer);
});
}
void write(struct AstTableIndexer* indexer)
{
if (indexer)
{
writeRaw("{");
bool c = pushComma();
write("location", indexer->location);
write("indexType", indexer->indexType);
write("resultType", indexer->resultType);
popComma(c);
writeRaw("}");
}
else
{
writeRaw("null");
}
}
void write(class AstTypeFunction* node)
{
@ -836,6 +882,12 @@ struct AstJsonEncoder : public AstVisitor
return false;
}
bool visit(class AstExprIfElse* node) override
{
write(node);
return false;
}
bool visit(class AstExprLocal* node) override
{
write(node);
@ -1093,6 +1145,42 @@ struct AstJsonEncoder : public AstVisitor
write(node);
return false;
}
void writeComments(std::vector<Comment> commentLocations)
{
bool commentComma = false;
for (Comment comment : commentLocations)
{
if (commentComma)
{
writeRaw(",");
}
else
{
commentComma = true;
}
writeRaw("{");
bool c = pushComma();
switch (comment.type)
{
case Lexeme::Comment:
writeType("Comment");
break;
case Lexeme::BlockComment:
writeType("BlockComment");
break;
case Lexeme::BrokenComment:
writeType("BrokenComment");
break;
default:
break;
}
write("location", comment.location);
popComma(c);
writeRaw("}");
}
}
};
std::string toJson(AstNode* node)
@ -1102,4 +1190,15 @@ std::string toJson(AstNode* node)
return encoder.str();
}
std::string toJson(AstNode* node, const std::vector<Comment>& commentLocations)
{
AstJsonEncoder encoder;
encoder.writeRaw(R"({"root":)");
node->visit(&encoder);
encoder.writeRaw(R"(,"commentLocations":[)");
encoder.writeComments(commentLocations);
encoder.writeRaw("]}");
return encoder.str();
}
} // namespace Luau

View File

@ -17,6 +17,7 @@
LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false);
namespace Luau
{
@ -124,15 +125,21 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
moduleScope2->returnType = returnType; // TODO varargPack
}
ForceNormal forceNormal{&interfaceTypes};
if (FFlag::LuauLowerBoundsCalculation)
{
normalize(returnType, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(returnType);
if (varargPack)
{
normalize(*varargPack, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(*varargPack);
}
}
ForceNormal forceNormal{&interfaceTypes};
if (exportedTypeBindings)
{
for (auto& [name, tf] : *exportedTypeBindings)
@ -147,6 +154,16 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
// We're about to freeze the memory. We know that the flag is conservative by design. Cyclic tables
// won't be marked normal. If the types aren't normal by now, they never will be.
forceNormal.traverse(tf.type);
for (GenericTypeDefinition param : tf.typeParams)
{
forceNormal.traverse(param.ty);
if (param.defaultValue)
{
normalize(*param.defaultValue, interfaceTypes, ice);
forceNormal.traverse(*param.defaultValue);
}
}
}
}
}
@ -166,7 +183,12 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
{
ty = clone(ty, interfaceTypes, cloneState);
if (FFlag::LuauLowerBoundsCalculation)
{
normalize(ty, interfaceTypes, ice);
if (FFlag::LuauForceExportSurfacesToBeNormal)
forceNormal.traverse(ty);
}
}
freeze(internalTypes);

View File

@ -31,6 +31,7 @@ LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300)
LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500)
LUAU_FASTFLAG(LuauKnowsTheDataModel3)
LUAU_FASTFLAG(LuauAutocompleteDynamicLimits)
LUAU_FASTFLAGVARIABLE(LuauExpectedTableUnionIndexerType, false)
LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
@ -38,7 +39,6 @@ LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix2, false)
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
LUAU_FASTFLAGVARIABLE(LuauReturnTypeInferenceInNonstrict, false)
LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false);
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
@ -50,6 +50,7 @@ LUAU_FASTFLAGVARIABLE(LuauCheckGenericHOFTypes, false)
LUAU_FASTFLAGVARIABLE(LuauBinaryNeedsExpectedTypesToo, false)
LUAU_FASTFLAGVARIABLE(LuauNeverTypesAndOperatorsInference, false)
LUAU_FASTFLAGVARIABLE(LuauReturnsFromCallsitesAreNotWidened, false)
LUAU_FASTFLAGVARIABLE(LuauCompleteVisitor, false)
namespace Luau
{
@ -890,7 +891,7 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatReturn& return_)
TypePackId retPack = checkExprList(scope, return_.location, return_.list, false, {}, expectedTypes).type;
if (FFlag::LuauReturnTypeInferenceInNonstrict ? FFlag::LuauLowerBoundsCalculation : useConstrainedIntersections())
if (useConstrainedIntersections())
{
unifyLowerBound(retPack, scope->returnType, demoter.demotedLevel(scope->level), return_.location);
return;
@ -1292,6 +1293,11 @@ void TypeChecker::check(const ScopePtr& scope, const AstStatForIn& forin)
for (size_t i = 2; i < varTypes.size(); ++i)
unify(nilType, varTypes[i], forin.location);
}
else if (isNonstrictMode())
{
for (TypeId var : varTypes)
unify(anyType, var, forin.location);
}
else
{
TypeId varTy = errorRecoveryType(loopScope);
@ -1385,12 +1391,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
// If in nonstrict mode and allowing redefinition of global function, restore the previous definition type
// in case this function has a differing signature. The signature discrepancy will be caught in checkBlock.
if (previouslyDefined)
{
if (FFlag::LuauReturnTypeInferenceInNonstrict && FFlag::LuauLowerBoundsCalculation)
quantify(funScope, ty, exprName->location);
globalBindings[name] = oldBinding;
}
else
globalBindings[name] = {quantify(funScope, ty, exprName->location), exprName->location};
@ -2365,9 +2366,16 @@ WithPredicate<TypeId> TypeChecker::checkExpr(const ScopePtr& scope, const AstExp
{
std::vector<TypeId> expectedResultTypes;
for (TypeId expectedOption : expectedUnion)
{
if (const TableTypeVar* ttv = get<TableTypeVar>(follow(expectedOption)))
{
if (auto prop = ttv->props.find(key->value.data); prop != ttv->props.end())
expectedResultTypes.push_back(prop->second.type);
else if (FFlag::LuauExpectedTableUnionIndexerType && ttv->indexer && maybeString(ttv->indexer->indexType))
expectedResultTypes.push_back(ttv->indexer->indexResultType);
}
}
if (expectedResultTypes.size() == 1)
expectedResultType = expectedResultTypes[0];
else if (expectedResultTypes.size() > 1)
@ -3367,7 +3375,7 @@ std::pair<TypeId, ScopePtr> TypeChecker::checkFunctionSignature(const ScopePtr&
TypePackId retPack;
if (expr.returnAnnotation)
retPack = resolveTypePack(funScope, *expr.returnAnnotation);
else if (FFlag::LuauReturnTypeInferenceInNonstrict ? (!FFlag::LuauLowerBoundsCalculation && isNonstrictMode()) : isNonstrictMode())
else if (isNonstrictMode())
retPack = anyTypePack;
else if (expectedFunctionType &&
(!FFlag::LuauCheckGenericHOFTypes || (expectedFunctionType->generics.empty() && expectedFunctionType->genericPacks.empty())))

View File

@ -7,6 +7,7 @@
#include "Luau/Transpiler.h"
#include "FileUtils.h"
#include "Flags.h"
#ifdef CALLGRIND
#include <valgrind/callgrind.h>
@ -223,9 +224,7 @@ int main(int argc, char** argv)
{
Luau::assertHandler() = assertionHandler;
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0)
flag->value = true;
setLuauFlagsDefault();
if (argc >= 2 && strcmp(argv[1], "--help") == 0)
{
@ -252,12 +251,14 @@ int main(int argc, char** argv)
annotate = true;
else if (strcmp(argv[i], "--timetrace") == 0)
FFlag::DebugLuauTimeTracing.value = true;
else if (strncmp(argv[i], "--fflags=", 9) == 0)
setLuauFlags(argv[i] + 9);
}
#if !defined(LUAU_ENABLE_TIME_TRACE)
if (FFlag::DebugLuauTimeTracing)
{
printf("To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1;
}
#endif

View File

@ -62,6 +62,7 @@ int main(int argc, char** argv)
Luau::AstNameTable names(allocator);
Luau::ParseOptions options;
options.captureComments = true;
options.supportContinueStatement = true;
options.allowTypeAnnotations = true;
options.allowDeclarationSyntax = true;
@ -78,7 +79,7 @@ int main(int argc, char** argv)
fprintf(stderr, "\n");
}
printf("%s", Luau::toJson(parseResult.root).c_str());
printf("%s", Luau::toJson(parseResult.root, parseResult.commentLocations).c_str());
return parseResult.errors.size() > 0 ? 1 : 0;
}

75
CLI/Flags.cpp Normal file
View File

@ -0,0 +1,75 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/Common.h"
#include "Luau/ExperimentalFlags.h"
#include <string_view>
#include <stdio.h>
#include <string.h>
static void setLuauFlag(std::string_view name, bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
{
if (name == flag->name)
{
flag->value = state;
return;
}
}
fprintf(stderr, "Warning: unrecognized flag '%.*s'.\n", int(name.length()), name.data());
}
static void setLuauFlags(bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0)
flag->value = state;
}
void setLuauFlagsDefault()
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
if (strncmp(flag->name, "Luau", 4) == 0 && !Luau::isFlagExperimental(flag->name))
flag->value = true;
}
void setLuauFlags(const char* list)
{
std::string_view rest = list;
while (!rest.empty())
{
size_t ending = rest.find(",");
std::string_view element = rest.substr(0, ending);
if (size_t separator = element.find('='); separator != std::string_view::npos)
{
std::string_view key = element.substr(0, separator);
std::string_view value = element.substr(separator + 1);
if (value == "true" || value == "True")
setLuauFlag(key, true);
else if (value == "false" || value == "False")
setLuauFlag(key, false);
else
fprintf(stderr, "Warning: unrecognized value '%.*s' for flag '%.*s'.\n", int(value.length()), value.data(), int(key.length()),
key.data());
}
else
{
if (element == "true" || element == "True")
setLuauFlags(true);
else if (element == "false" || element == "False")
setLuauFlags(false);
else
setLuauFlag(element, true);
}
if (ending != std::string_view::npos)
rest.remove_prefix(ending + 1);
else
break;
}
}

5
CLI/Flags.h Normal file
View File

@ -0,0 +1,5 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
void setLuauFlagsDefault();
void setLuauFlags(const char* list);

View File

@ -8,9 +8,10 @@
#include "Luau/BytecodeBuilder.h"
#include "Luau/Parser.h"
#include "FileUtils.h"
#include "Profiler.h"
#include "Coverage.h"
#include "FileUtils.h"
#include "Flags.h"
#include "Profiler.h"
#include "isocline.h"
@ -97,7 +98,11 @@ static int lua_require(lua_State* L)
// return the module from the cache
lua_getfield(L, -1, name.c_str());
if (!lua_isnil(L, -1))
{
// L stack: _MODULES result
return finishrequire(L);
}
lua_pop(L, 1);
std::optional<std::string> source = readFile(name + ".luau");
@ -109,6 +114,7 @@ static int lua_require(lua_State* L)
}
// module needs to run in a new thread, isolated from the rest
// note: we create ML on main thread so that it doesn't inherit environment of L
lua_State* GL = lua_mainthread(L);
lua_State* ML = lua_newthread(GL);
lua_xmove(GL, L, 1);
@ -142,11 +148,12 @@ static int lua_require(lua_State* L)
}
}
// there's now a return value on top of ML; stack of L is MODULES thread
// there's now a return value on top of ML; L stack: _MODULES ML
lua_xmove(ML, L, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -4, name.c_str());
// L stack: _MODULES ML result
return finishrequire(L);
}
@ -682,60 +689,11 @@ static int assertionHandler(const char* expr, const char* file, int line, const
return 1;
}
static void setLuauFlags(bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
{
if (strncmp(flag->name, "Luau", 4) == 0)
flag->value = state;
}
}
static void setFlag(std::string_view name, bool state)
{
for (Luau::FValue<bool>* flag = Luau::FValue<bool>::list; flag; flag = flag->next)
{
if (name == flag->name)
{
flag->value = state;
return;
}
}
fprintf(stderr, "Warning: --fflag unrecognized flag '%.*s'.\n\n", int(name.length()), name.data());
}
static void applyFlagKeyValue(std::string_view element)
{
if (size_t separator = element.find('='); separator != std::string_view::npos)
{
std::string_view key = element.substr(0, separator);
std::string_view value = element.substr(separator + 1);
if (value == "true")
setFlag(key, true);
else if (value == "false")
setFlag(key, false);
else
fprintf(stderr, "Warning: --fflag unrecognized value '%.*s' for flag '%.*s'.\n\n", int(value.length()), value.data(), int(key.length()),
key.data());
}
else
{
if (element == "true")
setLuauFlags(true);
else if (element == "false")
setLuauFlags(false);
else
setFlag(element, true);
}
}
int replMain(int argc, char** argv)
{
Luau::assertHandler() = assertionHandler;
setLuauFlags(true);
setLuauFlagsDefault();
CliMode mode = CliMode::Unknown;
CompileFormat compileFormat{};
@ -818,27 +776,10 @@ int replMain(int argc, char** argv)
else if (strcmp(argv[i], "--timetrace") == 0)
{
FFlag::DebugLuauTimeTracing.value = true;
#if !defined(LUAU_ENABLE_TIME_TRACE)
printf("To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1;
#endif
}
else if (strncmp(argv[i], "--fflags=", 9) == 0)
{
std::string_view list = argv[i] + 9;
while (!list.empty())
{
size_t ending = list.find(",");
applyFlagKeyValue(list.substr(0, ending));
if (ending != std::string_view::npos)
list.remove_prefix(ending + 1);
else
break;
}
setLuauFlags(argv[i] + 9);
}
else if (argv[i][0] == '-')
{
@ -848,6 +789,14 @@ int replMain(int argc, char** argv)
}
}
#if !defined(LUAU_ENABLE_TIME_TRACE)
if (FFlag::DebugLuauTimeTracing)
{
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
return 1;
}
#endif
const std::vector<std::string> files = getSourceFiles(argc, argv);
if (mode == CliMode::Unknown)
{

View File

@ -61,12 +61,22 @@ public:
void call(Label& label);
void call(OperandX64 op);
void int3();
// AVX
void vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vaddps(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vaddsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vaddss(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vsubsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vmulsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vdivsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vxorpd(OperandX64 dst, OperandX64 src1, OperandX64 src2);
void vcomisd(OperandX64 src1, OperandX64 src2);
void vsqrtpd(OperandX64 dst, OperandX64 src);
void vsqrtps(OperandX64 dst, OperandX64 src);
void vsqrtsd(OperandX64 dst, OperandX64 src1, OperandX64 src2);

View File

@ -37,8 +37,6 @@ enum class Condition
Zero,
NotZero,
// TODO: ordered and unordered floating-point conditions
Count
};

View File

@ -231,6 +231,7 @@ void AssemblyBuilderX64::lea(OperandX64 lhs, OperandX64 rhs)
if (logText)
log("lea", lhs, rhs);
LUAU_ASSERT(rhs.cat == CategoryX64::mem);
placeBinaryRegAndRegMem(lhs, rhs, 0x8d, 0x8d);
}
@ -314,6 +315,14 @@ void AssemblyBuilderX64::call(OperandX64 op)
commit();
}
void AssemblyBuilderX64::int3()
{
if (logText)
log("int3");
place(0xcc);
}
void AssemblyBuilderX64::vaddpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vaddpd", dst, src1, src2, 0x58, false, AVX_0F, AVX_66);
@ -334,6 +343,31 @@ void AssemblyBuilderX64::vaddss(OperandX64 dst, OperandX64 src1, OperandX64 src2
placeAvx("vaddss", dst, src1, src2, 0x58, false, AVX_0F, AVX_F3);
}
void AssemblyBuilderX64::vsubsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vsubsd", dst, src1, src2, 0x5c, false, AVX_0F, AVX_F2);
}
void AssemblyBuilderX64::vmulsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vmulsd", dst, src1, src2, 0x59, false, AVX_0F, AVX_F2);
}
void AssemblyBuilderX64::vdivsd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vdivsd", dst, src1, src2, 0x5e, false, AVX_0F, AVX_F2);
}
void AssemblyBuilderX64::vxorpd(OperandX64 dst, OperandX64 src1, OperandX64 src2)
{
placeAvx("vxorpd", dst, src1, src2, 0x57, false, AVX_0F, AVX_66);
}
void AssemblyBuilderX64::vcomisd(OperandX64 src1, OperandX64 src2)
{
placeAvx("vcomisd", src1, src2, 0x2f, false, AVX_0F, AVX_66);
}
void AssemblyBuilderX64::vsqrtpd(OperandX64 dst, OperandX64 src)
{
placeAvx("vsqrtpd", dst, src, 0x51, false, AVX_0F, AVX_66);
@ -494,9 +528,10 @@ void AssemblyBuilderX64::placeBinaryRegMemAndImm(OperandX64 lhs, OperandX64 rhs,
LUAU_ASSERT(lhs.cat == CategoryX64::reg || lhs.cat == CategoryX64::mem);
LUAU_ASSERT(rhs.cat == CategoryX64::imm);
SizeX64 size = lhs.base.size;
SizeX64 size = lhs.cat == CategoryX64::reg ? lhs.base.size : lhs.memSize;
LUAU_ASSERT(size == SizeX64::byte || size == SizeX64::dword || size == SizeX64::qword);
placeRex(lhs.base);
placeRex(lhs);
if (size == SizeX64::byte)
{

View File

@ -0,0 +1,26 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <string.h>
namespace Luau
{
inline bool isFlagExperimental(const char* flag)
{
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
// or critical bugs that are found after the code has been submitted.
static const char* kList[] =
{
"LuauLowerBoundsCalculation",
nullptr, // makes sure we always have at least one entry
};
for (const char* item: kList)
if (item && strcmp(item, flag) == 0)
return true;
return false;
}
}

View File

@ -27,6 +27,7 @@ LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFoldBuiltins, false)
LUAU_FASTFLAGVARIABLE(LuauCompileBetterMultret, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
namespace Luau
{
@ -616,7 +617,7 @@ struct Compiler
}
else
{
AstExprLocal* le = arg->as<AstExprLocal>();
AstExprLocal* le = FFlag::LuauCompileFreeReassign ? getExprLocal(arg) : arg->as<AstExprLocal>();
Variable* lv = le ? variables.find(le->local) : nullptr;
// if the argument is a local that isn't mutated, we will simply reuse the existing register
@ -2200,19 +2201,27 @@ struct Compiler
compileLValueUse(lv, source, /* set= */ true);
}
int getExprLocalReg(AstExpr* node)
AstExprLocal* getExprLocal(AstExpr* node)
{
if (AstExprLocal* expr = node->as<AstExprLocal>())
return expr;
else if (AstExprGroup* expr = node->as<AstExprGroup>())
return getExprLocal(expr->expr);
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
return getExprLocal(expr->expr);
else
return nullptr;
}
int getExprLocalReg(AstExpr* node)
{
if (AstExprLocal* expr = getExprLocal(node))
{
// note: this can't check expr->upvalue because upvalues may be upgraded to locals during inlining
Local* l = locals.find(expr->local);
return l && l->allocated ? l->reg : -1;
}
else if (AstExprGroup* expr = node->as<AstExprGroup>())
return getExprLocalReg(expr->expr);
else if (AstExprTypeAssertion* expr = node->as<AstExprTypeAssertion>())
return getExprLocalReg(expr->expr);
else
return -1;
}
@ -2498,6 +2507,22 @@ struct Compiler
if (options.optimizationLevel >= 1 && options.debugLevel <= 1 && areLocalsRedundant(stat))
return;
// Optimization: for 1-1 local assignments, we can reuse the register *if* neither local is mutated
if (FFlag::LuauCompileFreeReassign && options.optimizationLevel >= 1 && stat->vars.size == 1 && stat->values.size == 1)
{
if (AstExprLocal* re = getExprLocal(stat->values.data[0]))
{
Variable* lv = variables.find(stat->vars.data[0]);
Variable* rv = variables.find(re->local);
if (int reg = getExprLocalReg(re); reg >= 0 && (!lv || !lv->written) && (!rv || !rv->written))
{
pushLocal(stat->vars.data[0], uint8_t(reg));
return;
}
}
}
// 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));

View File

@ -31,15 +31,15 @@ ISOCLINE_SOURCES=extern/isocline/src/isocline.c
ISOCLINE_OBJECTS=$(ISOCLINE_SOURCES:%=$(BUILD)/%.o)
ISOCLINE_TARGET=$(BUILD)/libisocline.a
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/FileUtils.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp
TESTS_SOURCES=$(wildcard tests/*.cpp) CLI/FileUtils.cpp CLI/Flags.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp
TESTS_OBJECTS=$(TESTS_SOURCES:%=$(BUILD)/%.o)
TESTS_TARGET=$(BUILD)/luau-tests
REPL_CLI_SOURCES=CLI/FileUtils.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp CLI/ReplEntry.cpp
REPL_CLI_SOURCES=CLI/FileUtils.cpp CLI/Flags.cpp CLI/Profiler.cpp CLI/Coverage.cpp CLI/Repl.cpp CLI/ReplEntry.cpp
REPL_CLI_OBJECTS=$(REPL_CLI_SOURCES:%=$(BUILD)/%.o)
REPL_CLI_TARGET=$(BUILD)/luau
ANALYZE_CLI_SOURCES=CLI/FileUtils.cpp CLI/Analyze.cpp
ANALYZE_CLI_SOURCES=CLI/FileUtils.cpp CLI/Flags.cpp CLI/Analyze.cpp
ANALYZE_CLI_OBJECTS=$(ANALYZE_CLI_SOURCES:%=$(BUILD)/%.o)
ANALYZE_CLI_TARGET=$(BUILD)/luau-analyze
@ -117,15 +117,18 @@ $(REPL_CLI_TARGET): LDFLAGS+=-lpthread
fuzz-proto fuzz-prototest: LDFLAGS+=build/libprotobuf-mutator/src/libfuzzer/libprotobuf-mutator-libfuzzer.a build/libprotobuf-mutator/src/libprotobuf-mutator.a build/libprotobuf-mutator/external.protobuf/lib/libprotobuf.a
# pseudo targets
.PHONY: all test clean coverage format luau-size
.PHONY: all test clean coverage format luau-size aliases
all: $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(TESTS_TARGET)
all: $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(TESTS_TARGET) aliases
aliases: luau luau-analyze
test: $(TESTS_TARGET)
$(TESTS_TARGET) $(TESTS_ARGS)
clean:
rm -rf $(BUILD)
rm -rf luau luau-analyze
coverage: $(TESTS_TARGET)
$(TESTS_TARGET) --fflags=true

View File

@ -4,6 +4,7 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.19")
target_sources(Luau.Common PRIVATE
Common/include/Luau/Common.h
Common/include/Luau/Bytecode.h
Common/include/Luau/ExperimentalFlags.h
)
endif()
@ -220,6 +221,8 @@ if(TARGET Luau.Repl.CLI)
CLI/Coverage.cpp
CLI/FileUtils.h
CLI/FileUtils.cpp
CLI/Flags.h
CLI/Flags.cpp
CLI/Profiler.h
CLI/Profiler.cpp
CLI/Repl.cpp
@ -231,6 +234,8 @@ if(TARGET Luau.Analyze.CLI)
target_sources(Luau.Analyze.CLI PRIVATE
CLI/FileUtils.h
CLI/FileUtils.cpp
CLI/Flags.h
CLI/Flags.cpp
CLI/Analyze.cpp)
endif()
@ -321,6 +326,8 @@ if(TARGET Luau.CLI.Test)
CLI/Coverage.cpp
CLI/FileUtils.h
CLI/FileUtils.cpp
CLI/Flags.h
CLI/Flags.cpp
CLI/Profiler.h
CLI/Profiler.cpp
CLI/Repl.cpp

View File

@ -854,7 +854,7 @@ void lua_rawset(lua_State* L, int idx)
StkId t = index2addr(L, idx);
api_check(L, ttistable(t));
if (hvalue(t)->readonly)
luaG_runerror(L, "Attempt to modify a readonly table");
luaG_readonlyerror(L);
setobj2t(L, luaH_set(L, hvalue(t), L->top - 2), L->top - 1);
luaC_barriert(L, hvalue(t), L->top - 1);
L->top -= 2;
@ -867,7 +867,7 @@ void lua_rawseti(lua_State* L, int idx, int n)
StkId o = index2addr(L, idx);
api_check(L, ttistable(o));
if (hvalue(o)->readonly)
luaG_runerror(L, "Attempt to modify a readonly table");
luaG_readonlyerror(L);
setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top - 1);
luaC_barriert(L, hvalue(o), L->top - 1);
L->top--;
@ -890,7 +890,7 @@ int lua_setmetatable(lua_State* L, int objindex)
case LUA_TTABLE:
{
if (hvalue(obj)->readonly)
luaG_runerror(L, "Attempt to modify a readonly table");
luaG_readonlyerror(L);
hvalue(obj)->metatable = mt;
if (mt)
luaC_objbarrier(L, hvalue(obj), mt);

View File

@ -269,6 +269,11 @@ l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2)
luaG_runerror(L, "attempt to index %s with %s", t1, t2);
}
l_noret luaG_readonlyerror(lua_State* L)
{
luaG_runerror(L, "attempt to modify a readonly table");
}
static void pusherror(lua_State* L, const char* msg)
{
CallInfo* ci = L->ci;

View File

@ -19,6 +19,7 @@ LUAI_FUNC l_noret luaG_concaterror(lua_State* L, StkId p1, StkId p2);
LUAI_FUNC l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
LUAI_FUNC l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
LUAI_FUNC l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2);
LUAI_FUNC l_noret luaG_readonlyerror(lua_State* L);
LUAI_FUNC LUA_PRINTF_ATTR(2, 3) l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...);
LUAI_FUNC void luaG_pusherror(lua_State* L, const char* error);

View File

@ -79,7 +79,7 @@ static void moveelements(lua_State* L, int srct, int dstt, int f, int e, int t)
Table* dst = hvalue(L->base + (dstt - 1));
if (dst->readonly)
luaG_runerror(L, "Attempt to modify a readonly table");
luaG_readonlyerror(L);
int n = e - f + 1; /* number of elements to move */
@ -204,7 +204,7 @@ static int tmove(lua_State* L)
Table* dst = hvalue(L->base + (tt - 1));
if (dst->readonly) /* also checked in moveelements, but this blocks resizes of r/o tables */
luaG_runerror(L, "Attempt to modify a readonly table");
luaG_readonlyerror(