Skip to content

feature, pb:: namespace-scoped attribute vocabulary (taxonomy Phase 1) #31

Description

@steven-varga

Goal

Replace today's [[clang::annotate(\"pb::field=N\")]] string-encoded annotations with the proper pb::* namespace-scoped vocabulary specified in tasks/h5cpp-compiler-pb-attribute-taxonomy.md in the workspace. Phase 1 of a 5-phase migration; this issue covers Phase 1 only.

Background

Today's 30-pb-producer ships four string-encoded annotations parsed by src/consumer_pb.hpp:181-244:

Today Target form (Phase B / C++17 standard-attribute syntax) Phase C / C++26 reflection
[[clang::annotate(\"pb::field=2\")]] [[pb::field(2)]] [[=pb::field{2}]]
[[clang::annotate(\"pb::oneof_tags=5,6,7\")]] [[pb::field(5, 6, 7)]] (variadic on std::variant) [[=pb::field{5, 6, 7}]]
[[clang::annotate(\"pb::wire=sint32\")]] [[pb::wire(sint32)]] [[=pb::wire{pb::spec::sint32}]]
[[clang::annotate(\"pb::adapter=Timestamp\")]] [[pb::adapter(Timestamp)]] [[=pb::adapter{pb::adapter_kind::Timestamp}]]

The taxonomy doc (sections 1-2, 5, 6) specifies the full vocabulary, the C++26 typed-value sketches, and the permissive int-or-enum-tag-argument policy:

`pb::field(...)` accepts integer literals, enum values, and any mix of the two. We are the guides — this is the user's story. The library's job is to clear the path, not to choose the route.

Scope (Phase 1 only)

In: the four field-level attributes that exist today (field, oneof_tags/variadic field, wire, adapter). Plus the int-or-enum tag-argument policy.

Out: universal pb::name / pb::ignore / pb::doc / pb::on_missing (Phase 2). The .proto text emitter (Phase 3). Tier-2/3/4 attributes (Phase 4). C++26 reflection (Phase 5).

Acceptance criteria

  1. New syntax accepted. Fixtures using [[pb::field(N)]], [[pb::field(N1, N2, ...)]], [[pb::wire(spec)]], [[pb::adapter(name)]] parse and emit the same pb::meta::descriptor_t<T> specialization as today's [[clang::annotate(\"pb::...\")]] fixtures.
  2. Old syntax still accepted. Existing tests/fixtures/pb_*.cpp continue to pass without modification — both forms coexist during a transition window.
  3. Enum tag arguments work. A fixture like:
    ```cpp
    enum class user_profile_tag : std::uint32_t { name = 1, scores = 2 };
    struct user_profile_t {
    [[pb::field(user_profile_tag::name)]] std::string name;
    [[pb::field(user_profile_tag::scores)]] std::map<std::string, std::int32_t> scores;
    };
    ```
    emits the same descriptor as the integer-literal form.
  4. Mixed forms within a class allowed. No compile-error for [[pb::field(my_tag::name)]] next to [[pb::field(7)]]. Permissive default per taxonomy §6.
  5. Argument validation diagnostics. Tag values outside [1, 2^29-1], or inside protoc's reserved [19000, 19999], emit a clean compiler diagnostic at h5cpp-compiler time (open question ci, add GitHub Actions CI with compiler/OS matrix #5 in the taxonomy doc).
  6. Test coverage. Add new fixtures under tests/fixtures/ exercising:
    • Each attribute in the new form (4 fixtures, mirroring existing pb_* fixtures)
    • Enum-tag form on one fixture
    • Mixed int/enum form on one fixture
      Pre-existing fixtures (pb_primitives.cpp, etc.) preserved unchanged as the regression net for the clang::annotate path.
  7. Zero impact on emitted descriptor output. Both forms produce byte-identical generated headers when measured against the existing golden tests.

Implementation paths to evaluate

Path A — Enrich clang::annotate with multi-arg form (mechanical). Keep the clang::annotate envelope; switch the encoding from string-equality (\"pb::field=2\") to multi-arg (\"pb::field\", 2). The integer arg becomes a Clang Expr* accessible via Expr::EvaluateAsInt(ctx), which handles both integer literals and enum constant references uniformly. Keeps today's ugly outer syntax ([[clang::annotate(...)]]) but delivers enum support without plugin work. Suitable as Phase 1a.

Path B — Clang plugin registering the pb::* attribute namespace (substantial). Implements the clean [[pb::field(N)]] syntax by extending Clang's attribute parser via the libtooling plugin API. Delivers the actual target syntax. Plugin development effort; depends on a stable Clang plugin ABI (we're on Clang 20.1.8). Suitable as Phase 1b.

Recommendation: ship Path A first as a smaller, lower-risk increment that unblocks enum tag arguments. Path B (clean syntax) follows once Path A is stable; Phase 5 (C++26 reflection) eventually obsoletes both.

Branch + worktree

  • Issue number: this one
  • Branch: `-pb-attribute-vocabulary` cut from `staging` per CLAUDE.md flow
  • Worktree: `/home/steven/projects/vargalabs-workspace/worktrees/h5cpp-compiler/-pb-attribute-vocabulary`

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions