From 9d4cd1169abac9e6194fb453a30e073bff3f5331 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Mon, 13 Apr 2026 16:32:35 +1000 Subject: [PATCH 1/3] fix: named imports under STORE_FAST --- ASTree.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ASTree.cpp b/ASTree.cpp index 6635808e1..846da4227 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -2153,6 +2153,10 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) && !curblock->inited()) { curblock.cast()->setExpr(value); curblock.cast()->setVar(name); + } else if (stack.top().type() == ASTNode::NODE_IMPORT) { + PycRef import = stack.top().cast(); + + import->add_store(new ASTStore(value, name)); } else if (value.type() == ASTNode::NODE_CHAINSTORE) { append_to_chain_store(value, name, stack, curblock); } else { From 6edc5ec1666651c0fcc6cf303ab4e06b444c4b87 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Mon, 13 Apr 2026 17:55:15 +1000 Subject: [PATCH 2/3] fix: CALL_FUNCTION_KW on >=3.9 --- ASTree.cpp | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ASTree.cpp b/ASTree.cpp index 846da4227..36a1271b5 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -555,24 +555,45 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) { PycRef kw = stack.top(); stack.pop(); - int kwparams = (operand & 0xFF00) >> 8; - int pparams = (operand & 0xFF); ASTCall::kwparam_t kwparamList; ASTCall::pparam_t pparamList; - for (int i=0; i val = stack.top(); - stack.pop(); - PycRef key = stack.top(); - stack.pop(); - kwparamList.push_front(std::make_pair(key, val)); + int kwparams, pparams; + if (mod->verCompare(3, 9) >= 0) { + if (kw.type() != ASTNode::NODE_OBJECT) { + fprintf(stderr, "Something TERRIBLE happened!!\n"); + break; + } + PycRef kw_obj = kw.cast()->object(); + if (kw_obj.type() != PycObject::TYPE_TUPLE && kw_obj.type() != PycObject::TYPE_SMALL_TUPLE) { + fprintf(stderr, "Something TERRIBLE happened!!\n"); + break; + } + const auto& kw_names = kw_obj.cast()->values(); + kwparams = static_cast(kw_names.size()); + pparams = operand - kwparams; + for (int i = kwparams - 1; i >= 0; i--) { + PycRef val = stack.top(); + stack.pop(); + PycRef key = new ASTObject(kw_names[i]); + kwparamList.push_front(std::make_pair(key, val)); + } + } else { + kwparams = (operand & 0xFF00) >> 8; + pparams = (operand & 0xFF); + for (int i = 0; i < kwparams; i++) { + PycRef val = stack.top(); + stack.pop(); + PycRef key = stack.top(); + stack.pop(); + kwparamList.push_front(std::make_pair(key, val)); + } } - for (int i=0; i func = stack.top(); stack.pop(); - PycRef call = new ASTCall(func, pparamList, kwparamList); call.cast()->setKW(kw); stack.push(call); @@ -588,14 +609,14 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) int pparams = (operand & 0xFF); ASTCall::kwparam_t kwparamList; ASTCall::pparam_t pparamList; - for (int i=0; i val = stack.top(); stack.pop(); PycRef key = stack.top(); stack.pop(); kwparamList.push_front(std::make_pair(key, val)); } - for (int i=0; i Date: Mon, 13 Apr 2026 18:12:18 +1000 Subject: [PATCH 3/3] fix: inline generator expressions --- ASTNode.h | 7 +++-- ASTree.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 7 deletions(-) diff --git a/ASTNode.h b/ASTNode.h index 98760dbf5..e4b3aa7d6 100644 --- a/ASTNode.h +++ b/ASTNode.h @@ -642,13 +642,15 @@ class ASTWithBlock : public ASTBlock { class ASTComprehension : public ASTNode { public: + enum ComprehensionType { LISTCOMP, GENEXPR }; typedef std::list> generator_t; - ASTComprehension(PycRef result) - : ASTNode(NODE_COMPREHENSION), m_result(std::move(result)) { } + ASTComprehension(PycRef result, ComprehensionType type = LISTCOMP) + : ASTNode(NODE_COMPREHENSION), m_result(std::move(result)), m_comptype(type) { } PycRef result() const { return m_result; } generator_t generators() const { return m_generators; } + ComprehensionType comprehensionType() const { return m_comptype; } void addGenerator(PycRef gen) { m_generators.emplace_front(std::move(gen)); @@ -657,6 +659,7 @@ class ASTComprehension : public ASTNode { private: PycRef m_result; generator_t m_generators; + ComprehensionType m_comptype; }; diff --git a/ASTree.cpp b/ASTree.cpp index 36a1271b5..34c0e56a9 100644 --- a/ASTree.cpp +++ b/ASTree.cpp @@ -521,6 +521,58 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) stack.pop(); } + // Detect inline call and convert to ASTComprehension + if (func.type() == ASTNode::NODE_FUNCTION + && kwparamList.empty() + && pparamList.size() == 1) { + PycRef fun_code_node = func.cast()->code(); + if (fun_code_node.type() == ASTNode::NODE_OBJECT) { + PycRef fun_code_obj = fun_code_node.cast()->object(); + if (fun_code_obj.type() == PycObject::TYPE_CODE + || fun_code_obj.type() == PycObject::TYPE_CODE2) { + PycRef genexpr_code = fun_code_obj.cast(); + bool is_genexpr = (genexpr_code->flags() & PycCode::CO_GENERATOR) + && genexpr_code->argCount() == 1 + && genexpr_code->numLocals() >= 1 + && strcmp(genexpr_code->getLocal(0)->value(), ".0") == 0; + if (is_genexpr) { + PycRef actual_iter = pparamList.front(); + PycRef genexpr_ast = BuildFromCode(genexpr_code, mod); + PycRef genexpr_nodes = genexpr_ast.cast(); + if (!genexpr_nodes->nodes().empty()) { + PycRef last_node = genexpr_nodes->nodes().back(); + if (last_node.type() == ASTNode::NODE_RETURN) { + PycRef ret_val = last_node.cast()->value(); + if (ret_val.type() == ASTNode::NODE_COMPREHENSION + && ret_val.cast()->comprehensionType() + == ASTComprehension::GENEXPR) { + PycRef inner = ret_val.cast(); + PycRef result = new ASTComprehension( + inner->result(), ASTComprehension::GENEXPR); + // Rebuild generators, replacing the implicit .0 iter + // of the outermost for-clause with the actual argument. + bool outermost = true; + for (const auto& gen : inner->generators()) { + PycRef gen_iter = outermost ? actual_iter : gen->iter(); + PycRef new_gen = new ASTIterBlock( + gen->blktype(), gen->start(), gen->end(), gen_iter); + new_gen->setIndex(gen->index()); + new_gen->setComprehension(gen->isComprehension()); + if (gen->condition()) + new_gen->setCondition(gen->condition()); + result->addGenerator(new_gen); + outermost = false; + } + stack.push(result.cast()); + break; + } + } + } + } + } + } + } + stack.push(new ASTCall(func, pparamList, kwparamList)); } break; @@ -936,7 +988,11 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) if (mod->verCompare(3, 10) >= 0) end *= sizeof(uint16_t); // // BPO-27129 end += pos; - comprehension = strcmp(code->name()->value(), "") == 0; + bool is_genexpr = (code->flags() & PycCode::CO_GENERATOR) + && code->argCount() >= 1 + && code->numLocals() >= 1 + && strcmp(code->getLocal(0)->value(), ".0") == 0; + comprehension = strcmp(code->name()->value(), "") == 0 || is_genexpr; } else { PycRef top = blocks.top(); end = top->end(); // block end position from SETUP_LOOP @@ -1752,6 +1808,16 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) break; } + // For genexpr: POP_TOP discards the sent value after YIELD_VALUE. + // Keep the ASTComprehension on the stack so JUMP_BACKWARD can find it. + if (curblock->blktype() == ASTBlock::BLK_FOR + && curblock.cast()->isComprehension() + && value.type() == ASTNode::NODE_COMPREHENSION + && value.cast()->comprehensionType() == ASTComprehension::GENEXPR) { + stack.push(value); + break; + } + curblock->append(value); if (curblock->blktype() == ASTBlock::BLK_FOR @@ -1856,6 +1922,19 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) { PycRef value = stack.top(); stack.pop(); + // For genexpr: "return None" sits above the ASTComprehension on the stack. + // Lift the comprehension as the real return value so BuildFromCode exposes it. + if (!stack.empty() && stack.top() != nullptr + && stack.top().type() == ASTNode::NODE_COMPREHENSION + && stack.top().cast()->comprehensionType() == ASTComprehension::GENEXPR) { + bool value_is_none = (value == nullptr) + || (value.type() == ASTNode::NODE_OBJECT + && value.cast()->object()->type() == PycObject::TYPE_NONE); + if (value_is_none) { + value = stack.top(); + stack.pop(); + } + } curblock->append(new ASTReturn(value)); if ((curblock->blktype() == ASTBlock::BLK_IF @@ -2495,7 +2574,11 @@ PycRef BuildFromCode(PycRef code, PycModule* mod) { PycRef value = stack.top(); stack.pop(); - curblock->append(new ASTReturn(value, ASTReturn::YIELD)); + if (curblock->blktype() == ASTBlock::BLK_FOR && curblock.cast()->isComprehension()) { + stack.push(new ASTComprehension(value, ASTComprehension::GENEXPR)); + } else { + curblock->append(new ASTReturn(value, ASTReturn::YIELD)); + } } break; case Pyc::SETUP_ANNOTATIONS: @@ -2968,8 +3051,7 @@ void print_src(PycRef node, PycModule* mod, std::ostream& pyc_output) case ASTNode::NODE_COMPREHENSION: { PycRef comp = node.cast(); - - pyc_output << "[ "; + pyc_output << (comp->comprehensionType() == ASTComprehension::LISTCOMP ? "[ " : "( "); print_src(comp->result(), mod, pyc_output); for (const auto& gen : comp->generators()) { @@ -2982,7 +3064,7 @@ void print_src(PycRef node, PycModule* mod, std::ostream& pyc_output) print_src(gen->condition(), mod, pyc_output); } } - pyc_output << " ]"; + pyc_output << (comp->comprehensionType() == ASTComprehension::LISTCOMP ? " ]" : " )"); } break; case ASTNode::NODE_MAP: