Skip to content

The family_surface type mark: a welded base's opt-in to rod-synthesized family surfaces - #3

Closed
skarndev wants to merge 1 commit into
mainfrom
feature/family-surface-mark
Closed

skarndev wants to merge 1 commit into
mainfrom
feature/family-surface-mark

Conversation

@skarndev

Copy link
Copy Markdown
Owner

What

A new mark in the annotation vocabulary:

[[=welder::mark::family_surface]]                   // every honoring rod
[[=welder::mark::family_surface(welder::lang::py)]] // one language only

placed on a welded base class, opting its family (two or more welded classes deriving it — the shape a versioned class template welded per instantiation makes) into a rod-synthesized version-agnostic surface on that base. welder core only stores the mark (detail::family_surface_spec — lang-maskable like exclude/include, repeated marks union) and answers the query (welder::family_surface_for(type, lang)); what the surface consists of is each honoring rod's contract, and a rod without the feature ignores the mark.

Why

welder-csharp's family surface (skarndev/welder-csharp#1) synthesizes dispatch members onto family bases so base-typed C# code carries data without a downcast. Synthesizing members onto a base is too intrusive to infer from structure alone — any two welded classes sharing a welded base would qualify — so the contract is strict opt-in: a rod must synthesize nothing for a base that does not carry the mark. The mark lives in core (not the rod) because the base definitions sit in consumers' core headers, which must parse without any rod fetched — and because the concept is language-agnostic (a Python or LuaCATS family surface can honor the same mark later).

Tests

New compile lock tests/core/family_surface_mark.cpp: bare covers every language (user-minted user_lang included), scoped covers exactly the named ones, repeats union, unmarked answers false everywhere. All 51 compile.* locks green.

🤖 Generated with Claude Code

…nthesized family surfaces

A family (two or more welded classes deriving one welded base — the shape a
versioned class template welded per instantiation makes) may have a rod
synthesize a version-agnostic surface ON the base: the member intersection
the derived classes bind identically, so base-typed code carries data
without naming a concrete (the C# rod's dispatch members are the first
implementation). Synthesizing members onto a base is too intrusive to infer
from structure alone, so it is strictly OPT-IN:

    [[=welder::mark::family_surface]]                   // every honoring rod
    [[=welder::mark::family_surface(welder::lang::py)]] // one language only

welder core stores the mark (detail::family_surface_spec, lang-maskable
like exclude/include, repeats union) and answers the query
(welder::family_surface_for(type, lang)); what a surface consists of is
each honoring rod's contract, and a rod without the feature ignores the
mark. New compile lock: tests/core/family_surface_mark.cpp.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
skarndev added a commit to skarndev/welder-csharp that referenced this pull request Aug 21, 2026
…the option

Synthesizing members onto a base is too intrusive to infer from structure
alone — any two welded classes sharing a welded base would qualify. The
surface is now synthesized ONLY for a base carrying
[[=welder::mark::family_surface]] (skarndev/welder#3) covering this rod's
language; an unmarked base is never touched, however hoistable its family's
intersection is. The mark lives in welder CORE, not this rod, because base
definitions sit in consumers' core headers, which must parse without any
rod fetched (and gcc-16 ignores annotations on redeclarations, so the
bindings layer cannot attach one after the fact).

options::family_surface is gone — the mark is the one switch. Manifest
recording is unconditional now: an unmarked class's manifest still resolves
member types when it appears inside a marked family's members. welder pin
moves from main to the mark commit (restore main once welder#3 lands).
family.hpp marks its two bases and adds an Unmarked family whose perfectly
hoistable intersection must synthesize nothing; FamilyTests locks that
negative (42 managed tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@skarndev

Copy link
Copy Markdown
Owner Author

Closing per design discussion: the family-surface opt-in stays out of core (for now) — the marker moves to the C# rod itself, spelled in wowlib behind a macro that only expands when the rod is part of the build.

@skarndev skarndev closed this Aug 21, 2026
@skarndev
skarndev deleted the feature/family-surface-mark branch August 21, 2026 14:22
skarndev added a commit to skarndev/welder-csharp that referenced this pull request Aug 21, 2026
Design reversal of the previous commit's welder-core dependency: the
family_surface mark did not belong in welder core while only this rod
honors it (skarndev/welder#3 closed unmerged). The marker now lives here —
<welder/rods/csharp/marks.hpp> defines
[[=welder::rods::csharp::family_surface]] (a bare tag; no language mask —
it IS this rod's mark) plus the family_surface_marked(type) query, and the
class opener reads it directly. The welder pin returns to main.

A consumer whose welded headers must also parse WITHOUT this rod (a
Python-only build where welder-csharp is not fetched) spells the mark
behind a build-driven macro that expands to nothing when the rod is absent
— documented in marks.hpp; gcc-16 collects annotations only from the
DEFINING declaration, so the macro must be consistent across every TU of
one build tree.

Same tests, same results: 42 managed (marked bases synthesize, the
unmarked family's hoistable intersection synthesizes nothing), goldens
untouched, 7/7 ctest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant