Skip to content

Qualify type and event names - #1970

Draft
leighmcculloch wants to merge 14 commits into
handcode-spec-refs-in-derivesfrom
fully-qualified-udt-names
Draft

leighmcculloch wants to merge 14 commits into
handcode-spec-refs-in-derivesfrom
fully-qualified-udt-names

Conversation

@leighmcculloch

@leighmcculloch leighmcculloch commented Jul 30, 2026

Copy link
Copy Markdown
Member

Note

Part of a stack of PRs that must merge in this order.

A first group of PRs change contract specs so that they are produced at compile time instead of at proc-macro execution time. This provides the foundation to construct the specs from information that is only known at compile time, like the fully qualified name of a type:

  1. Add const XDR serialization rs-stellar-xdr#580
  2. Encode contract spec XDR at compile time #1965

A second group of PRs changes the type names that the sdk stores in specs are fully qualified type names, and then the cli reduces them down to unique simple identifiers. During the contract build the types are given names like ::mycrate::mymod::MyType instead of MyType. Then the cli reduces them back down to simple names after spec shaking. Qualified type names make it possible to uniquely identify types in the spec, even when they have the same name. This resolves several problems with contract specs the type identify problem (#1570), type aliases limitations (#1857 #1063), and optimise spec shaking data section size (#1978):

  1. Widen contract spec type and event name limits stellar-xdr#312
  2. Regenerate XDR from upstream stellar-xdr (01741cb) rs-stellar-xdr#543
  3. Qualify type and event names #1970 ← this PR
  4. Reduce qualified type and event names stellar-cli#2674

Note that downstream clients and SDKs should see no, or little, change because the cli will during the build process reduce the fully qualified names back to simple unique names. Contracts that had colliding type names, which meant they could not be used with clients, will now work.

A third group of PRs are an optimisation to spec shaking v2, and will use the new unique type names to shake type specs by reachability, producing a dependency graph (thanks @mootz12), so that only spec entries that can't be reached from fns, like errors and events, get spec markers from dead-code-elimination:

  1. Shake specs by reachability and markers #2043
  2. Shake by the recorded version stellar-cli#2720

What

Name a user-defined type in the contract spec by its Rust path rather than by its identifier. Each type gains a spec_type_name const fn returning module_path!() then its own name, and both the type's own definition entry and every reference to it take the name from that fn.

Examples

Generated Type Qualified Names and Spec XDR

Screenshot 2026-08-07 at 11 31 46 pm

Why

A bare name is not enough to tell two user-defined types apart: two crates, or two modules of one crate, can each define a type with the same name, and a spec that names both simply cannot distinguish between them.

This was once rare, but as contracts on Soroban are getting larger, and as libraries are showing up in the ecosystem, this is becoming more common. This does happen with error types more frequently but the impact has been minimal to none in practice.

Close #1570
Close #1857
Close #1063
Close #1978

@github-actions

Copy link
Copy Markdown
Contributor

semver:minor — the macros now generate two new public const fn items on every #[contracttype], which is an additive change to the public API.

  • spec_type_name() -> &'static str and spec_id() -> [u8; 8] are added to every struct/enum expanded by #[contracttype] (visible in tests-expanded/test_udt_tests.rs and all other expanded files). New inherent items → minor per the "adding inherent items" rule.
  • sha2-const = "0.1.3" is added as a dependency in soroban-sdk/Cargo.toml → minor (adding a Cargo dependency).
  • cargo semver-checks failed to build soroban-sdk and soroban-token-sdk due to the stellar-xdr git dependency requiring a const feature not on crates.io; those crates were classified from the diff alone. soroban-ledger-snapshot and soroban-spec both reported "no semver update required".

Worth reviewer attention: UDT spec names now include the module path (e.g. "test_udt::UdtEnum2" instead of "UdtEnum2"). This is a behavioral change to the contract spec XDR and could break tooling or downstream consumers that match on bare type names.

This classification is advisory; the author and reviewer decide the release type.

Generated by Semver Impact for #1970 · sonnet46 · 32.7 AIC · ⌖ 4.11 AIC · ⊞ 8.1K ·

Comment thread soroban-sdk/src/tests/contract_udt_struct_aliased_import.rs
@github-actions

Copy link
Copy Markdown
Contributor

Minor — the macros now emit new public inherent methods on every #[contracttype] type, which is an additive API change under the "adding inherent items" possibly-breaking rule.

  • spec_type_name() -> &'static str and spec_id() -> [u8; 8] are now generated on every #[contracttype] struct/enum (tests-expanded/test_udt_tests.rs), adding inherent items (Cargo SemVer: minor/possibly-breaking).
  • The contract spec UDT names change from bare ("UdtEnum2") to module-qualified ("test_udt::UdtEnum2"), affecting the emitted contract spec for any contract using #[contracttype]. This is a behavior change to the generated spec — existing consumers of the spec string will see different values.
  • A new dependency sha2-const = "0.1.3" is added to soroban-sdk/Cargo.toml (minor: adding a dependency).
  • cargo semver-checks could not run for soroban-sdk, soroban-token-sdk, and soroban-spec-rust due to build errors (stellar-xdr feature const unavailable on crates.io and SC_SPEC_TYPE_NAME_LIMIT missing in the baseline); classification is from the diff alone.
  • Unsure whether the UDT name change in the emitted spec (bare → module-qualified) is considered breaking for downstream consumers who rely on those spec strings. If those strings are part of the stable contract ABI, it may warrant major; authors should confirm.

This classification is advisory; the author and reviewer decide the release type.

Generated by Semver Impact for #1970 · sonnet46 · 35.4 AIC · ⌖ 4.14 AIC · ⊞ 8.1K ·

@github-actions

Copy link
Copy Markdown
Contributor

semver:minor — the macros now generate new public inherent methods on every #[contracttype]-annotated type, which is an additive API change.

  • spec_type_name() -> &'static str and spec_id() -> [u8; 8] are emitted as pub const fn on every #[contracttype] struct/enum (tests-expanded/test_udt_tests.rs, and pervasively across all other tests-expanded/ files). Adding inherent items is listed as a minor (possibly-breaking) change under the Cargo SemVer rules.
  • New dependency sha2-const = "0.1.3" added to soroban-sdk/Cargo.toml. Adding a dependency is minor.
  • cargo semver-checks could not run for soroban-sdk, soroban-token-sdk, and soroban-spec-rust due to build errors (a stellar-xdr feature flag mismatch and a missing SC_SPEC_TYPE_NAME_LIMIT symbol). The tool's output is therefore incomplete; classification is from the diff alone for those crates.

No removals, renames, or signature changes to existing public items were found.

This classification is advisory; the author and reviewer decide the final release type.

Generated by Semver Impact for #1970 · sonnet46 · 29.4 AIC · ⌖ 6.06 AIC · ⊞ 8.1K ·

@leighmcculloch
leighmcculloch force-pushed the fully-qualified-udt-names branch from 89d152f to c2f3b14 Compare July 31, 2026 08:52
@leighmcculloch
leighmcculloch changed the base branch from handcode-spec-refs-in-derives to const-spec-id-marker July 31, 2026 08:54
@github-actions

Copy link
Copy Markdown
Contributor

semver:minor — the macros now emit two new public methods on every #[contracttype] type, which is an additive change to the public API.

  • spec_type_name() -> &'static str and spec_id() -> [u8; 8] are added as pub const fn methods on every generated type (tests-expanded/ shows them on UdtEnum, UdtEnum2, structs, etc.) — adding public inherent items is a possibly-breaking minor change per the semver rules.
  • soroban-sdk/Cargo.toml adds sha2-const = "0.1.3" — adding a Cargo dependency is minor.
  • cargo semver-checks could not run for soroban-sdk, soroban-token-sdk, or soroban-spec-rust due to a build error (stellar-xdr const feature not available on crates.io); classification is from the diff alone for those crates.

Unsure: whether renaming the UDT name source from a hard-coded b"UdtEnum" literal to UdtEnum::spec_type_name() changes observable behavior for existing contracts (it should be identical for non-aliased imports, but might differ for aliased ones — the new contract_udt_struct_aliased_import test covers this). Reviewers may want to confirm the emitted XDR is equivalent for the common case.

This classification is advisory; the author and reviewer decide the release type.

Generated by Semver Impact for #1970 · sonnet46 · 30.1 AIC · ⌖ 6.9 AIC · ⊞ 8.1K ·

@leighmcculloch
leighmcculloch force-pushed the fully-qualified-udt-names branch from c2f3b14 to f527fc9 Compare July 31, 2026 09:26
@github-actions

Copy link
Copy Markdown
Contributor

semver:minor — the highest-impact single change is adding the public inherent method Bytes::to_string in soroban-sdk/src/bytes.rs, which is an additive API change (adding inherent items).

  • soroban-sdk/src/bytes.rs: new pub fn to_string(&self) -> String — adding an inherent item is minor per the possibly-breaking-changes rule.
  • soroban-sdk-macros/src/map_type.rs: new BN254 type name mappings (Bn254Fp, Bn254G1Affine, Bn254G2Affine, BnScalar) added to map_type — additive macro behavior (minor).
  • soroban-spec-rust/src/lib.rs: new apply_error_udt_override pass rewrites Error spec references to UDT on code generation — additive new behavior. cargo semver-checks could not analyze soroban-sdk, soroban-spec-rust, or soroban-token-sdk due to stellar-xdr const feature resolution failure; those crates were classified from the diff alone.

This classification is advisory; the author and reviewer decide the release type.

Generated by Semver Impact for #1970 · sonnet46 · 69.8 AIC · ⌖ 4.1 AIC · ⊞ 8.1K ·

@leighmcculloch leighmcculloch mentioned this pull request Jul 31, 2026
5 tasks
@leighmcculloch leighmcculloch linked an issue Jul 31, 2026 that may be closed by this pull request
@github-actions

Copy link
Copy Markdown
Contributor

Minor — the macros now generate a new pub const fn spec_type_name() -> &'static str inherent method on every user-defined contract type annotated with #[contracttype], which is an additive change to the generated public API.

  • tests-expanded/test_udt_tests.rs and many others: every #[contracttype] type gains impl Foo { pub const fn spec_type_name() -> &'static str { ... } }. Adding inherent items is a [possibly-breaking minor change]((doc.rustlang.org/redacted) under the Cargo SemVer rules.
  • soroban-sdk-macros/src/map_type.rs (spec_type_name_gen): the new method is emitted by spec_type_name_gen, wired into every derive macro (derive_struct, derive_enum, etc.), confirming the generated API change is universal.
  • cargo semver-checks could not analyze soroban-sdk, soroban-token-sdk, or soroban-spec-rust due to a build error (new stellar-xdr git rev requires a const feature absent from crates.io); soroban-ledger-snapshot and soroban-spec reported "no semver update required".

soroban-spec-rust had a doc-build failure in semver-checks too; its source diff adds new helper functions that appear internal, but this couldn't be confirmed by the tool. Reviewers may want to verify no public items were added there.

This classification is advisory; the author and reviewer decide the release type.

Generated by Semver Impact for #1970 · sonnet46 · 28.8 AIC · ⌖ 6.09 AIC · ⊞ 8.1K ·

@github-actions

Copy link
Copy Markdown
Contributor

Classification: minor — the PR adds new public items to soroban-sdk and changes macro-generated code, which is an additive change requiring a minor version bump.

  • soroban-sdk/src/bytes.rs: Adds BytesBuffer<B> struct and Bytes::to_buffer() method — new public items ([item-new]((doc.rustlang.org/redacted)
  • tests-expanded/: Widespread changes across nearly all expanded test files indicate the macro-generated client/spec code changed. The generated APIs are additive (new struct/impl items, not removals or signature changes), consistent with minor.
  • cargo semver-checks: Could not analyze soroban-sdk, soroban-token-sdk, or soroban-spec-rust due to build errors (missing stellar-xdr feature const and SC_SPEC_TYPE_NAME_LIMIT). soroban-ledger-snapshot and soroban-spec passed with "no semver update required".

Note: BytesBuffer is pub in bytes.rs but bytes is a private module — only to_buffer() (returning BytesBuffer) makes it reachable via soroban_sdk::bytes::BytesBuffer. If BytesBuffer is not reachable from the crate root, those items may be patch. The to_buffer method on the public Bytes type is definitely new public API (minor). I erred toward minor.

This classification is advisory; the author and reviewer decide the release type.

Generated by Semver Impact for #1970 · sonnet46 · 50 AIC · ⌖ 4.17 AIC · ⊞ 8.1K ·

@leighmcculloch leighmcculloch changed the title Qualify user-defined type names Qualify type and event names Sep 5, 2026
@leighmcculloch
leighmcculloch force-pushed the handcode-spec-refs-in-derives branch from 05771ce to 86fda69 Compare September 7, 2026 01:22
@leighmcculloch
leighmcculloch force-pushed the fully-qualified-udt-names branch 3 times, most recently from 9c4db5d to fad3c8a Compare September 7, 2026 12:55
@leighmcculloch
leighmcculloch force-pushed the handcode-spec-refs-in-derives branch 3 times, most recently from 72c3689 to 20e9cab Compare September 8, 2026 07:09
@leighmcculloch
leighmcculloch force-pushed the fully-qualified-udt-names branch from b5e38e1 to 1e8cb2d Compare September 8, 2026 07:25
@leighmcculloch
leighmcculloch force-pushed the handcode-spec-refs-in-derives branch from 20e9cab to 6d052e1 Compare September 8, 2026 12:54
@leighmcculloch
leighmcculloch force-pushed the fully-qualified-udt-names branch from 1e8cb2d to db078a5 Compare September 8, 2026 13:32
@leighmcculloch
leighmcculloch force-pushed the fully-qualified-udt-names branch from db078a5 to 7093a64 Compare September 8, 2026 21:38
@leighmcculloch
leighmcculloch force-pushed the handcode-spec-refs-in-derives branch 2 times, most recently from ee8ba41 to 44ae8ca Compare September 9, 2026 21:49
…-names

# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	soroban-sdk-macros/src/derive_event.rs
#	soroban-sdk/src/tests/contract_udt_raw_identifier.rs
#	tests-expanded/test_attributes_tests.rs
#	tests-expanded/test_attributes_wasm32v1-none.rs
#	tests-expanded/test_events_ref_tests.rs
#	tests-expanded/test_events_ref_wasm32v1-none.rs
#	tests-expanded/test_events_tests.rs
#	tests-expanded/test_events_wasm32v1-none.rs
#	tests-expanded/test_spec_lib_tests.rs
#	tests-expanded/test_spec_lib_wasm32v1-none.rs
#	tests-expanded/test_spec_shaking_v2_tests.rs
#	tests-expanded/test_spec_shaking_v2_wasm32v1-none.rs
#	tests/fuzz/fuzz/Cargo.lock
#	tests/fuzz/fuzz/Cargo.toml
#	tests/fuzz_afl/fuzz/Cargo.lock
#	tests/fuzz_afl/fuzz/Cargo.toml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Optimise spec shaking v2 with the use of a graph

1 participant