2i-issues Pyrope lowering & cgen emission defects (non-LEC)
A (registered-output interface) and B (hierarchical
enums) are the only remaining work — both still hard-error in lowering
(the latest patch round cada30102, import + ABC sra/mul ops, did not touch
their code paths). Each is valid, documented Pyrope; they have no ordering between them,
so pick either and work it end-to-end from its subtask below.
re-verified again 2026-06-20 — direct compile of both
repros confirms neither has cleared: A aborts with
reg-output-cycle (state register 'count' … homes at stage 0 but its
declared landing cycle 0 requires home -1) and B with
undefined-read (read of undefined variable 'eagle'), so both
rows stay open. The companion TBD-feature audit (2026-06-20) —
documented-but-unimplemented features, a category distinct from the A/B lowering
defects — now lives in the index's
TBD features still pending summary.
B is itself a TBD (hierarchical enums) and is intentionally not
given a new fail-by-design test there — it is already tracked as subtask B here.
Scope. This page deliberately excludes the LEC-engine
precision issues (cvc5 false-refutes on hotmux / FSM-state / memory, the
\x01nxt: phantom-output, the lgyosys no-reset-flop disagreement).
Those are checker bugs, not compiler bugs, and live in the repo-root
issues.md (Group C). Every code pointer below was re-verified by
reading the source on 2026-06-20 against a fresh
bazel build -c dbg //lhd:lhd.
Status snapshot (2026-06-20)
| Sub | Construct | Kind | Today | Primary source |
|---|---|---|---|---|
| A | Registered-output interface -> (reg count:u8@[0]) | hard error | homes at stage 0 … requires home -1 | upass/tolg/upass_tolg.cpp:4525 |
| B | Hierarchical enum enum E=(,a=(,x,,y),…) | hard error | read of undefined variable 'x' | inou/prp/prp2lnast.cpp:4362 |
Depends on
None. A and B are each self-contained and the only remaining work.
Subtasks — pending
- A — registered-output interface form
-> (reg name@[0]). Reprounexpected_semantics/regout_form/impl.prp— the canonicalcounter2pattern from the pyrope SKILL.md anddocs/pyrope/04-variables.md:pub mod top(enable:bool) -> (reg count:u8@[0]) { if enable { wrap count += 1 } }lhd compile … --emit verilog:gen.vhard-errors:state register 'count' of 'impl.top' homes at stage 0 but its declared landing cycle 0 requires home -1. The in-body form (reg count:u8=0; value=count) works; only the interface spelling fails. Root cause: the state-register output discharge applies the feedforward invarianthome.min + 1 == rec.minatupass/tolg/upass_tolg.cpp:4525(diagnostic:4526-4534). A state register pinsσ(q)=σ(din)with no flop crossing (:4434-4437, documented:4085-4086), so a counter whosedinis the same-cycle increment ofqhashome.min=0; the+1rule then demandshome.min == -1. The comment at:4093-4094("discharges against home+1") directly contradicts the no-crossing model at:4085-4086. Fix: in the state-reg branch (:4514-4535) testhome.min == rec.min(so@[0]matches home 0) instead ofhome.min + 1 == rec.min; update the comments. Before landing, confirm no passing test relies on thehome+1spelling for a state-reg output@[N>0]. - B — hierarchical (nested-tuple) enum members.
Repro
unexpected_semantics/hier_enum/impl.prp— verbatim fromdocs/pyrope/03-bundle.md:600-616:
Hard-errorsenum Animal = (,bird=(,eagle, ,parrot) ,mammal=(,rat, ,human)) pub comb top(s:bool) -> (r:u6) { r = int(Animal.bird.eagle) } // == 0ub000011read of undefined variable 'eagle'. Root cause: nested-enum detection atinou/prp/prp2lnast.cpp:4553callsenum_definition_node_of(e.value_node), which (:4362) returns non-null only for an explicitenum(...)node — not for the bare-tuple value(,eagle,,parrot)the spec prescribes. So the hierarchical branch is skipped, control falls into the "Expression value" branch (:4593-4605) which lowers the tuple viaexpr_to_node, readingeagle/parrotas variables (read_sites_:5372) → undefined-read abort (:859-864). The explicit nested-enum(...)form already works, and the runner read side (upass/runner/upass_runner.cpp:168,:2606) already supports hierarchical enums — only the bare-tuple detection is missing. Fix: make thenestedtest at:4553(orenum_definition_node_of) also accept atuplevalue and parse its members with the existingprocess_enum_assignmentlogic (:4657-4699), reusing the parent-bit /__enumvalmachinery so inner names go to fields, notexpr_to_node.
Verification
Each fix should both compile and prove equivalent
to the directory's ref.v (which encodes the intended hardware). The
re-test harness from the 2026-06-20 round lives in the session scratchpad
(retest.sh = compile + both LEC engines; comb_oracle.py =
exhaustive combinational iverilog; seq_oracle.py = clocked
random iverilog). For one case:
# A, B: must stop hard-erroring
lhd compile <dir>/impl.prp --emit verilog:gen.v
# once they compile: prove equivalent to ref.v with BOTH engines
lhd lec --impl <dir>/impl.prp --ref <dir>/ref.v \
--impl-top impl.top --ref-top top --set lec.solver=cvc5
lhd lec --impl <dir>/impl.prp --ref <dir>/ref.v \
--impl-top impl.top --ref-top top --set lec.solver=lgyosys
Add a permanent regression for each: a glob-matched pair under
inou/prp/tests/equiv/ (forward LEC) — these auto-generate a
prp-<name> bazel target with no BUILD edit. Keep the full prp
suite green (bazel test //inou/prp/... //upass/... //pass/...) and watch
for no regression in the other (already-passing) unexpected_* cases.
Done when
The remaining work is A and B only. Each must
compile and emit a netlist that is iverilog-correct and LEC-equivalent to
ref.v under both solvers, with its checked-in
regression flipping green; then its row is removed from the index's "Unimplemented /
mis-lowering forms" tables. This page is deleted when both A and B clear.