From a2860e803893cfaf0acd93df5dd69a2b5b4a5827 Mon Sep 17 00:00:00 2001 From: Stefan Seifert Date: Sat, 5 Nov 2022 12:04:48 +0100 Subject: [PATCH] RakuAST: Add potential infinite loop in BEGIN time call with nested blocks Creating QAST for a block did not mean that we would generate QAST for nested blocks. The reason is that these blocks are not called directly, but instead their meta object gets closure cloned. Creating the meta object does not require immediate creation of the QAST. We just stub code objects instead. This means that if the outer block gets called at BEGIN time, the inner block may not have been linked yet to its QAST::Block. This in turn results in the clone not being registered in the original's clone list as they are linked via the CUID which comes from the QAST::Block. Since the clone is not known, calling the clone would have lead to dynamic compilation when its stub was called, but this compilation only set the $!do of the original code object, not the clone's. Thus when the stub then tried to forward the call to the actual code object, we'd end up in the stub again, hence in an infinite loop. --- src/Raku/ast/code.rakumod | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Raku/ast/code.rakumod b/src/Raku/ast/code.rakumod index 44f83d24caa..51808b350e0 100644 --- a/src/Raku/ast/code.rakumod +++ b/src/Raku/ast/code.rakumod @@ -699,6 +699,9 @@ class RakuAST::Block is RakuAST::LexicalScope is RakuAST::Term is RakuAST::Code else { # Not immediate, so already produced as a declaration above; just # closure clone it. Only invoke if it's a bare block. + # Ensure the block is linked when our outer block gets cloned before + # our IMPL-QAST-DECL-CODE is called. + self.IMPL-QAST-BLOCK($context, :blocktype('declaration_static')); my $ast := self.IMPL-CLOSURE-QAST($context); self.bare-block ?? QAST::Op.new( :op('call'), $ast )