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>
LUAU_FASTFLAG(LuauAssertStripsFalsyTypes)
LUAU_FASTFLAGVARIABLE(LuauTableCloneType, false)
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
/** 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
ttv->props["freeze"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.freeze");
if (FFlag::LuauTableCloneType)
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
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(LuauRecursiveTypeParameterRestriction, false)
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
LUAU_FASTFLAGVARIABLE(LuauInferStatFunction, false)
LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix, false)
LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false)
@ -34,7 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify2, false)
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify3, false)
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
LUAU_FASTFLAG(LuauTypeMismatchModuleName)
LUAU_FASTFLAGVARIABLE(LuauTwoPassAliasDefinitionFix, false)
@ -463,7 +464,18 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block)
}
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;
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};
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;
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)
{
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}});
}
@ -1141,7 +1153,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
if (ttv && ttv->state != TableState::Sealed)
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>());
@ -1151,7 +1163,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
}
else if (function.func->self)
{
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify2);
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify3);
AstExprIndexName* indexName = function.name->as<AstExprIndexName>();
if (!indexName)
@ -1190,7 +1202,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
}
else
{
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify2);
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify3);
TypeId leftType = checkLValueBinding(scope, *function.name);
@ -2985,7 +2997,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T
return errorRecoveryType(scope);
}
if (FFlag::LuauStatFunctionSimplify2)
if (FFlag::LuauStatFunctionSimplify3)
{
if (lhsType->persistent)
return errorRecoveryType(scope);

View file

@ -130,8 +130,8 @@ ThreadContext& getThreadContext();
struct Scope
{
explicit Scope(ThreadContext& context, uint16_t token)
: context(context)
explicit Scope(uint16_t token)
: context(getThreadContext())
{
if (!FFlag::DebugLuauTimeTracing)
return;
@ -152,8 +152,8 @@ struct Scope
struct OptionalTailScope
{
explicit OptionalTailScope(ThreadContext& context, uint16_t token, uint32_t threshold)
: context(context)
explicit OptionalTailScope(uint16_t token, uint32_t threshold)
: context(getThreadContext())
, token(token)
, threshold(threshold)
{
@ -188,27 +188,27 @@ struct OptionalTailScope
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 Luau
// Regular scope
#define LUAU_TIMETRACE_SCOPE(name, category) \
static auto lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::Scope lttScope(lttScopeStatic.second, lttScopeStatic.first)
static uint16_t lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::Scope lttScope(lttScopeStatic)
// 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) \
static auto lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail.second, lttScopeStaticOptTail.first, microsec)
static uint16_t lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail, microsec)
// Extra key/value data can be added to regular scopes
#define LUAU_TIMETRACE_ARGUMENT(name, value) \
do \
{ \
if (FFlag::DebugLuauTimeTracing) \
lttScopeStatic.second.eventArgument(name, value); \
lttScope.context.eventArgument(name, value); \
} while (false)
#else

View file

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

View file

@ -246,10 +246,9 @@ ThreadContext& getThreadContext()
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 {token, Luau::TimeTrace::getThreadContext()};
return createToken(Luau::TimeTrace::getGlobalContext(), name, category);
}
} // namespace TimeTrace
} // namespace Luau

View file

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

View file

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

View file

@ -11,8 +11,6 @@
#include <string.h>
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false)
/* convert a stack index to positive */
#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)
{
if (DFFlag::LuauMorePreciseLuaLTypeName)
{
const TValue* obj = luaA_toobject(L, idx);
return luaT_objtypename(L, obj);
}
else
{
return lua_typename(L, lua_type(L, idx));
}
const TValue* obj = luaA_toobject(L, idx);
return luaT_objtypename(L, obj);
}
/*

View file

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

View file

@ -12,8 +12,6 @@
#include <string.h>
#include <stdio.h>
LUAU_FASTFLAG(LuauBytecodeV2Force)
static const char* getfuncname(Closure* f);
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;
}
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)
{
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->what = "Lua";
ar->linedefined = getlinedefined(f->l.p);
ar->linedefined = f->l.p->linedefined;
}
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
break;
@ -133,7 +121,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
}
else
{
ar->currentline = f->isC ? -1 : getlinedefined(f->l.p);
ar->currentline = f->isC ? -1 : f->l.p->linedefined;
}
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;
int linedefined = getlinedefined(p);
int linedefined = p->linedefined;
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_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_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 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
/*
** 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
** part. The actual size of the array is the 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.
** 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
** to it), then the colliding element is in its own main position.
** Hence even when the load factor reaches 100%, performance remains good.
*/
* Implementation of tables (aka arrays, objects, or hash tables).
*
* Tables keep the elements in two parts: an array part and a hash part.
* Integer keys >=1 are all candidates to be kept in the array part. The actual size of the array is the
* 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.
*
* 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 to it), then the colliding element is in its own main position.
* 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"
@ -25,6 +34,7 @@
#include <string.h>
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary, false)
// max size of both array and hash part is 2^MAXBITS
#define MAXBITS 26
@ -460,7 +470,20 @@ static void rehash(lua_State* L, Table* t, const TValue* ek)
totaluse++;
/* compute new size for array part */
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 */
LUAU_ASSERT(na <= totaluse);
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)
{
/* 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);
if (!ttisnil(gval(mp)) || mp == dummynode)
{
LuaNode* othern;
LuaNode* n = getfreepos(t); /* get a free place */
if (n == NULL)
{ /* cannot find a free place? */
@ -542,7 +573,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
LUAU_ASSERT(n != dummynode);
TValue mk;
getnodekey(L, &mk, mp);
othern = mainposition(t, &mk);
LuaNode* othern = mainposition(t, &mk);
if (othern != mp)
{ /* is colliding node out of its main 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)
{
LUAU_ASSERT(!FFlag::LuauTableNewBoundary);
unsigned int i = j; /* i is zero or a present index */
j++;
/* 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);
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 if (t->node == dummynode) /* hash part is empty? */
return j; /* that is easy... */

View file

@ -10,7 +10,9 @@
#include "ldebug.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)
{
@ -197,6 +199,29 @@ static int tmove(lua_State* L)
int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
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)
{ /* otherwise, nothing 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)
{
if (!FFlag::LuauTableClone)
luaG_runerror(L, "table.clone is not available");
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");

View file

@ -16,6 +16,8 @@
#include <string.h>
LUAU_FASTFLAG(LuauTableNewBoundary)
// Disable c99-designator to avoid the warning in CGOTO dispatch table
#ifdef __clang__
#if __has_warning("-Wc99-designator")
@ -2266,9 +2268,9 @@ static void luau_execute(lua_State* L)
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();
}
else

View file

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

View file

@ -14,7 +14,6 @@
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
LUAU_FASTFLAG(LuauTableCloneType)
using namespace Luau;
@ -262,7 +261,7 @@ TEST_CASE_FIXTURE(ACFixture, "get_member_completions")
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("pack"));
CHECK(!ac.entryMap.count("math"));
@ -2221,7 +2220,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocompleteSource")
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("pack"));
CHECK(!ac.entryMap.count("math"));

View file

@ -241,8 +241,6 @@ TEST_CASE("Math")
TEST_CASE("Table")
{
ScopedFastFlag sff("LuauTableClone", true);
runConformance("nextvar.lua");
}
@ -467,8 +465,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
TEST_CASE("Types")
{
ScopedFastFlag sff("LuauTableCloneType", true);
runConformance("types.lua", [](lua_State* L) {
Luau::NullModuleResolver moduleResolver;
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);
}
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")
{
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")
{
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true};
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
fileResolver.source["game/isAMagicMock"] = R"(
--!nonstrict
@ -1294,7 +1294,7 @@ end
TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite")
{
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true};
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
CheckResult result = check(R"(
function string.len(): number
@ -1302,7 +1302,40 @@ function string.len(): number
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")
@ -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")
{
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true};
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
CheckResult result = check(R"(
local t: {[string]: () -> number} = {}

View file

@ -550,4 +550,35 @@ do
assert(not pcall(table.clone, 42))
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"