From 9386e49a3173b68e8b5a7ba882c4c2faf557b61e Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 16 Jun 2025 16:29:32 -0300 Subject: New metatable in an all-weak table can fool the GC All-weak tables are not being revisited after being visited during propagation; if it gets a new metatable after that, the new metatable may not be marked. --- lgc.c | 7 +++++-- testes/gc.lua | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lgc.c b/lgc.c index e4cbcf0c..bbaa5ff7 100644 --- a/lgc.c +++ b/lgc.c @@ -617,8 +617,11 @@ static l_mem traversetable (global_State *g, Table *h) { case 2: /* weak keys */ traverseephemeron(g, h, 0); break; - case 3: /* all weak */ - linkgclist(h, g->allweak); /* nothing to traverse now */ + case 3: /* all weak; nothing to traverse */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must visit again its metatable */ + else + linkgclist(h, g->allweak); /* must clear collected entries */ break; } return 1 + 2*sizenode(h) + h->asize; diff --git a/testes/gc.lua b/testes/gc.lua index 5d2b3085..62713dac 100644 --- a/testes/gc.lua +++ b/testes/gc.lua @@ -294,6 +294,16 @@ do -- invalid mode end +if T then -- bug since 5.3: all-weak tables are not being revisited + T.gcstate("propagate") + local t = setmetatable({}, {__mode = "kv"}) + T.gcstate("enteratomic") -- 't' was visited + setmetatable(t, {__mode = "kv"}) + T.gcstate("pause") -- its new metatable is not being visited + assert(getmetatable(t).__mode == "kv") +end + + -- 'bug' in 5.1 a = {} local t = {x = 10} -- cgit v1.2.3-55-g6feb