Skip to content

Latest commit

 

History

History
143 lines (119 loc) · 11.1 KB

codegen-coverage-gaps.md

File metadata and controls

143 lines (119 loc) · 11.1 KB

Codegen coverage gaps — running follow-up list

Follow the emission invariant for translator changes. The historical acceptance of defensive placeholder fallthroughs below is superseded; an upstream check does not authorize a placeholder in the emitter. Existing placeholders are open defects, even behind an upstream refusal. Preserve compilation for TradingView-valid scripts that already transpile and compile while implementing their semantics, as the invariant requires.

Historical record. Each section below is dated and describes the tree of its date, not main. The Phase B audit ran on 2026-05-28, before the first public release, so its file:line references point into that tree. Since then, color.from_gradient has become a visual-only stub that transpiles with a warning (it is no longer rejected), and the 2026-09-25 request.security follow-up is superseded: a request of another symbol whose value can reach a trade now reads the feed a requests manifest pins for it or stops the run where its value is read (one that reaches only plots, alerts, tables or logs lowers to na with a warning), and a ?: symbol that can reach a trade registers the symbol the run computes (see CHANGELOG.md, "Libraries and requests for outside data"). AGENTS.md describes the current support contracts.

Tracks fallthrough paths in pineforge_codegen/codegen/visit_call.py and pineforge_codegen/codegen/visit_expr.py that emit a literal "false" / "0" / "na<double>()" for an unrecognized construct, and whether the analyzer (support_checker.py) already rejects the construct before codegen ever sees it.

The historical audit counted a gap only when both were true:

  1. The fallthrough is reachable from a valid Pine v6 construct.
  2. The fallthrough produces silently-wrong behavior (not a defensive sentinel that the engine ignores).

The audit treated a fallthrough behind an analyzer refusal as a defensive safety net. Under the emission invariant, such a fallback must raise an internal error that becomes a located refusal instead of emitting a value, subject to the invariant's non-regression precedence.

Status — 2026-05-28 (end of Phase B)

The historical audit reported no additional gaps beyond those it closed. That conclusion used the narrower criteria above; it does not close the defensive-placeholder defects identified below.

Audit method

Ran grep -n 'return "false"\|return "0"' pineforge_codegen/codegen/visit_call.py pineforge_codegen/codegen/visit_expr.py and triaged each of the 34 matches against the analyzer's rejection tables (HARD_REJECT_FUNC, HARD_REJECT_NAMESPACE, NOT_YET_FUNC, UNSUPPORTED_BARE_FUNCS, UNSUPPORTED_NAMESPACES, UNSUPPORTED_MEMBERS, UNSUPPORTED_NAMESPACE_VARS, SUPPORTED_TIMEFRAME_FUNC, SUPPORTED_SYMINFO, SUPPORTED_MATH, SUPPORTED_STR, SUPPORTED_TA, SUPPORTED_ARRAY, SUPPORTED_MAP, SUPPORTED_COLOR_CONST, SUPPORTED_COLOR_FUNC, SUPPORTED_LOG, SUPPORTED_RUNTIME_FUNC).

Triage summary

Location Construct Status
visit_expr.py:140 ColorLiteral → "0" Valid color literals lower to int64 constants. A zero substituted for a malformed literal is a placeholder defect.
visit_expr.py:218 strategy.short → "false" Known enum constant.
visit_expr.py:288 strategy.fixed → "0" Known qty-type constant.
visit_expr.py:339 chart.is_* non-standard chart types → "false" Correct semantics (batch is always standard OHLCV).
visit_expr.py:340-345 chart.<unknown> → raise ValueError An internal guard must become a located refusal under the emission invariant. A bare exception is an error-reporting defect.
visit_expr.py:371-372 timeframe.isticks → "false"; other timeframe.* → "0" The known isticks branch describes batch data. The unknown-member catch-all is a placeholder defect even if upstream checks prevent it from running.
visit_expr.py:385,388 barstate.isrealtime → "false"; other barstate.* → "false" The known historical-bar value is separate from the unknown-member catch-all, which is a placeholder defect.
visit_expr.py:396,405,412,417,423 backadjustment.*, settlement_as_close.*, adjustment.*, dayofweek.* catch-alls Known constants have defined values; unknown-member defaults are placeholder defects. An engine accepting an int does not establish Pine semantics.
visit_expr.py:443 session.<unknown> → "false" Known members being enumerated does not justify a value for an unknown member. The catch-all is a placeholder defect.
visit_expr.py:448 syminfo.<unknown> → "0" The historical whitelist blocked unknown members upstream; the emitter placeholder is still a defect.
visit_expr.py:459 color.<unknown> → "0" The historical whitelist covered known constants; an unknown-member placeholder is still a defect.
visit_expr.py:461 SKIP_NAMESPACES (table/label/line/box/polyline/chart/linefill/display/size/position) → "0" The audit treated these as visual omissions. Substituting zero for a missing value lowering is a defect; each supported member needs its semantics.
visit_expr.py:484-507 strategy.oca.<unknown>, strategy.direction.<unknown>, strategy.commission.<unknown>, strategy.closedtrades.<unknown>, strategy.opentrades.<unknown> catch-alls Known enum values do not define unknown members. The catch-all defaults are placeholder defects.
visit_call.py:357 map.<unknown>(...) → "0" The historical whitelist rejected unknown functions upstream; the emitter placeholder is still a defect.
visit_call.py:404 array.<unknown>(...) → "0" The historical whitelist rejected unknown functions upstream; the emitter placeholder is still a defect.
visit_call.py:411-414 SKIP_NAMESPACES/SKIP_VAR_TYPES/SKIP_FUNC_NAMES → "0" The audit treated these as visual omissions. A missing lowering remains a defect even when its result is usually ignored.
visit_call.py:664,689 timestamp() with 0 or 1 positional args → "0" Arity must be checked against the applicable overload, including the string overload. A placeholder zero is not an arity check or a timestamp lowering.
visit_call.py:693 barssince(...) → "0" The historical checker rejected this bare call upstream; the emitter placeholder is still a defect.
visit_call.py:809-815 timeframe.<unknown>(...) → raise ValueError The historical whitelist rejected unknown functions upstream. The internal guard must become a located refusal.
visit_call.py:1057 strategy.opentrades.exit_time(idx) → "0" Accepting zero in the engine does not establish Pine semantics. Apply the accessor grammar and return semantics; any placeholder remains a defect.
visit_call.py:1088 Generic strategy.<unknown>(...) → "0" The historical checker rejected unknown functions upstream; the emitter placeholder is still a defect.
visit_call.py:1096-1109 color.new/r/g/b/t/rgb/from_gradient with missing args → "0" Missing required arguments need signature checks, not placeholder values. The historical rejection of color.from_gradient has since changed, as noted above.

Conclusion

The historical audit closed the reachable fallthroughs it identified with analyzer checks. Its remaining known-constant branches are distinct from defensive placeholders and missing visual lowerings. The latter remain defects under the emission invariant, even if an upstream check blocks them or the engine ignores the result.

Two raise ValueError guards (visit_call.py:812, visit_expr.py:342) were added to expose internal failures. Those errors still need conversion to located refusals where the emission invariant permits refusal.

The audit's original conclusion of no further work does not resolve these defects or authorize new refusals of TradingView-valid, compiling scripts.

Array range and slice follow-up (2026-09-25)

lab tv probes on BINANCE:ETHUSDT.P, 15 minutes, 2025-04-01 through 2025-04-08 used the operation's result in each order signal. An earlier unobserved probe let TradingView eliminate the operation and was discarded.

Operation TradingView evidence PineForge behavior
Primitive array<float> slice, then write through slice and source 32/32 entries signal both-alias; covered tape SHA-256 fbd83309262bfd1a8a06e8f3cc82228fad1b1358b03b135b200246d5f4cfb7c0 The same source run against the built engine signals no-alias. Both syntax forms emit a located warning in transpile_full / transpile_json.
array.fill(a, 9, 2, 1) on [1,2,3] 32/32 entries signal 123 (no-op); covered tape SHA-256 83243e37dd66678e1b9127baac2ba42ef74815d31097c0c2095a08efffca575e Guarded no-op, without invalid STL iterator arithmetic.
array.slice(a, 2, 1) request_error RE10044, “Index 'from' should be less than index 'to'.” Runtime error with the same message.
Negative index_from or index_to in fill / slice Four observed probes return request_error RE10045, index -1 out of bounds for size 3. Both endpoints reject before iterator arithmetic; existing guard was correct.

The slice copy remains a semantic gap. A safe alias requires a collection view whose source storage survives slice lifetime and mutations, including source resizing and temporary receivers. The current std::vector representation is used throughout codegen, so the implementation preserves the compiling lowering and warns at each slice call. The probe establishes that primitive elements alias in TradingView; the earlier “UDTs only” possibility is refuted.

request.security symbol follow-up (2026-09-25)

The support checker now indexes declarations, reads and rebinds by lexical binding for the security symbol argument. A local sym inside a function no longer taints an unrelated global sym; a rebind of the global inside an if still disqualifies it. A TradingView probe with the local rebind and a global chart-symbol request recorded 32/32 chart-feed entries (covered tape SHA-256 8a0492f71ffc489e45c3eb80907bf796658719e2ef7efa7c0df312e8eeb28917).

A second TradingView probe chose BINANCE:BTCUSDT.P in the true ternary arm on an ETH chart and recorded 32/32 alternate-feed entries (covered tape SHA-256 a9aa27e13b64260eba08cafec3e345ddc7e06f378f5c6b94e5f6955eb32abaf3). The generated security evaluation has no alternate-symbol feed. The checker therefore tests both ternary arms, including arms reached through aliases, and warns when any path can select another symbol. It preserves previously accepted scripts and their current-symbol lowering; an unconditional alternate symbol remains rejected. A legacy bare-name admission that lexical resolution finds unsafe also remains accepted with a warning. These approximations remain semantic defects to fix while preserving compilation.

The 314 public corpus strategies all still transpile, and their emitted C++ hashes are unchanged. There is no public TU or public trade tape to regrade for this change; the changed behavior is diagnostic or accepts a formerly over-rejected source.