From fecca6948d42af6504f4b2108c1c1e054b6e9ecb Mon Sep 17 00:00:00 2001 From: luisleo526 Date: Tue, 7 Jul 2026 21:16:19 +0800 Subject: [PATCH] fix(codegen): bare ta. read must bind to a LIVE ta site, not a dead one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare property-read resolver for ta.vwap / argless-implicit-compute TAs (visit_expr.py) picked the FIRST ta_call_sites entry whose member matched `_ta__`, with no dead-code check. When the first such site is owned by a never-called (dead) function, its member declaration is pruned by the dead-code pass — so a LIVE bare read (top-level or inside request.security) referenced an undeclared member. Regression exemplar: nightowlxtrader-azt — `f5()`/`f15()` are defined but never called (dead), so their `ta.vwap` sites (`_ta_vwap_10`/`_ta_vwap_13`) are pruned; the live `vwap2 = ta.vwap` and two `request.security(..., ta.vwap, ...)` reads all resolved to `_ta_vwap_10` → `error: use of undeclared identifier '_ta_vwap_10'` (compile-error; the strategy was carrying a STALE old-codegen excellent CSV). Fix: skip `_dead_ta_indices` in the resolver so a live read binds to the first LIVE matching site (here `_ta_vwap_37`, owner=None). Truly-dead sites stay pruned — dead-code elimination is preserved. Corpus emission BYTE-IDENTICAL (265/265 corpus strategies, SHA-256 fix vs base — no corpus strategy has a dead-owned property site before a live read). pytest 1446 passed. nightowlxtrader now compiles + grades a genuine excellent (was stale). Co-Authored-By: Claude Opus 4.8 (1M context) --- pineforge_codegen/codegen/visit_expr.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pineforge_codegen/codegen/visit_expr.py b/pineforge_codegen/codegen/visit_expr.py index a4972b3..5c2e75f 100644 --- a/pineforge_codegen/codegen/visit_expr.py +++ b/pineforge_codegen/codegen/visit_expr.py @@ -493,8 +493,19 @@ def _visit_member_access(self, node: MemberAccess) -> str: (node.member in TA_IMPLICIT_COMPUTE_FULL and node.member in TA_COMPUTE_ARGS and TA_COMPUTE_ARGS[node.member] == []) or node.member == "vwap" ): - # Find the matching call site - for site in self.ctx.ta_call_sites: + # Find the matching call site. Skip sites pruned as dead + # code (their owner function is never called): a dead site's + # member declaration is never emitted (see base.py + # ``_dead_ta_indices`` / emit_top.py member-decl guard), so + # binding a LIVE bare property read to one references an + # undeclared member. Regression: nightowlxtrader-azt — a live + # ``ta.vwap`` (top-level + inside request.security) resolved + # to dead ``f5``'s first-in-order ``_ta_vwap_10`` and emitted + # ``use of undeclared identifier '_ta_vwap_10'``. A live read + # must bind to a live site. + for _i, site in enumerate(self.ctx.ta_call_sites): + if _i in self._dead_ta_indices: + continue ta_short = site.class_name.split("::")[-1].lower() if site.member_name.startswith(f"_ta_{node.member}_"): if node.member == "vwap":