Master Implementation PR: generic templates for $dynamicRef bindings across languages - #8115
Draft
Abdullah Alaqeel (aqeelat) wants to merge 16 commits into
Draft
Master Implementation PR: generic templates for $dynamicRef bindings across languages#8115Abdullah Alaqeel (aqeelat) wants to merge 16 commits into
Abdullah Alaqeel (aqeelat) wants to merge 16 commits into
Conversation
C# $ref-bound dynamic-ref templates now emit one reusable generic class (PaginatedTemplate<TItemType>) instead of per-binding concrete classes (PaginatedTemplateUser). Anchor-typed properties become type parameters; executor/request-body usages close over the bound types via the existing generic-argument channel, with constructor-injected ParsableFactory fields driving deserialization inside the template. Recursive '#self' anchors close over the type parameters. Scope gates (documented in UseGenericDynamicBindingTemplates): only C#; all anchors $ref-bound; additionalProperties dynamic refs and allOf templates stay on the Phase 2 concrete path. Unbound references to a generic template degrade to UntypedNode with a warning.
… nested inline models Inherited templates now reuse the generic binding frame: InheritedTemplate<TItemType> keeps a concrete PageBase base, while BasePage promotes to BasePage<TItemType> when it resolves items through the active binding and DerivedPage<TItemType> closes over the base argument, forwarding itemTypeFactory to it. Derived declarations own fresh type parameter instances so base and derived never share a CodeDOM element. Generic promotion moved into the class build lifecycle (single-threaded per class, lifecycle registered before publication) so parallel bindings observe final genericity; bare or ordering-conflicted references degrade to UntypedNode instead of emitting open generic types. Nested inline models containing $dynamicRef share the enclosing template's type parameters (InlineModel<TItemType>), staying open inside the template and closing only at usage sites. The C# declaration writer now emits factory fields only for parameters the class's own deserializers consume.
… templates - language gate becomes GenericTemplateLanguages (CSharp only; per-language entries pre-stubbed commented for the Dart/Java/Python/TypeScript/Go ports) - CodeFunction gains TypeParameters/IsGeneric (TypeScript generic factory fns) - CommonLanguageRefiner: import generation skips CodeTypeParameter definitions; CopyClassAsInterface carries type parameters and closes parent interface implements over the interface's own parameters - pin PHP/Ruby concrete per-binding fallback with parameterized tests (no generics in either language, permanent concrete path)
Render generic model declarations (class X<TItemType extends Parsable>) with constructor-injected ParsableFactory<T> fields owned only by the parameters each class's own deserializers consume, forwarding factories to generic bases. Deserializer/executor sites resolve CodeTypeParameter properties through the injected factory and close generic classes with closures at usage sites.
GetTypeString now renders generic type arguments and CodeTypeParameter names. Generic models declare <TItemType extends Parsable> with a synthesized constructor injecting ParsableFactory fields (own-deserializer parameters only) and super-forwarding for generic bases. Deserializers and executor send calls use the injected factory fields and instantiating lambdas.
…cription binding Generic models declare TypeVar + Generic[TItemType] bases with a cached __class_getitem__ that binds _item_type on a dynamic subclass, so existing class-object factory call sites work unchanged (PaginatedTemplate[User]). The factory becomes a classmethod returning cls(); parameter-typed properties deserialize through type(self)._item_type.
…ons for dynamicRef bindings Generic models convert to generic interfaces (interface X<TItemType>) whose factory and serializer functions take the item factory/serializer as leading parameters; executor metadata closes the generics with partial application (createXFromDiscriminatorValue(createUserFromDiscriminatorValue)) since the runtime constructs object literals without constructors.
Generic models declare type X[TItemType serialization.Parsable] structs with unexported ParsableFactory fields set by generic constructors; able-interfaces carry the type parameters and closed usages instantiate with them (NewPaginatedTemplate[Userable](CreateUserFromDiscriminatorValue)). Nullable parameter-typed properties no longer render with a pointer star.
Open GenericTemplateLanguages to Java, Dart, Python, TypeScript and Go with per-language integration expectations for the generic fixtures; PHP and Ruby remain on the permanent concrete per-binding path. Extend ReadGeneratedModelText to Dart/PHP/Ruby outputs.
… self-references IsSelfReferenceToEnclosingTemplate compared two null reference ids as equal, so when the template itself is inline (operation-local with a $defs binding), every nested inline object property was forwarded to the template instead of materializing its own model (metadata: MyResponse<TItemType>). Require a non-empty reference id before comparing.
AllTypes does not expand GenericTypeParameterValues, so request builders and models referencing PaginatedTemplate<User> never imported user.dart and the generated code did not compile. Mirror the Java generic-argument using pass, keeping same-namespace arguments since Dart imports sibling model files.
…rete path - the bare-reference pre-scan now traverses $ref targets, so a bare template reference hiding behind a wrapper $ref hop is recorded and the template deterministically stays concrete instead of degrading order-dependently - templates carrying discriminators are excluded from generics (their polymorphism relies on factory methods generic classes skip) - bindings whose anchor-bound schema itself contains a dynamic reference are excluded (the bound class would be promoted to generic, emitting open generics at usage sites); the Phase 2 suffixed concrete classes cover them - drop stale agent TODO comments from GenericTemplateLanguages
…odel builds WaitForGenericModelParameters blocked on another class's CountdownEvent for every resolved class. Property-type resolution runs while the current thread holds the enclosing class's lifecycle monitor, so two parallel threads building mutually-referencing models (A.b -> B, B.a -> A) could each wait forever on the other. Track a thread-local class build depth and skip the wait while it is positive — returning the mid-build class is the pre-existing tolerated behavior for circular references — and skip entirely for languages that never promote generic models.
…or resolves to a composed type An anchor bound via $ref to a oneOf/anyOf component resolves to a union type which cannot serve as a generic argument; the generic closing would then emit usages with fewer type arguments than declared (CS0305 and equivalent). When any anchor fails to resolve to a single type, restart the binding frame as concrete (suffixed) before the class is created, keeping full union typing in the per-binding class instead of degrading the endpoint.
… the bare-reference pre-scan memo Error-mapping types now expand GenericTypeParameterValues like return types so argument classes of generic error templates are not trimmed away. The bare-reference pre-scan keeps one shared (schema, scope) memo across operations instead of a per-operation visited set, so large specs do not re-walk shared component graphs per operation; a schema first walked in binding scope is still re-walked when later reached bare.
…eneric ceilings AddGenericTypeArgumentsImports moves to CommonLanguageRefiner with a keepSameNamespaceArguments flag (Java false, Dart true). Extend the generic ceilings note: the bare-reference pre-scan ordering limitation and generic models dropping constructor default values.
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.
Master Implementation PR
This is the master implementation PR for multi-language generic template support of
$dynamicRefbindings (Phase 4b of #7815). It tracks the complete implementation; we will split this into multiple smaller PRs before final review.What's here
Derived<T> : Base<T>), shared nested inline models, concrete-path fallbacks for unsupported shapes (discriminators, recursive bound anchors, composed anchor targets), and deterministic bare/bound resolutionParsableFactory<T>), Python (__class_getitem__subscription binding), TypeScript (generic interfaces + factory functions closed by partial application), Go (generic structs + able-interfaces)Planned split
Roughly one PR per language on top of a shared-contract PR, following the commit boundaries already in this branch.
Review on individual units will happen in the split PRs; this PR exists to make the overall direction and progress visible. The Phase 4b checkbox in #7815 stays open until the split PRs merge.