Sync to upstream/release/521 (#443)

This commit is contained in:
Arseny Kapoulkine 2022-03-31 14:01:51 -07:00 committed by GitHub
parent ba60730e0f
commit 4c1f208d7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 222 additions and 119 deletions

View File

@ -9,7 +9,6 @@
#include <algorithm> #include <algorithm>
LUAU_FASTFLAG(LuauAssertStripsFalsyTypes) LUAU_FASTFLAG(LuauAssertStripsFalsyTypes)
LUAU_FASTFLAGVARIABLE(LuauTableCloneType, false)
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false) LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
/** FIXME: Many of these type definitions are not quite completely accurate. /** FIXME: Many of these type definitions are not quite completely accurate.
@ -289,9 +288,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
{ {
// tabTy is a generic table type which we can't express via declaration syntax yet // tabTy is a generic table type which we can't express via declaration syntax yet
ttv->props["freeze"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.freeze"); ttv->props["freeze"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.freeze");
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
if (FFlag::LuauTableCloneType)
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
attachMagicFunction(ttv->props["pack"].type, magicFunctionPack); attachMagicFunction(ttv->props["pack"].type, magicFunctionPack);
} }

View File

@ -27,6 +27,7 @@ LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as fals
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false) LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false) LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
LUAU_FASTFLAGVARIABLE(LuauInferStatFunction, false)
LUAU_FASTFLAGVARIABLE(LuauSealExports, false) LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix, false) LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix, false)
LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false) LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false)
@ -34,7 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false) LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false) LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false) LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify2, false) LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify3, false)
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false) LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
LUAU_FASTFLAG(LuauTypeMismatchModuleName) LUAU_FASTFLAG(LuauTypeMismatchModuleName)
LUAU_FASTFLAGVARIABLE(LuauTwoPassAliasDefinitionFix, false) LUAU_FASTFLAGVARIABLE(LuauTwoPassAliasDefinitionFix, false)
@ -463,7 +464,18 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block)
} }
else if (auto fun = (*protoIter)->as<AstStatFunction>()) else if (auto fun = (*protoIter)->as<AstStatFunction>())
{ {
auto pair = checkFunctionSignature(scope, subLevel, *fun->func, fun->name->location, std::nullopt); std::optional<TypeId> expectedType;
if (FFlag::LuauInferStatFunction && !fun->func->self)
{
if (auto name = fun->name->as<AstExprIndexName>())
{
TypeId exprTy = checkExpr(scope, *name->expr).type;
expectedType = getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, false);
}
}
auto pair = checkFunctionSignature(scope, subLevel, *fun->func, fun->name->location, expectedType);
auto [funTy, funScope] = pair; auto [funTy, funScope] = pair;
functionDecls[*protoIter] = pair; functionDecls[*protoIter] = pair;
@ -1103,7 +1115,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
scope->bindings[name->local] = {anyIfNonstrict(quantify(funScope, ty, name->local->location)), name->local->location}; scope->bindings[name->local] = {anyIfNonstrict(quantify(funScope, ty, name->local->location)), name->local->location};
return; return;
} }
else if (auto name = function.name->as<AstExprIndexName>(); name && FFlag::LuauStatFunctionSimplify2) else if (auto name = function.name->as<AstExprIndexName>(); name && FFlag::LuauStatFunctionSimplify3)
{ {
TypeId exprTy = checkExpr(scope, *name->expr).type; TypeId exprTy = checkExpr(scope, *name->expr).type;
TableTypeVar* ttv = getMutableTableType(exprTy); TableTypeVar* ttv = getMutableTableType(exprTy);
@ -1116,7 +1128,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
} }
else if (ttv->state == TableState::Sealed) else if (ttv->state == TableState::Sealed)
{ {
if (!ttv->indexer || !isPrim(ttv->indexer->indexType, PrimitiveTypeVar::String)) if (!getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, false))
reportError(TypeError{function.location, CannotExtendTable{exprTy, CannotExtendTable::Property, name->index.value}}); reportError(TypeError{function.location, CannotExtendTable{exprTy, CannotExtendTable::Property, name->index.value}});
} }
@ -1141,7 +1153,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
if (ttv && ttv->state != TableState::Sealed) if (ttv && ttv->state != TableState::Sealed)
ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation}; ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation};
} }
else if (FFlag::LuauStatFunctionSimplify2) else if (FFlag::LuauStatFunctionSimplify3)
{ {
LUAU_ASSERT(function.name->is<AstExprError>()); LUAU_ASSERT(function.name->is<AstExprError>());
@ -1151,7 +1163,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
} }
else if (function.func->self) else if (function.func->self)
{ {
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify2); LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify3);
AstExprIndexName* indexName = function.name->as<AstExprIndexName>(); AstExprIndexName* indexName = function.name->as<AstExprIndexName>();
if (!indexName) if (!indexName)
@ -1190,7 +1202,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
} }
else else
{ {
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify2); LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify3);
TypeId leftType = checkLValueBinding(scope, *function.name); TypeId leftType = checkLValueBinding(scope, *function.name);
@ -2985,7 +2997,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T
return errorRecoveryType(scope); return errorRecoveryType(scope);
} }
if (FFlag::LuauStatFunctionSimplify2) if (FFlag::LuauStatFunctionSimplify3)
{ {
if (lhsType->persistent) if (lhsType->persistent)
return errorRecoveryType(scope); return errorRecoveryType(scope);

View File

@ -130,8 +130,8 @@ ThreadContext& getThreadContext();
struct Scope struct Scope
{ {
explicit Scope(ThreadContext& context, uint16_t token) explicit Scope(uint16_t token)
: context(context) : context(getThreadContext())
{ {
if (!FFlag::DebugLuauTimeTracing) if (!FFlag::DebugLuauTimeTracing)
return; return;
@ -152,8 +152,8 @@ struct Scope
struct OptionalTailScope struct OptionalTailScope
{ {
explicit OptionalTailScope(ThreadContext& context, uint16_t token, uint32_t threshold) explicit OptionalTailScope(uint16_t token, uint32_t threshold)
: context(context) : context(getThreadContext())
, token(token) , token(token)
, threshold(threshold) , threshold(threshold)
{ {
@ -188,27 +188,27 @@ struct OptionalTailScope
uint32_t pos; uint32_t pos;
}; };
LUAU_NOINLINE std::pair<uint16_t, Luau::TimeTrace::ThreadContext&> createScopeData(const char* name, const char* category); LUAU_NOINLINE uint16_t createScopeData(const char* name, const char* category);
} // namespace TimeTrace } // namespace TimeTrace
} // namespace Luau } // namespace Luau
// Regular scope // Regular scope
#define LUAU_TIMETRACE_SCOPE(name, category) \ #define LUAU_TIMETRACE_SCOPE(name, category) \
static auto lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \ static uint16_t lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::Scope lttScope(lttScopeStatic.second, lttScopeStatic.first) Luau::TimeTrace::Scope lttScope(lttScopeStatic)
// A scope without nested scopes that may be skipped if the time it took is less than the threshold // A scope without nested scopes that may be skipped if the time it took is less than the threshold
#define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec) \ #define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec) \
static auto lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \ static uint16_t lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail.second, lttScopeStaticOptTail.first, microsec) Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail, microsec)
// Extra key/value data can be added to regular scopes // Extra key/value data can be added to regular scopes
#define LUAU_TIMETRACE_ARGUMENT(name, value) \ #define LUAU_TIMETRACE_ARGUMENT(name, value) \
do \ do \
{ \ { \
if (FFlag::DebugLuauTimeTracing) \ if (FFlag::DebugLuauTimeTracing) \
lttScopeStatic.second.eventArgument(name, value); \ lttScope.context.eventArgument(name, value); \
} while (false) } while (false)
#else #else

View File

@ -6,6 +6,8 @@
#include <limits.h> #include <limits.h>
LUAU_FASTFLAGVARIABLE(LuauParseLocationIgnoreCommentSkip, false)
namespace Luau namespace Luau
{ {
@ -352,6 +354,8 @@ const Lexeme& Lexer::next()
const Lexeme& Lexer::next(bool skipComments) const Lexeme& Lexer::next(bool skipComments)
{ {
bool first = true;
// in skipComments mode we reject valid comments // in skipComments mode we reject valid comments
do do
{ {
@ -359,9 +363,11 @@ const Lexeme& Lexer::next(bool skipComments)
while (isSpace(peekch())) while (isSpace(peekch()))
consume(); consume();
prevLocation = lexeme.location; if (!FFlag::LuauParseLocationIgnoreCommentSkip || first)
prevLocation = lexeme.location;
lexeme = readNext(); lexeme = readNext();
first = false;
} while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment)); } while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment));
return lexeme; return lexeme;

View File

@ -246,10 +246,9 @@ ThreadContext& getThreadContext()
return context; return context;
} }
std::pair<uint16_t, Luau::TimeTrace::ThreadContext&> createScopeData(const char* name, const char* category) uint16_t createScopeData(const char* name, const char* category)
{ {
uint16_t token = createToken(Luau::TimeTrace::getGlobalContext(), name, category); return createToken(Luau::TimeTrace::getGlobalContext(), name, category);
return {token, Luau::TimeTrace::getThreadContext()};
} }
} // namespace TimeTrace } // namespace TimeTrace
} // namespace Luau } // namespace Luau

View File

@ -376,8 +376,7 @@ enum LuauOpcode
enum LuauBytecodeTag enum LuauBytecodeTag
{ {
// Bytecode version // Bytecode version
LBC_VERSION = 1, LBC_VERSION = 2,
LBC_VERSION_FUTURE = 2, // TODO: This will be removed in favor of LBC_VERSION with LuauBytecodeV2Force
// Types of constant table entries // Types of constant table entries
LBC_CONSTANT_NIL = 0, LBC_CONSTANT_NIL = 0,
LBC_CONSTANT_BOOLEAN, LBC_CONSTANT_BOOLEAN,

View File

@ -508,7 +508,7 @@ uint32_t BytecodeBuilder::getDebugPC() const
void BytecodeBuilder::finalize() void BytecodeBuilder::finalize()
{ {
LUAU_ASSERT(bytecode.empty()); LUAU_ASSERT(bytecode.empty());
bytecode = char(LBC_VERSION_FUTURE); bytecode = char(LBC_VERSION);
writeStringTable(bytecode); writeStringTable(bytecode);

View File

@ -11,8 +11,6 @@
#include <string.h> #include <string.h>
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false)
/* convert a stack index to positive */ /* convert a stack index to positive */
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1) #define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
@ -337,15 +335,8 @@ const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
const char* luaL_typename(lua_State* L, int idx) const char* luaL_typename(lua_State* L, int idx)
{ {
if (DFFlag::LuauMorePreciseLuaLTypeName) const TValue* obj = luaA_toobject(L, idx);
{ return luaT_objtypename(L, obj);
const TValue* obj = luaA_toobject(L, idx);
return luaT_objtypename(L, obj);
}
else
{
return lua_typename(L, lua_type(L, idx));
}
} }
/* /*

View File

@ -11,8 +11,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName)
static void writestring(const char* s, size_t l) static void writestring(const char* s, size_t l)
{ {
fwrite(s, 1, l, stdout); fwrite(s, 1, l, stdout);
@ -189,31 +187,16 @@ static int luaB_gcinfo(lua_State* L)
static int luaB_type(lua_State* L) static int luaB_type(lua_State* L)
{ {
luaL_checkany(L, 1); luaL_checkany(L, 1);
if (DFFlag::LuauMorePreciseLuaLTypeName) /* resulting name doesn't differentiate between userdata types */
{ lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
/* resulting name doesn't differentiate between userdata types */
lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
}
else
{
lua_pushstring(L, luaL_typename(L, 1));
}
return 1; return 1;
} }
static int luaB_typeof(lua_State* L) static int luaB_typeof(lua_State* L)
{ {
luaL_checkany(L, 1); luaL_checkany(L, 1);
if (DFFlag::LuauMorePreciseLuaLTypeName) /* resulting name returns __type if specified unless the input is a newproxy-created userdata */
{ lua_pushstring(L, luaL_typename(L, 1));
/* resulting name returns __type if specified unless the input is a newproxy-created userdata */
lua_pushstring(L, luaL_typename(L, 1));
}
else
{
const TValue* obj = luaA_toobject(L, 1);
lua_pushstring(L, luaT_objtypename(L, obj));
}
return 1; return 1;
} }

View File

@ -12,8 +12,6 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
LUAU_FASTFLAG(LuauBytecodeV2Force)
static const char* getfuncname(Closure* f); static const char* getfuncname(Closure* f);
static int currentpc(lua_State* L, CallInfo* ci) static int currentpc(lua_State* L, CallInfo* ci)
@ -91,16 +89,6 @@ const char* lua_setlocal(lua_State* L, int level, int n)
return name; return name;
} }
static int getlinedefined(Proto* p)
{
if (FFlag::LuauBytecodeV2Force)
return p->linedefined;
else if (p->linedefined >= 0)
return p->linedefined;
else
return luaG_getline(p, 0);
}
static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, CallInfo* ci) static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, CallInfo* ci)
{ {
int status = 1; int status = 1;
@ -120,7 +108,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
{ {
ar->source = getstr(f->l.p->source); ar->source = getstr(f->l.p->source);
ar->what = "Lua"; ar->what = "Lua";
ar->linedefined = getlinedefined(f->l.p); ar->linedefined = f->l.p->linedefined;
} }
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
break; break;
@ -133,7 +121,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
} }
else else
{ {
ar->currentline = f->isC ? -1 : getlinedefined(f->l.p); ar->currentline = f->isC ? -1 : f->l.p->linedefined;
} }
break; break;
@ -424,7 +412,7 @@ static void getcoverage(Proto* p, int depth, int* buffer, size_t size, void* con
} }
const char* debugname = p->debugname ? getstr(p->debugname) : NULL; const char* debugname = p->debugname ? getstr(p->debugname) : NULL;
int linedefined = getlinedefined(p); int linedefined = p->linedefined;
callback(context, debugname, linedefined, depth, buffer, size); callback(context, debugname, linedefined, depth, buffer, size);

View File

@ -10,12 +10,12 @@ union GCObject;
#define luaM_newgco(L, t, size, memcat) cast_to(t*, luaM_newgco_(L, size, memcat)) #define luaM_newgco(L, t, size, memcat) cast_to(t*, luaM_newgco_(L, size, memcat))
#define luaM_freegco(L, p, size, memcat, page) luaM_freegco_(L, obj2gco(p), size, memcat, page) #define luaM_freegco(L, p, size, memcat, page) luaM_freegco_(L, obj2gco(p), size, memcat, page)
#define luaM_arraysize_(n, e) ((cast_to(size_t, (n)) <= SIZE_MAX / (e)) ? (n) * (e) : (luaM_toobig(L), SIZE_MAX)) #define luaM_arraysize_(L, n, e) ((cast_to(size_t, (n)) <= SIZE_MAX / (e)) ? (n) * (e) : (luaM_toobig(L), SIZE_MAX))
#define luaM_newarray(L, n, t, memcat) cast_to(t*, luaM_new_(L, luaM_arraysize_(n, sizeof(t)), memcat)) #define luaM_newarray(L, n, t, memcat) cast_to(t*, luaM_new_(L, luaM_arraysize_(L, n, sizeof(t)), memcat))
#define luaM_freearray(L, b, n, t, memcat) luaM_free_(L, (b), (n) * sizeof(t), memcat) #define luaM_freearray(L, b, n, t, memcat) luaM_free_(L, (b), (n) * sizeof(t), memcat)
#define luaM_reallocarray(L, v, oldn, n, t, memcat) \ #define luaM_reallocarray(L, v, oldn, n, t, memcat) \
((v) = cast_to(t*, luaM_realloc_(L, v, (oldn) * sizeof(t), luaM_arraysize_(n, sizeof(t)), memcat))) ((v) = cast_to(t*, luaM_realloc_(L, v, (oldn) * sizeof(t), luaM_arraysize_(L, n, sizeof(t)), memcat)))
LUAI_FUNC void* luaM_new_(lua_State* L, size_t nsize, uint8_t memcat); LUAI_FUNC void* luaM_new_(lua_State* L, size_t nsize, uint8_t memcat);
LUAI_FUNC GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat); LUAI_FUNC GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat);

View File

@ -2,17 +2,26 @@
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details // This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
/* /*
** Implementation of tables (aka arrays, objects, or hash tables). * Implementation of tables (aka arrays, objects, or hash tables).
** Tables keep its elements in two parts: an array part and a hash part. *
** Non-negative integer keys are all candidates to be kept in the array * Tables keep the elements in two parts: an array part and a hash part.
** part. The actual size of the array is the largest `n' such that at * Integer keys >=1 are all candidates to be kept in the array part. The actual size of the array is the
** least half the slots between 0 and n are in use. * largest n such that at least half the slots between 0 and n are in use.
** Hash uses a mix of chained scatter table with Brent's variation. * Hash uses a mix of chained scatter table with Brent's variation.
** A main invariant of these tables is that, if an element is not *
** in its main position (i.e. the `original' position that its hash gives * A main invariant of these tables is that, if an element is not in its main position (i.e. the original
** to it), then the colliding element is in its own main position. * position that its hash gives to it), then the colliding element is in its own main position.
** Hence even when the load factor reaches 100%, performance remains good. * Hence even when the load factor reaches 100%, performance remains good.
*/ *
* Table keys can be arbitrary values unless they contain NaN. Keys are hashed and compared using raw equality,
* so even if the key is a userdata with an overridden __eq, it's not used during hash lookups.
*
* Each table has a "boundary", defined as the index k where t[k] ~= nil and t[k+1] == nil. The boundary can be
* computed using a binary search and can be adjusted when the table is modified; crucially, Luau enforces an
* invariant where the boundary must be in the array part - this enforces a consistent iteration order through the
* prefix of the table when using pairs(), and allows to implement algorithms that access elements in 1..#t range
* more efficiently.
*/
#include "ltable.h" #include "ltable.h"
@ -25,6 +34,7 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false) LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary, false)
// max size of both array and hash part is 2^MAXBITS // max size of both array and hash part is 2^MAXBITS
#define MAXBITS 26 #define MAXBITS 26
@ -460,7 +470,20 @@ static void rehash(lua_State* L, Table* t, const TValue* ek)
totaluse++; totaluse++;
/* compute new size for array part */ /* compute new size for array part */
int na = computesizes(nums, &nasize); int na = computesizes(nums, &nasize);
/* enforce the boundary invariant; for performance, only do hash lookups if we must */
if (FFlag::LuauTableNewBoundary)
{
bool tbound = t->node != dummynode || nasize < t->sizearray;
int ekindex = ttisnumber(ek) ? arrayindex(nvalue(ek)) : -1;
/* move the array size up until the boundary is guaranteed to be inside the array part */
while (nasize + 1 == ekindex || (tbound && !ttisnil(luaH_getnum(t, nasize + 1))))
{
nasize++;
na++;
}
}
/* resize the table to new computed sizes */ /* resize the table to new computed sizes */
LUAU_ASSERT(na <= totaluse);
resize(L, t, nasize, totaluse - na); resize(L, t, nasize, totaluse - na);
} }
@ -520,10 +543,18 @@ static LuaNode* getfreepos(Table* t)
*/ */
static TValue* newkey(lua_State* L, Table* t, const TValue* key) static TValue* newkey(lua_State* L, Table* t, const TValue* key)
{ {
/* enforce boundary invariant */
if (FFlag::LuauTableNewBoundary && ttisnumber(key) && nvalue(key) == t->sizearray + 1)
{
rehash(L, t, key); /* grow table */
// after rehash, numeric keys might be located in the new array part, but won't be found in the node part
return arrayornewkey(L, t, key);
}
LuaNode* mp = mainposition(t, key); LuaNode* mp = mainposition(t, key);
if (!ttisnil(gval(mp)) || mp == dummynode) if (!ttisnil(gval(mp)) || mp == dummynode)
{ {
LuaNode* othern;
LuaNode* n = getfreepos(t); /* get a free place */ LuaNode* n = getfreepos(t); /* get a free place */
if (n == NULL) if (n == NULL)
{ /* cannot find a free place? */ { /* cannot find a free place? */
@ -542,7 +573,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
LUAU_ASSERT(n != dummynode); LUAU_ASSERT(n != dummynode);
TValue mk; TValue mk;
getnodekey(L, &mk, mp); getnodekey(L, &mk, mp);
othern = mainposition(t, &mk); LuaNode* othern = mainposition(t, &mk);
if (othern != mp) if (othern != mp)
{ /* is colliding node out of its main position? */ { /* is colliding node out of its main position? */
/* yes; move colliding node into free position */ /* yes; move colliding node into free position */
@ -704,6 +735,7 @@ TValue* luaH_setstr(lua_State* L, Table* t, TString* key)
static LUAU_NOINLINE int unbound_search(Table* t, unsigned int j) static LUAU_NOINLINE int unbound_search(Table* t, unsigned int j)
{ {
LUAU_ASSERT(!FFlag::LuauTableNewBoundary);
unsigned int i = j; /* i is zero or a present index */ unsigned int i = j; /* i is zero or a present index */
j++; j++;
/* find `i' and `j' such that i is present and j is not */ /* find `i' and `j' such that i is present and j is not */
@ -788,6 +820,12 @@ int luaH_getn(Table* t)
maybesetaboundary(t, boundary); maybesetaboundary(t, boundary);
return boundary; return boundary;
} }
else if (FFlag::LuauTableNewBoundary)
{
/* validate boundary invariant */
LUAU_ASSERT(t->node == dummynode || ttisnil(luaH_getnum(t, j + 1)));
return j;
}
/* else must find a boundary in hash part */ /* else must find a boundary in hash part */
else if (t->node == dummynode) /* hash part is empty? */ else if (t->node == dummynode) /* hash part is empty? */
return j; /* that is easy... */ return j; /* that is easy... */

View File

@ -10,7 +10,9 @@
#include "ldebug.h" #include "ldebug.h"
#include "lvm.h" #include "lvm.h"
LUAU_FASTFLAGVARIABLE(LuauTableClone, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauTableMoveTelemetry2, false)
void (*lua_table_move_telemetry)(lua_State* L, int f, int e, int t, int nf, int nt);
static int foreachi(lua_State* L) static int foreachi(lua_State* L)
{ {
@ -197,6 +199,29 @@ static int tmove(lua_State* L)
int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
luaL_checktype(L, tt, LUA_TTABLE); luaL_checktype(L, tt, LUA_TTABLE);
void (*telemetrycb)(lua_State* L, int f, int e, int t, int nf, int nt) = lua_table_move_telemetry;
if (DFFlag::LuauTableMoveTelemetry2 && telemetrycb)
{
int nf = lua_objlen(L, 1);
int nt = lua_objlen(L, tt);
bool report = false;
// source index range must be in bounds in source table unless the table is empty (permits 1..#t moves)
if (!(f == 1 || (f >= 1 && f <= nf)))
report = true;
if (!(e == nf || (e >= 1 && e <= nf)))
report = true;
// destination index must be in bounds in dest table or be exactly at the first empty element (permits concats)
if (!(t == nt + 1 || (t >= 1 && t <= nt)))
report = true;
if (report)
telemetrycb(L, f, e, t, nf, nt);
}
if (e >= f) if (e >= f)
{ /* otherwise, nothing to move */ { /* otherwise, nothing to move */
luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements to move"); luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements to move");
@ -512,9 +537,6 @@ static int tisfrozen(lua_State* L)
static int tclone(lua_State* L) static int tclone(lua_State* L)
{ {
if (!FFlag::LuauTableClone)
luaG_runerror(L, "table.clone is not available");
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable"); luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");

View File

@ -16,6 +16,8 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAG(LuauTableNewBoundary)
// Disable c99-designator to avoid the warning in CGOTO dispatch table // Disable c99-designator to avoid the warning in CGOTO dispatch table
#ifdef __clang__ #ifdef __clang__
#if __has_warning("-Wc99-designator") #if __has_warning("-Wc99-designator")
@ -2266,9 +2268,9 @@ static void luau_execute(lua_State* L)
VM_NEXT(); VM_NEXT();
} }
} }
else if (h->lsizenode == 0 && ttisnil(gval(h->node))) else if (FFlag::LuauTableNewBoundary || (h->lsizenode == 0 && ttisnil(gval(h->node))))
{ {
// hash part is empty: fallthrough to exit // fallthrough to exit
VM_NEXT(); VM_NEXT();
} }
else else

View File

@ -13,8 +13,6 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauBytecodeV2Force, false)
// TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens // TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens
template<typename T> template<typename T>
struct TempBuffer struct TempBuffer
@ -156,12 +154,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
return 1; return 1;
} }
if (FFlag::LuauBytecodeV2Force ? (version != LBC_VERSION_FUTURE) : (version != LBC_VERSION && version != LBC_VERSION_FUTURE)) if (version != LBC_VERSION)
{ {
char chunkid[LUA_IDSIZE]; char chunkid[LUA_IDSIZE];
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE); luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, LBC_VERSION, version);
FFlag::LuauBytecodeV2Force ? LBC_VERSION_FUTURE : LBC_VERSION, version);
return 1; return 1;
} }
@ -292,11 +289,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
p->p[j] = protos[fid]; p->p[j] = protos[fid];
} }
if (FFlag::LuauBytecodeV2Force || version == LBC_VERSION_FUTURE) p->linedefined = readVarInt(data, size, offset);
p->linedefined = readVarInt(data, size, offset);
else
p->linedefined = -1;
p->debugname = readString(strings, data, size, offset); p->debugname = readString(strings, data, size, offset);
uint8_t lineinfo = read<uint8_t>(data, size, offset); uint8_t lineinfo = read<uint8_t>(data, size, offset);

View File

@ -14,7 +14,6 @@
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2) LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel) LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTFLAG(LuauTableCloneType)
using namespace Luau; using namespace Luau;
@ -262,7 +261,7 @@ TEST_CASE_FIXTURE(ACFixture, "get_member_completions")
auto ac = autocomplete('1'); auto ac = autocomplete('1');
CHECK_EQ(FFlag::LuauTableCloneType ? 17 : 16, ac.entryMap.size()); CHECK_EQ(17, ac.entryMap.size());
CHECK(ac.entryMap.count("find")); CHECK(ac.entryMap.count("find"));
CHECK(ac.entryMap.count("pack")); CHECK(ac.entryMap.count("pack"));
CHECK(!ac.entryMap.count("math")); CHECK(!ac.entryMap.count("math"));
@ -2221,7 +2220,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocompleteSource")
auto ac = autocompleteSource(frontend, source, Position{1, 24}, nullCallback).result; auto ac = autocompleteSource(frontend, source, Position{1, 24}, nullCallback).result;
CHECK_EQ(FFlag::LuauTableCloneType ? 17 : 16, ac.entryMap.size()); CHECK_EQ(17, ac.entryMap.size());
CHECK(ac.entryMap.count("find")); CHECK(ac.entryMap.count("find"));
CHECK(ac.entryMap.count("pack")); CHECK(ac.entryMap.count("pack"));
CHECK(!ac.entryMap.count("math")); CHECK(!ac.entryMap.count("math"));

View File

@ -241,8 +241,6 @@ TEST_CASE("Math")
TEST_CASE("Table") TEST_CASE("Table")
{ {
ScopedFastFlag sff("LuauTableClone", true);
runConformance("nextvar.lua"); runConformance("nextvar.lua");
} }
@ -467,8 +465,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
TEST_CASE("Types") TEST_CASE("Types")
{ {
ScopedFastFlag sff("LuauTableCloneType", true);
runConformance("types.lua", [](lua_State* L) { runConformance("types.lua", [](lua_State* L) {
Luau::NullModuleResolver moduleResolver; Luau::NullModuleResolver moduleResolver;
Luau::InternalErrorReporter iceHandler; Luau::InternalErrorReporter iceHandler;

View File

@ -1604,6 +1604,20 @@ TEST_CASE_FIXTURE(Fixture, "end_extent_of_functions_unions_and_intersections")
CHECK_EQ((Position{3, 42}), block->body.data[2]->location.end); CHECK_EQ((Position{3, 42}), block->body.data[2]->location.end);
} }
TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments")
{
ScopedFastFlag luauParseLocationIgnoreCommentSkip{"LuauParseLocationIgnoreCommentSkip", true};
AstStatBlock* block = parse(R"(
type F = number
--comment
print('hello')
)");
REQUIRE_EQ(2, block->body.size);
CHECK_EQ((Position{1, 23}), block->body.data[0]->location.end);
}
TEST_CASE_FIXTURE(Fixture, "parse_error_loop_control") TEST_CASE_FIXTURE(Fixture, "parse_error_loop_control")
{ {
matchParseError("break", "break statement must be inside a loop"); matchParseError("break", "break statement must be inside a loop");

View File

@ -1270,7 +1270,7 @@ caused by:
TEST_CASE_FIXTURE(Fixture, "function_decl_quantify_right_type") TEST_CASE_FIXTURE(Fixture, "function_decl_quantify_right_type")
{ {
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true}; ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
fileResolver.source["game/isAMagicMock"] = R"( fileResolver.source["game/isAMagicMock"] = R"(
--!nonstrict --!nonstrict
@ -1294,7 +1294,7 @@ end
TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite") TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite")
{ {
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true}; ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
CheckResult result = check(R"( CheckResult result = check(R"(
function string.len(): number function string.len(): number
@ -1302,7 +1302,40 @@ function string.len(): number
end end
)"); )");
LUAU_REQUIRE_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
// if 'string' library property was replaced with an internal module type, it will be freed and the next check will crash
frontend.clear();
result = check(R"(
print(string.len('hello'))
)");
LUAU_REQUIRE_NO_ERRORS(result);
}
TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite_2")
{
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
ScopedFastFlag inferStatFunction{"LuauInferStatFunction", true};
CheckResult result = check(R"(
local t: { f: ((x: number) -> number)? } = {}
function t.f(x)
print(x + 5)
return x .. "asd"
end
t.f = function(x)
print(x + 5)
return x .. "asd"
end
)");
LUAU_REQUIRE_ERROR_COUNT(2, result);
CHECK_EQ(toString(result.errors[0]), R"(Type 'string' could not be converted into 'number')");
CHECK_EQ(toString(result.errors[1]), R"(Type 'string' could not be converted into 'number')");
} }
TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments") TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
@ -1319,7 +1352,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
TEST_CASE_FIXTURE(Fixture, "function_statement_sealed_table_assignment_through_indexer") TEST_CASE_FIXTURE(Fixture, "function_statement_sealed_table_assignment_through_indexer")
{ {
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true}; ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
CheckResult result = check(R"( CheckResult result = check(R"(
local t: {[string]: () -> number} = {} local t: {[string]: () -> number} = {}

View File

@ -550,4 +550,35 @@ do
assert(not pcall(table.clone, 42)) assert(not pcall(table.clone, 42))
end end
-- test boundary invariant maintenance during rehash
do
local arr = table.create(5, 42)
arr[1] = nil
arr.a = 'a' -- trigger rehash
assert(#arr == 5) -- technically 0 is also valid, but it happens to be 5 because array capacity is 5
end
-- test boundary invariant maintenance when replacing hash keys
do
local arr = {}
arr.a = 'a'
arr.a = nil
arr[1] = 1 -- should rehash and resize array part, otherwise # won't find the boundary in array part
assert(#arr == 1)
end
-- test boundary invariant maintenance when table is filled from the end
do
local arr = {}
for i=5,2,-1 do
arr[i] = i
assert(#arr == 0)
end
arr[1] = 1
assert(#arr == 5)
end
return"OK" return"OK"