Luau "attempt to yield across metamethod/C-call boundary" error with generic `for` loop

13 views Asked by At

I'm coding a Luau producer/consumer pattern, where the consumer runs in a coroutine. I was getting "attempt to yield across metamethod/C-call boundary" errors despite the fact that my code uses no metamethods and no C calls (of mine anyway). Code:

-- wrap coroutine.resume() to expose errors in resumed coroutine
function resume(...)
    ok_result = table.pack(coroutine.resume(...))
    if not ok_result[1] then
        error(ok_result[2])
    end
    return table.unpack(ok_result, 2)
end

-- loop using generic 'for'; should work, yes?
function consumer()
    print('generic for loop')
    for item in coroutine.yield do
        print(item)
    end
    print('generic for done')
end

array = {'loop', 'over', 'yield', 'calls'}

-- just pump array into consumer coroutine
conco = coroutine.create(consumer)
-- run conco up to first yield()
resume(conco)
for _, word in pairs(array) do
    resume(conco, word)
end
-- finish with nil to exit the consumer loop
resume(conco)
coroutine.close(conco)
1

There are 1 answers

0
Nat Goodspeed On

After considerable flailing, I discovered that this change works:

-- straightforward consumer while loop
function consumer()
    print('two explicit yield() calls')
    item = coroutine.yield()
    while item do
        print(item)
        item = coroutine.yield()
    end
    print('two explicit yield() calls done')
end

Tentative conclusion: using a Luau generic for loop stitches an internal C function call into the call of the iterator function?

That was so opaque that it seems worth sharing the discovery.