Skip to content

Support associated types and generic trait parameters#25

Merged
Kab1r merged 3 commits intomainfrom
claude/add-associated-types-VqblH
Apr 22, 2026
Merged

Support associated types and generic trait parameters#25
Kab1r merged 3 commits intomainfrom
claude/add-associated-types-VqblH

Conversation

@Kab1r
Copy link
Copy Markdown
Owner

@Kab1r Kab1r commented Apr 17, 2026

Summary

  • Associated types: partial devirtualization — methods that don't reference any associated type in their signature get full vtable-cmp dispatch; methods that do reference one fall back to straight vtable dispatch
  • Generic trait parameters: no devirtualization, but the trait compiles and works correctly with dyn Trait and dyn Trait + Send
  • Adds syn's visit feature for signature analysis, new helper functions for associated type detection and Self::AssocType rewriting in inherent impl signatures

Test plan

  • All existing tests pass (equivalence, UI, UI attr, declarative macro)
  • attr_assoc_type.rs — associated type trait with mixed devirt/fallback methods, multiple concrete Color bindings, Send dispatch
  • attr_assoc_default.rs — associated type trait with default method body calling a devirtualizable method
  • attr_generic_trait.rs — generic trait with multiple impl specializations and Send dispatch
  • Equivalence tests for both associated types and generic traits
  • cargo clippy --workspace --exclude devirt-fuzz --all-targets -- -D warnings passes clean

Associated types get partial devirtualization: methods that don't
reference any associated type in their signature get full vtable-cmp
dispatch, while methods that do reference one fall back to vtable
dispatch. Generic trait parameters disable devirtualization entirely
but the trait compiles and works correctly with dyn Trait.

https://claude.ai/code/session_017idUHxWBY7bSNFQQMb4Rm4
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: be75e0ca-e6f9-4f73-b32a-aab71680d06b

📥 Commits

Reviewing files that changed from the base of the PR and between a9cae0b and 8dd9ef4.

📒 Files selected for processing (1)
  • crates/macros/src/lib.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Support for devirtualizing traits with associated types
    • Support for devirtualizing generic traits
  • Tests

    • Expanded test coverage and UI tests for associated-type and generic-trait devirtualization
    • Added validation for trait-object dispatch with Send/Sync bounds
  • Chores

    • Enabled an additional parsing feature in a dependency to support these enhancements

Walkthrough

Extends devirtualization to support trait associated types and trait generics, updates proc-macro expansion to collect associated-type info and rewrite method signatures via a syn visitor, enables the syn "visit" feature, and adds unit/UI tests covering associated types, default associated methods, and generic traits.

Changes

Cohort / File(s) Summary
Workspace Dependencies
Cargo.toml
Added "visit" to the syn features list while keeping "full" and "visit-mut".
Proc-macro: trait expansion & rewriting
crates/macros/src/lib.rs
Allow trait generics and associated types; collect AssocTypeInfo; parameterize generated dyn Trait<...> with generics and associated-type bindings; switch fat-pointer assertion to generic never-called fn; conditionally coerce fake pointer to *const dyn Trait<...>; route methods referencing associated types (or when trait generics disallow devirt) to fallback; introduce a VisitMut rewrite for Self::AssocType → generated generics; preserve trait args and emit ImplItem::Type items in inner impls.
Tests — runtime equivalence
crates/core/tests/equivalence.rs
Added macro-gated tests for associated-type traits (Drawable) and generic traits (Processor<T>), including dispatch checks across &dyn Trait<Color = ...>, &dyn Processor<T>, and auto-trait object bounds (+ Send, + Send + Sync).
Tests — UI registration
crates/core/tests/ui_attr.rs
Registered three new passing trybuild tests: tests/ui_attr/attr_assoc_type.rs, tests/ui_attr/attr_assoc_default.rs, and tests/ui_attr/attr_generic_trait.rs.
Tests — UI cases: associated types & default
crates/core/tests/ui_attr/attr_assoc_type.rs, crates/core/tests/ui_attr/attr_assoc_default.rs
New UI tests demonstrating devirtualized dispatch for traits with associated types (including a default-method case) and examples using &dyn Trait<Color = ...> and (dyn Trait<Color = ...> + Send) paths.
Tests — UI case: generic trait
crates/core/tests/ui_attr/attr_generic_trait.rs
New UI test demonstrating devirtualization of a generic trait Processor<T> with Processor<String> and Processor<u32> impls and tests for + Send object bounds.

Sequence Diagram(s)

sequenceDiagram
    participant Macro as Devirt Macro
    participant Validator as Trait Validator
    participant Collector as AssocType Collector
    participant Rewriter as Visitor (syn::visit_mut)
    participant Generator as Code Generator

    Macro->>Validator: validate_trait(trait_def)
    Validator-->>Macro: allow generics & assoc types
    Macro->>Collector: collect_assoc_types(trait_def)
    Collector-->>Macro: AssocTypeInfo
    Macro->>Rewriter: rewrite methods (Self::AssocType -> generics)
    Rewriter-->>Macro: rewritten method signatures
    Macro->>Generator: build dyn type + fat-pointer assertion
    Generator-->>Macro: vtable helpers, shims, impls (dispatch or fallback)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hop through traits and nibble Self::Color,

I stitch generics where the vtables holler,
Visitors prance, rewrite with care,
Vtables hum and methods pair,
Hooray — devirtuals stretch a little taller!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main changes: adding support for associated types and generic trait parameters, which aligns with the primary objectives of the pull request.
Description check ✅ Passed The description is directly related to the changeset, providing a clear summary of associated types implementation, generic trait parameter support, test coverage, and dependency updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/add-associated-types-VqblH

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/macros/src/lib.rs`:
- Around line 209-230: The current build_assert_ty() hardcodes witness types (()
, 'static , { 0 }) which over-constrains generic bounds and breaks non-integer
const params; instead generate synthetic generic parameters that mirror the
original trait_generic_params and associated-type idents (preserving their
bounds and const types) and use those synthetic idents as the arguments when
constructing the assertion type (*const dyn `#name`<...>), so the compile-time
size_of assertion uses generics with the same bounds rather than concrete
placeholder values; update build_assert_ty to emit those synthetic
type/lifetime/const parameters and substitute them into the *const dyn
`#name`<#(`#args`),*> instantiation (and remove the hardcoded (), 'static, { 0 }
tokens).
- Around line 145-173: The rewrite currently drops generic args and trailing
projections when replacing Self::Assoc paths; update
RewriteSelfAssocTypes::visit_path_mut to preserve any remaining path segments
and their GenericArguments instead of replacing the entire path with the
synthetic ident from AssocTypeInfo (produced by collect_assoc_types).
Concretely: when matching a path of form Self::X{...} or Self::X::Y, locate the
segment index for the assoc type (currently using i.segments[1].ident), replace
only that segment's ident with the corresponding synthetic ident (from
AssocTypeInfo.idents/generics mapping) and keep the rest of i.segments
(including segment.arguments and any subsequent segments) intact; if you prefer
to reject unsupported forms, add an explicit check in visit_path_mut that
detects GenericArguments or extra segments beyond the assoc segment and emit a
compile_error or panic with a clear message mentioning RewriteSelfAssocTypes and
AssocTypeInfo so callers know to avoid GATs or projections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a7ad8d34-400d-4a05-85d0-37f0e813ddae

📥 Commits

Reviewing files that changed from the base of the PR and between 4be945b and f9c7287.

📒 Files selected for processing (7)
  • Cargo.toml
  • crates/core/tests/equivalence.rs
  • crates/core/tests/ui_attr.rs
  • crates/core/tests/ui_attr/attr_assoc_default.rs
  • crates/core/tests/ui_attr/attr_assoc_type.rs
  • crates/core/tests/ui_attr/attr_generic_trait.rs
  • crates/macros/src/lib.rs

Comment thread crates/macros/src/lib.rs
Comment thread crates/macros/src/lib.rs Outdated
- Replace hardcoded witness types ((), 'static, { 0 }) in the fat-ptr
  assertion with synthetic generic parameters that mirror the trait's
  own generics and associated type bounds, avoiding breakage with
  non-integer const params or custom-bounded associated types.
- Fix RewriteSelfAssocTypes to preserve generic arguments and trailing
  path segments (e.g. Self::Color<T>::Inner → __Color<T>::Inner)
  instead of replacing the entire path.

https://claude.ai/code/session_017idUHxWBY7bSNFQQMb4Rm4
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/macros/src/lib.rs`:
- Around line 319-327: predicate_references_self only inspects pt.bounded_ty and
misses occurrences of "Self" in bounds (e.g., `T: Into<Self>`); update
predicate_references_self to traverse the entire syn::WherePredicate using a
visitor: implement a SelfFinder struct implementing syn::visit::Visit that sets
found=true when visiting a syn::Path whose first segment ident == "Self", then
call syn::visit::visit_where_predicate(&mut finder, pred) and return
finder.found so any appearance of Self anywhere in the predicate is detected.
- Around line 287-296: The generated const block defines fn
__devirt_assert<#(`#fn_params`),*>() `#where_clause` but never calls it, so the
assert isn't monomorphized for generic traits; update the code generation to add
a clear doc comment above the const/ fn __devirt_assert explaining that this
compile-time fat-pointer size check cannot be evaluated for generic trait
parameters (the function is intentionally not invoked), and document the
limitation and rationale so future maintainers (reading the const block/
__devirt_assert and the surrounding generated code) understand why the assertion
only effectively validates non-generic traits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7dc4277a-0bba-4bc2-9f67-c7a942862bff

📥 Commits

Reviewing files that changed from the base of the PR and between f9c7287 and a9cae0b.

📒 Files selected for processing (1)
  • crates/macros/src/lib.rs

Comment thread crates/macros/src/lib.rs
Comment thread crates/macros/src/lib.rs
- predicate_references_self now uses a visitor to find Self anywhere
  in a where predicate (e.g. T: Into<Self>), not just as the bounded
  type.
- Add comment explaining why the generic-trait fat-pointer assertion
  is a type-well-formedness check rather than a compile-time size
  assertion.

https://claude.ai/code/session_017idUHxWBY7bSNFQQMb4Rm4
@Kab1r Kab1r merged commit c645c29 into main Apr 22, 2026
7 checks passed
@Kab1r Kab1r deleted the claude/add-associated-types-VqblH branch April 22, 2026 01:51
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.

2 participants