Skip to content

feat(aot): AOT/trim annotations on dependency-search surface + NativeAOT smoke#12

Open
Rolling2405 wants to merge 3 commits intoNeVeSpl:mainfrom
Rolling2405:feat/aot-trim-annotations
Open

feat(aot): AOT/trim annotations on dependency-search surface + NativeAOT smoke#12
Rolling2405 wants to merge 3 commits intoNeVeSpl:mainfrom
Rolling2405:feat/aot-trim-annotations

Conversation

@Rolling2405
Copy link
Copy Markdown

@Rolling2405 Rolling2405 commented May 1, 2026

Summary

Adds [RequiresUnreferencedCode] and [RequiresDynamicCode] annotations to the dependency-search (Tier 2) public API surface, gated #if NET10_0_OR_GREATER, so that consumers targeting NativeAOT or the trimmer receive clear, actionable warnings at their call sites instead of opaque linker errors deep in Mono.Cecil.

Also includes a NativeAOT publish smoke test (tests/NetArchTest.AotSmoke) that proves the Tier 1 surface (type loading + name/namespace/structural predicates) compiles clean under PublishAot=true /warnaserror on net10.0-windows.

netstandard2.0 is fully preserved — all annotations are #if NET10_0_OR_GREATER guarded. No public API shape change on netstandard2.0.

Intentional scope

Tier Description This PR
1 Types.FromPath / name / namespace / structural predicates ✅ Smoke-tested, zero IL warnings
2 Dependency-search predicates ✅ Annotated with [RequiresUnreferencedCode] / [RequiresDynamicCode]
3 Slice predicates ✅ Annotated
4 TypeContainer.ReflectionType / ToType() Annotated as minimum chain required — follow-up can go deeper

<IsTrimmable> / <IsAotCompatible> are deliberately NOT set on the library project. Tier 2+ surfaces use runtime reflection into Mono.Cecil internals that cannot be made fully trim-safe without a major rewrite. Setting those flags would over-promise and suppress legitimate consumer warnings.

Changes

sources/NetArchTest/NetArchTestAotMessages.cs (new)

  • Shared const string message used by all [RequiresUnreferencedCode] / [RequiresDynamicCode] attributes so the message is consistent and DRY.

sources/NetArchTest/Condition_Dependencies.cs, Predicate_Dependencies.cs, Slices/SliceCondition.cs, Functions/FunctionDelegates_Dependencies.cs

  • [RequiresUnreferencedCode] + [RequiresDynamicCode] on every method in the dependency-search pipeline (Tier 2/3 surface).

sources/NetArchTest/Dependencies/TypeParser.cs

  • Type-level [UnconditionalSuppressMessage] for IL2057/IL2077/IL2080 covering Mono.Cecil private-field reflection in the static .cctor.
  • [RequiresUnreferencedCode] + [RequiresDynamicCode] on Parse() and ParseReflectionNameToRuntimeName().

sources/NetArchTest/Dependencies/DataStructures/NamespaceTree.cs

  • Extracted the parseNames=true branch into a private ParseTokensRequiringReflection() annotated with Requires*. Add() gets [UnconditionalSuppressMessage] with justification: Tier 1 callers always pass parseNames=false so the reflection path is unreachable for them.

sources/NetArchTest/Extensions/Mono.Cecil/TypeDefinitionExtensions.cs

  • [RequiresUnreferencedCode] + [RequiresDynamicCode] on ToType().

sources/NetArchTest/Assemblies/PublicUse/TypeContainer.cs

  • Extracted Lazy<Type> initialiser lambda body into private ResolveReflectionType() annotated with Requires*. Constructor gets [UnconditionalSuppressMessage] (Tier 1 paths never read ReflectionType).

tests/NetArchTest.AotSmoke/ (new)

  • NativeAOT publish smoke: exercises Types.FromPath + name/namespace/structural predicates only.
  • PublishAot=true, TreatWarningsAsErrors=true, <NoWarn>IL2104</NoWarn> (Mono.Cecil 0.11.x is not trim-annotated upstream; IL2104 is its aggregate warning which we cannot fix from here — Tier 2 callers still receive proper annotations at their own call sites).
  • Prints OK and exits 0 when the AOT image is correct.

tests/NetArchTest.AotSmoke.Fixture/ (new)

  • Minimal netstandard2.0 DLL published alongside the smoke binary as a realistic input to Types.FromPath.

.github/workflows/net-workflow.yml

  • New aot-smoke job on windows-latest: publishes the smoke project and runs the produced exe (asserts exit 0).

Local verification

dotnet build            → 0 errors (both TFMs)
dotnet test             → 353×2 passed, 2×2 skipped
dotnet publish (win-x64) → 0 IL warnings, 0 errors
./NetArchTest.AotSmoke.exe → "OK: NetArchTest AOT smoke passed." (exit 0)

Depends on

This PR was developed on top of the net10 TFM PR (#11). It can be rebased cleanly once that lands, or reviewed independently — the #if NET10_0_OR_GREATER guards mean netstandard2.0 builds are unaffected either way.

Copilot AI and others added 3 commits April 30, 2026 20:39
Adds .NET 10 to library and test multi-targets while preserving
netstandard2.0 (library) and net8.0 (tests). Fixes packaging icon
declaration to use <None Include> (was <None Update>) so it resolves
correctly under multi-targeting on newer SDKs.

Workflow:
- Bump actions/checkout v3 -> v4
- Bump actions/setup-dotnet v3 -> v4
- Add 10.0.x to dotnet-version (alongside 3.1.x and 8.0.x)

Verified locally: 353 passed (2 skipped) on both net8.0 and net10.0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…AOT smoke test

Add [RequiresUnreferencedCode] and [RequiresDynamicCode] annotations to the
dependency-search (Tier 2) public API surface, gated #if NET10_0_OR_GREATER, so
that consumers using NativeAOT or the trimmer receive clear, actionable warnings
at their own call sites rather than opaque linker errors.

Changes
-------
sources/NetArchTest/NetArchTest.csproj
- Add net10.0 TFM alongside existing netstandard2.0
- Add IsTrimmable/IsAotCompatible guards (NET10_0_OR_GREATER only)

sources/NetArchTest/NetArchTestAotMessages.cs (new)
- Shared const message strings used by all Requires* attributes

sources/NetArchTest/Condition_Dependencies.cs
sources/NetArchTest/Predicate_Dependencies.cs
sources/NetArchTest/Slices/SliceCondition.cs
sources/NetArchTest/Functions/FunctionDelegates_Dependencies.cs
- [RequiresUnreferencedCode] + [RequiresDynamicCode] on every public/internal
  method that drives the dependency-search pipeline (Tier 2 surface)

sources/NetArchTest/Dependencies/TypeParser.cs
- Type-level [UnconditionalSuppressMessage] for IL2057/IL2077/IL2080 covering
  Mono.Cecil private-field reflection in the static cctor
- [RequiresUnreferencedCode] + [RequiresDynamicCode] on Parse() and
  ParseReflectionNameToRuntimeName()

sources/NetArchTest/Dependencies/DataStructures/NamespaceTree.cs
- Extract parseNames=true branch into private ParseTokensRequiringReflection()
  annotated with Requires*; Add() gets [UnconditionalSuppressMessage] with
  justification (runtime-guarded; Tier 1 callers pass false)

sources/NetArchTest/Extensions/Mono.Cecil/TypeDefinitionExtensions.cs
- [RequiresUnreferencedCode] + [RequiresDynamicCode] on ToType()

sources/NetArchTest/Assemblies/PublicUse/TypeContainer.cs
- Extract Lazy<Type> initialiser lambda into private ResolveReflectionType()
  annotated with Requires*; constructor gets [UnconditionalSuppressMessage]
  (Tier 1 paths never read ReflectionType)

tests/NetArchTest.AotSmoke/ (new)
- NativeAOT publish smoke that exercises Tier 1 only
  (Types.FromPath + name/namespace/structural predicates)
- PublishAot=true, TreatWarningsAsErrors=true, NoWarn IL2104
  (Mono.Cecil 0.11.x is not trim-annotated upstream — IL2104 is its aggregate
  warning; Tier 2 callers still get the proper annotations at their call sites)
- Prints 'OK' and exits 0 when the AOT image is correct

tests/NetArchTest.AotSmoke.Fixture/ (new)
- Minimal test-assembly DLL published alongside the smoke binary

Result: dotnet publish -r win-x64 -c Release produces a 4.3 MB native exe with
zero IL warnings and zero errors; the exe prints 'OK' and exits 0. All 353x2
unit tests continue to pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Publishes tests/NetArchTest.AotSmoke with PublishAot=true on win-x64 and
asserts the produced native exe exits 0. Runs on windows-latest since
NativeAOT win-x64 cross-compilation requires the MSVC toolchain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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.

2 participants