Skip to content

feat(ptodsl): support struct member access (named fields) - #1182

Draft
jimmychou0 wants to merge 3 commits into
hw-native-sys:mainfrom
jimmychou0:zjm/ptodsl-struct-member-access
Draft

feat(ptodsl): support struct member access (named fields)#1182
jimmychou0 wants to merge 3 commits into
hw-native-sys:mainfrom
jimmychou0:zjm/ptodsl-struct-member-access

Conversation

@jimmychou0

Copy link
Copy Markdown
Contributor

Summary

Add a named-field struct surface to PTODSL on top of the canonical
pto.struct_type / pto.declare_struct / pto.struct_get / pto.struct_set
API (introduced in #1104). Enables Python member syntax:

Point = pto.struct({"x": pto.i32, "y": pto.f32})
state = pto.declare_struct(Point)
state.x = 1            # -> pto.struct_set(state, [0], 1)
state.y = 2.5          # -> pto.struct_set(state, [1], 2.5)
count = state.x        # -> pto.struct_get(state, [0])

Nested member access works too: s.pt.x = 1 lowers to
pto.struct_set(s, [1, 0], 1).

Design

Full design doc: docs/designs/ptodsl-struct-member-access-design.md (three
rounds of review).

Key decisions:

  • Field names are compile-time constants carried by the descriptor; they
    never enter the IR. !pto.struct<...> stays positional.
  • Field-name → positional-path resolution happens entirely at AST-rewrite
    time
    . The rewriter statically evaluates pto.struct({...}) /
    pto.struct_type(...) literals and emits pto.struct_get / pto.struct_set
    with baked integer paths. No runtime helper, no descriptor→value propagation.
  • _StructMemberRewriter handles rebinding, multi-target assignment order,
    +=, annotated assignment, del, positional-layer rejection, and
    branch-merge conflicts. Gated by ast_rewrite.
  • declare_struct returns a StructValue whose __getattr__/__setattr__
    are diagnostic-only (clear error when source is unavailable / ast_rewrite=False).

Tests

  • test_struct.py: 14 tests pass (5 new NamedStructMemberAccessTest).
  • test_docs_as_test.py: 192 blocks pass.
  • test_ptoas_frontend_verify.py: PASS.
  • test_ast_rewrite_example_ir.py: PASS.

Validated on 144 in a fresh checkout:
/home/zhoujiaming/ptoas-sim-ci/scratch/struct-member-144-08071421/ptodsl-struct.

Related

Closes #1129

Add a pto.struct({...}) named-field surface on top of the canonical
pto.struct_type/declare_struct/struct_get/struct_set API. Field names are
compile-time constants resolved to positional paths entirely at AST-rewrite
time, so state.field / state.field = v / state.inner.field lower losslessly
to the existing get/set ops with no runtime helper or descriptor->value
propagation.

- _types.py: _StructDescriptor gains field_names, field_index,
  field_descriptor_at, from_named; add pto.struct(dict) constructor with
  field-name validation (identifier, non-keyword, non-underscore, reserved).
- _ast_rewrite.py: new _StructMemberRewriter statically evaluates
  pto.struct/pto.struct_type literals, tracks type/value bindings, and emits
  pto.struct_get/set with baked integer paths. Handles rebinding, multi-target
  assignment order, AugAssign, AnnAssign, del, positional-layer rejection, and
  branch-merge conflicts. Gated by ast_rewrite.
- _ops.py: declare_struct returns StructValue (diagnostic-only member access
  when source is unavailable).
- user guide + docs fixtures + test_struct.py coverage.
@jimmychou0
jimmychou0 force-pushed the zjm/ptodsl-struct-member-access branch from 35aa2f1 to c9922fe Compare August 7, 2026 07:53
- AugAssign: use Store context for the rewritten temp target so
  state.field += value compiles (was Load, which the AST compiler rejects).
- Local nested descriptors: _eval_field_type now resolves names from the
  local _type_bindings before static_env, so a named descriptor defined in
  the same function (Inner used inside Outer) is usable for nested member
  access instead of being treated as a scalar.
- AnnAssign: reject dynamic/non-static member annotations instead of silently
  discarding them; only pto.* dtypes and known static types are accepted.
- Duplicate literal keys: reject duplicate field names in pto.struct({...})
  so the AST metadata positions stay aligned with the runtime dict.
- Add a named-member pto.struct({...}) + state.field probe to
  test_ptoas_frontend_verify.py to cover the AST-rewritten named surface
  through EmitC field access.
- Preserve assignment-form local descriptor aliases: Alias = Inner now
  inherits the Inner type binding, so a locally-defined named descriptor can
  be reused inside a nested pto.struct({...}).
- Validate annotated member assignment against the field's scalar type: the
  statically-evaluated metadata now carries a scalar type key (_ScalarField)
  and a member annotation such as pto.i32 is rejected when it does not match
  the field type (e.g. a pto.f32 field annotated pto.i32).
- Add default (VPTO) named-member end-to-end coverage plus edge-case
  regressions: VPTO probe exercising local descriptor alias + nested member
  access + +=, and test_struct cases for +=, local alias/nesting, annotation
  mismatch, and duplicate literal keys.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ptodsl): 支持 struct 的成员访问语法

1 participant