Skip to content

feat(s7): resolve custom-type S7 property classes at write time (#154)#201

Open
CGMossa wants to merge 1 commit intomainfrom
fix/issue-154-s7-prop-class-resolve
Open

feat(s7): resolve custom-type S7 property classes at write time (#154)#201
CGMossa wants to merge 1 commit intomainfrom
fix/issue-154-s7-prop-class-resolve

Conversation

@CGMossa
Copy link
Copy Markdown
Collaborator

@CGMossa CGMossa commented Apr 17, 2026

Closes #154.

Summary

Reuse the existing `.MX_CLASS_REF_` write-time placeholder for S7 property class constraints. Before this change, a getter returning another S7 class fell through `rust_type_to_s7_class` → no `class =` emitted → S7 widened to `class_any`. Now:

  • Bare PascalCase, single-segment, ungeneric return types emit `class = .MX_CLASS_REF_`.
  • The existing resolver in `registry::write_r_wrappers_to_file` swaps the placeholder for the registered R-visible class name (honoring `class = "Override"`).
  • Primitives / type params / `Vec<>` / `Option<>` etc. still resolve via the existing scalar mappings; the new arm only fires for user-defined class-like idents.

No new distributed_slice — matches the precedent from #155 / #192.

Verified

inner = S7::new_property(class = S7PropInner, getter = function(self) ...)

(new `S7PropOuter`/`S7PropInner` fixture in `rpkg/src/rust/s7_tests.rs`).

Test plan

  • `just clippy`
  • `cargo clippy --workspace --all-targets --locked -- -D warnings` (clippy_default)
  • `cargo clippy --workspace --all-targets --locked --features -- -D warnings` (clippy_all)
  • `just fmt`
  • `just rcmdinstall` — generated wrapper has `class = S7PropInner`, not `class_any`
  • `just devtools-document` — `S7PropInner.Rd` / `S7PropOuter.Rd` added, NAMESPACE updated
  • `just vendor` — `rpkg/inst/vendor.tar.xz` refreshed

Related

Generated with Claude Code

S7 property getters whose return type is another `#[miniextendr(s7)]`
class previously fell through `rust_type_to_s7_class` and were emitted
without a `class = …` constraint, so S7 implicitly widened them to
`class_any`. With `#155` / `#192` there's already an established
pattern for cross-crate class-name resolution via the CLASS_REF
placeholder — this change reuses it for S7 properties.

When a getter returns a bare, ungeneric, PascalCase single-segment
type (i.e. what a user-defined class looks like), the macro now emits
`.__MX_CLASS_REF_<Type>__` as the class constraint. At cdylib
write-time the existing resolver in `write_r_wrappers_to_file` swaps
in the R-visible class name recorded by the referenced impl block's
`MX_CLASS_NAMES` entry — so `class = "Override"` on the target is
honored, and cross-crate class hierarchies Just Work.

Verified against a new `S7PropOuter`/`S7PropInner` pair in
`rpkg/src/rust/s7_tests.rs`:

  inner = S7::new_property(class = S7PropInner, ...)

instead of the prior `class_any`. The existing CLASS_REF
unresolved-fallback path (emit bare Rust name + compile warning)
gives users a clear diagnostic when a getter returns an unregistered
type — one more place where that warning lands, and the R side will
then error at load time with `object '…' not found`, which is the
correct signal.

Matching criteria are narrow on purpose — only PascalCase + single
segment + no generics + no `::` — so primitives and type params keep
falling through to `None` (macro omits the constraint, S7 uses
`class_any`, old behavior). Types like `SEXP`, `PathBuf`, or
`ExternalPtr` in a getter return still trip the warning, which is
probably what users want anyway.

Closes #154.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@CGMossa CGMossa force-pushed the fix/issue-154-s7-prop-class-resolve branch from b2b4fa4 to 41318ba Compare April 17, 2026 11:02
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.

S7 property class constraints: resolve custom ExternalPtr types at write time

1 participant