[NO-MERGE] Prototype: C# union types, closed enums, and closed hierarchies#404
Draft
eiriktsarpalis wants to merge 1 commit intomainfrom
Draft
[NO-MERGE] Prototype: C# union types, closed enums, and closed hierarchies#404eiriktsarpalis wants to merge 1 commit intomainfrom
eiriktsarpalis wants to merge 1 commit intomainfrom
Conversation
8826560 to
7f27772
Compare
…erarchies This commit introduces experimental support for three upcoming C# language features: 1. **C# Union Types** - Maps to existing IUnionTypeShape abstraction via new UnionKind enum (ClassHierarchy, FSharpUnion, CSharpUnion). Uses DelegateMarshaler<TSource, TTarget> for marshal/unmarshal between unrelated case types and union types. Detection via [Union] attribute + IUnion interface by metadata name. 2. **Closed Enums** - Adds IsClosed property to IEnumTypeShape, detected via [Closed] attribute by metadata name. Supported in both reflection and source generator providers. 3. **Closed Hierarchies** - [ClosedSubtype] attributes are always honored for union detection. Assembly scanning fallback requires InferDerivedTypes opt-in via TypeShapeAttribute property. Key design decisions: - No breaking changes to public interfaces (InternalImplementationsOnly) - Detection by metadata name avoids shipping BCL-conflicting types - IUnionCaseShape has no TUnionCase:TUnion constraint at interface level, enabling C# union support without new abstractions - Source gen uses string UnionKindName to avoid accessibility issues - Example JSON serializer extended with structural matching for C# unions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7f27772 to
450a14a
Compare
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.
This PR is a design prototype for mapping upcoming C# language features to PolyType's type shape abstractions. It is not intended to be merged in its current form — it exists to validate API feasibility and gather feedback.
Background
C# is introducing three related language features:
See also the System.Text.Json design for the same features.
API Changes
New types
UnionKindenumClassHierarchy,FSharpUnion, andCSharpUnionunion originsDelegateMarshaler<TSource, TTarget>IMarshalerusingFuncdelegates, enabling marshal/unmarshal between unrelated typesExtended interfaces
IUnionTypeShapeUnionKind UnionKindIEnumTypeShapebool IsClosed[Closed]Extended attributes
TypeShapeAttributebool InferDerivedTypes[ClosedSubtype]attributes or assembly scanningGenerateShapeAttributebool InferDerivedTypesGenerateShapeForAttributebool InferDerivedTypesHow C# Union Types Map to
IUnionTypeShapeThe key insight is that
IUnionCaseShape<TUnionCase, TUnion>has noTUnionCase : TUnionconstraint at the interface level. This means PolyType already supports the case where union case types are unrelated to the union type — exactly what C# unions require.Marshaling:
DelegateMarshaler<Dog, Pet>wraps:Marshal: callsnew Pet(dog)(union constructor)Unmarshal: calls((IUnion)pet).Valueand casts toDogCase index: determined by checking the runtime type of
IUnion.Value.Detection: by metadata name (
System.Runtime.CompilerServices.UnionAttribute+System.IUnion), not by PolyType-defined types. Mock attributes are provided in test cases since no compiler supports these yet.Closed Hierarchy Support
[ClosedSubtype(typeof(Circle))]attributes on a base type are always honored for union detection (similar to[DerivedTypeShapeAttribute]). Assembly scanning (finding all subtypes in the same assembly) requires explicitInferDerivedTypes = trueopt-in.What's included
IsClosedand[ClosedSubtype]-based union detection