Conversation
…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>
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A new mark in the annotation vocabulary:
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 likeexclude/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-minteduser_langincluded), scoped covers exactly the named ones, repeats union, unmarked answers false everywhere. All 51compile.*locks green.🤖 Generated with Claude Code