Skip to content

feat: source-generate type descriptors; quarantine reflection for AOT (ADR 0006, #95) - #195

Merged
Chris-Wolfgang merged 5 commits into
vNextfrom
feat/sourcegen-descriptors
Aug 8, 2026
Merged

feat: source-generate type descriptors; quarantine reflection for AOT (ADR 0006, #95)#195
Chris-Wolfgang merged 5 commits into
vNextfrom
feat/sourcegen-descriptors

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

Option B — fixes the IL2070 the AOT smoke surfaced. The generator emits full type descriptors for flat [BulkCopyable] types so TypeMap.Create builds the map reflection-free; the reflection path is quarantined behind [RequiresUnreferencedCode] (+ netFx/netstandard polyfills). Guarded by a descriptor conformance test (generated map == reflection map across flat/enum/[Table]+[Column]/override). 273 tests green, all 5 TFMs clean.

Nested-table types still use reflection (recursive nested descriptors = next increment). The AOT Smoke check on this PR is the real verification that IL2070 is cleared.

Copilot AI review requested due to automatic review settings July 18, 2026 19:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Chris-Wolfgang
Chris-Wolfgang force-pushed the feat/sourcegen-descriptors branch 2 times, most recently from 7fca2c7 to 8d16c3f Compare August 2, 2026 23:20
@Chris-Wolfgang
Chris-Wolfgang changed the base branch from vNext to feat/bump-abstractions-0.20-testkit-0.13 August 2, 2026 23:20
Chris-Wolfgang and others added 2 commits August 2, 2026 21:51
… (ADR 0006, #95)

Option B for the AOT-mapping gap the smoke surfaced (IL2070 in TypeMap's
reflection). The generator now emits, per flat [BulkCopyable] type, a full
GeneratedTypeDescriptor (schema/table/column metadata) mirroring TypeMap's
rules; TypeMap.Create prefers it and builds the map reflection-free (delegates
from the PR-B/C accessor registry). The reflection path is quarantined behind
[RequiresUnreferencedCode] (+ netFx/netstandard polyfills) with a justified
suppression on Create, so a [BulkCopyable] type's mapping no longer reflects
over the type — clearing IL2070 under Native AOT.

Guarded by a descriptor conformance test: the generated map equals the
reflection map of a structurally-identical plain type across flat / enum /
[Table]+[Column] / per-load-override cases. 273 unit tests green; all 5 TFMs
build clean.

Types with nested-table properties still use the reflection path (recursive
nested descriptors are the next increment). Refs #95. Docs: ADR 0006.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ement 2)

Closes the nested-table gap. The generator now emits a GeneratedNestedTableDescriptor
per nested collection and includes it in the type descriptor — but only when the
WHOLE graph is [BulkCopyable] (recursive IsFullyGeneratable eligibility check;
cyclic or partly-unmarked graphs fall back to reflection, keeping AOT honest).
TypeMap.BuildFromDescriptor resolves each child via Create (its own descriptor),
and NestedTableMap gained a reflection-free ctor pulling the collection getter
from the accessor registry — so a fully-marked graph maps with zero reflection.

Conformance extended: generated nested tables (property, child table, child
columns) match the reflection map of a plain twin graph. The AOT smoke now
includes a nested [BulkCopyable] child, so the native run proves the nested
descriptor path is IL2070-clean too. 275 unit tests green; all 5 TFMs build clean.

Refs #95. Docs: ADR 0006.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Chris-Wolfgang
Chris-Wolfgang force-pushed the feat/sourcegen-descriptors branch from 8d16c3f to c5fe6fc Compare August 3, 2026 01:53
@Chris-Wolfgang
Chris-Wolfgang changed the base branch from feat/bump-abstractions-0.20-testkit-0.13 to vNext August 3, 2026 01:53
Comment thread src/Wolfgang.Etl.SqlBulkCopy.SourceGenerator/BulkCopyAccessorGenerator.cs Outdated
Comment thread src/Wolfgang.Etl.SqlBulkCopy.SourceGenerator/BulkCopyAccessorGenerator.cs Outdated
Co-authored-by: Chris-Wolfgang <210299580+Chris-Wolfgang@users.noreply.github.com>
Chris-Wolfgang and others added 2 commits August 5, 2026 13:40
Address review feedback on BulkCopyAccessorGenerator.cs: the repeated
IsStatic/IsIndexer/GetMethod-null(/[NotMapped]) or-chains are factored into
intention-revealing predicates, mirroring TypeMap.IsReadableInstanceProperty on
the reflection path so both providers filter identically:

- IsReadableInstanceProperty  — instance property with a getter
- IsMappableProperty          — readable + not [NotMapped] (was duplicated verbatim)
- IsAccessorEmittableProperty — readable + non-ref-like + reachable getter

Each of the four call sites collapses to one named line. Behaviour is unchanged
(DescriptorConformanceTests: generated map == reflection map, 276 tests green).

Also reflow the two .Add(string.Join(...)) blocks to the Allman multi-line
argument style (opening paren on its own line) per review feedback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Raise the four Option-B descriptor types to 100% line coverage so the
per-module 90% gate passes at the vNext->main release:

- ColumnMap: descriptor ctor throws when no generated getter is registered,
  and when an enum column type has no registered converter.
- NestedTableMap: descriptor ctor throws when no generated getter is registered.
- GeneratedAccessorRegistry.RegisterEnumConverter / GeneratedTypeMapRegistry.Register:
  ArgumentNullException guards.

Adds TestModels/UnregisteredProbeEnum (never [BulkCopyable], never registered)
to drive the enum-converter defensive branch. +7 tests (283 total, all green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Chris-Wolfgang
Chris-Wolfgang merged commit e7a7f78 into vNext Aug 8, 2026
2 checks passed
@Chris-Wolfgang
Chris-Wolfgang deleted the feat/sourcegen-descriptors branch August 8, 2026 01:10
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.

3 participants