Skip to content

Remove IsTrimmable=false from assemblies that are already AotCompatible#34573

Open
kotlarmilos wants to merge 8 commits intodotnet:net11.0from
kotlarmilos:dev/miloskotlar/remove-istrimmable-false
Open

Remove IsTrimmable=false from assemblies that are already AotCompatible#34573
kotlarmilos wants to merge 8 commits intodotnet:net11.0from
kotlarmilos:dev/miloskotlar/remove-istrimmable-false

Conversation

@kotlarmilos
Copy link
Copy Markdown
Member

@kotlarmilos kotlarmilos commented Mar 19, 2026

Description

These four assemblies already set IsAotCompatible=true (which implies IsTrimmable=true via the SDK's Microsoft.NET.Publish.targets), but the explicit IsTrimmable=false overrides that and prevents them from being trimmed in SdkOnly link mode.

The IsTrimmable=false was added in PR #10647 to lock down the value, but it was never updated when these assemblies were made AOT-compatible in PRs #21076 and #21505.

Removing IsTrimmable=false lets IsAotCompatible=true take effect, allowing these assemblies to be trimmed in SdkOnly link mode.

Affected assemblies:

  • Microsoft.Maui (Core.csproj)
  • Microsoft.Maui.Controls (Controls.Core.csproj)
  • Microsoft.Maui.Graphics (Graphics.csproj)
  • Microsoft.Maui.Controls.Xaml (Controls.Xaml.csproj)

Context

While working on reducing Debug bundle size for CoreCLR R2R builds in dotnet/macios (dotnet/macios#24909), we found these assemblies are the main ones not being trimmed in SdkOnly mode.

These assemblies already set IsAotCompatible=true (which implies
IsTrimmable=true via the SDK), but the explicit IsTrimmable=false
overrides that and prevents them from being trimmed in SdkOnly mode.

The IsTrimmable=false was added in PR dotnet#10647 (Oct 2022) to lock down
the value, but it was never updated when these assemblies were made
AOT-compatible in PRs dotnet#21076 and dotnet#21505 (2024).

Removing IsTrimmable=false lets IsAotCompatible=true take effect,
allowing these assemblies to be trimmed in SdkOnly link mode. This
reduces bundle size for both Debug and Release builds.

Affected assemblies:
- Microsoft.Maui (Core.csproj)
- Microsoft.Maui.Controls (Controls.Core.csproj)
- Microsoft.Maui.Graphics (Graphics.csproj)
- Microsoft.Maui.Controls.Xaml (Controls.Xaml.csproj)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 19, 2026 15:49
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 19, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34573

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34573"

@kotlarmilos kotlarmilos changed the base branch from main to net11.0 March 19, 2026 15:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aligns trimming behavior with existing AOT-compatibility declarations by removing explicit IsTrimmable=false overrides from key MAUI assemblies, so IsAotCompatible=true can cause them to be treated as trimmable (notably impacting SdkOnly link mode scenarios).

Changes:

  • Removed <IsTrimmable>false</IsTrimmable> from Microsoft.Maui (Core).
  • Removed <IsTrimmable>false</IsTrimmable> from Microsoft.Maui.Controls and Microsoft.Maui.Controls.Xaml.
  • Removed <IsTrimmable>false</IsTrimmable> from Microsoft.Maui.Graphics.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Core/src/Core.csproj Removes the explicit IsTrimmable=false override so the existing non-netstandard IsAotCompatible=true can drive trimming.
src/Controls/src/Core/Controls.Core.csproj Same: drops the override to allow trimming in configurations where IsAotCompatible=true is already set.
src/Controls/src/Xaml/Controls.Xaml.csproj Same: removes IsTrimmable=false to let IsAotCompatible=true imply trimming for applicable TFMs.
src/Graphics/src/Graphics/Graphics.csproj Same: removes IsTrimmable=false so trimming can apply when IsAotCompatible=true is set.

You can also share your feedback on Copilot code review. Take the survey.

@rolfbjarne
Copy link
Copy Markdown
Member

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@kotlarmilos
Copy link
Copy Markdown
Member Author

kotlarmilos commented Mar 24, 2026

CI failures (ui tests timeouts, mobile device test crashes) are pre-existing net11.0 baseline issues unrelated to this PR

@PureWeen
Copy link
Copy Markdown
Member

Shell MenuItem Trimming Regression Analysis

Investigation of the 34 VerifyShellFlyout_* Android test failures across all 6 Shell test runs.

Root Cause

Removing IsTrimmable=false makes MAUI assemblies eligible for ILLink trimming in Android Release builds (SdkOnly link mode). This causes Shell MenuItem elements to silently disappear from the flyout drawer — no runtime exception, no MissingMethodException, the items simply never render.

Evidence from ADO Test Attachments (Build 1344675)

Logcat analysis of Order 1 test (VerifyShellFlyout_Header, 1.3MB logcat):

Step Result
Navigate to Shell Feature Matrix page ✅ Found GoToTestButton, SearchBar
Tap ShellFlyoutButton → Shell page loads
Tap Options → FlyoutHeader → Apply ✅ All elements found and tapped
Open flyout drawer (hamburger icon) "Open navigation drawer" found
WaitForElement("Header") in flyout ✅ Found — drawer is open
WaitForElement("OpenFlyout") MenuItem 0 matches by ID and XPath

Page source XML at failure shows what is rendered vs what is not:

✅ Renders in flyout ❌ Missing from flyout
Header (FlyoutHeader) OpenFlyout (MenuItem)
FlyoutItems label FlyoutBehavior (MenuItem)
Home, Reports, Options (ShellContent tabs) Any MenuItem-type elements

No managed exceptions in logcat — no TypeLoadException, MissingMethodException, or MissingMemberException. The app runs fine, Shell navigation works, 152 other Shell tests pass. Only the MenuItem rendering path is broken.

Failure Pattern

  • 34 tests fail — all reference OpenFlyout AutomationId (a <MenuItem>)
  • 5 tests pass — the only ones that never touch OpenFlyout
  • 100% reproducible across all 6 Android Shell runs
  • Order 1 fails with NullReferenceException at Tap(OpenFlyout); Orders 2-34 cascade with TimeoutException

Suspected Trimmed Code Paths

The linker likely trims code in the MenuItem→flyout rendering pipeline:

  • Shell.cs line 694: if (bo is IMenuItemController) — conditional template selection for MenuItems
  • Shell.cs line 884: case MenuItem m: — MenuItem activation handler
  • ShellFlyoutItemsManager.AddMenuItems() — adds MenuItem items to the flyout collection
  • MenuShellItem (internal) — wrapper created via implicit operator
  • BaseShellItem.CreateDefaultFlyoutItemCell() — DataTemplate with Grid/Label/Image used for flyout item rendering

Shell navigation items (ShellContent, ShellSection) render through a different code path than MenuItem, which is why tabs work but MenuItems don't.

Recommendation

The PR may need [DynamicDependency] or [DynamicallyAccessedMembers] annotations on the MenuItem rendering paths to preserve them through ILLink, or the trimming scope needs to be narrowed for Shell-related assemblies.

kotlarmilos and others added 2 commits March 25, 2026 10:06
MenuShellItem is an internal type that wraps MenuItem for flyout rendering.
When MAUI assemblies become trimmable (IsTrimmable=true), the ILLink trimmer
can strip MenuShellItem and related code paths that are only reached through
event-driven and interface-dispatch patterns the linker cannot statically trace.

Add [DynamicDependency] on Shell constructor and ShellItem implicit operator
to preserve MenuShellItem and its members through trimming.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kotlarmilos
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

Add [UnconditionalSuppressMessage] for IL2026, IL2111, and IL3050 on
Shell() constructor and ShellItem.op_Implicit(MenuItem). The warnings
originate from the TypeConverter base class hierarchy
(ShellItemConverter : TypeConverter) whose inherited members like
GetProperties/GetEditor carry RequiresUnreferencedCode. These base
methods are never called by MAUI - ShellItemConverter only overrides
CanConvertFrom/To and ConvertFrom/To.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kotlarmilos
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@PureWeen
Copy link
Copy Markdown
Member

/rebase

PureWeen
PureWeen previously approved these changes Mar 31, 2026
@PureWeen PureWeen dismissed their stale review March 31, 2026 17:03

need to rerun with latest bits

@PureWeen
Copy link
Copy Markdown
Member

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@kotlarmilos
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

PureWeen
PureWeen previously approved these changes Apr 2, 2026
@PureWeen
Copy link
Copy Markdown
Member

PureWeen commented Apr 2, 2026

Trimming Regression: DragGestureRecognizer constructor trimmed

The Android Mono and iOS Mono device test runs are crashing with MissingMethodException — this is PR-specific (these jobs pass on every net11.0 baseline build).

Crash from Helix logcat (Android Mono, build 1362029, job e7834259):

E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.MissingMethodException]: 
    Activator_CannotCreateInstance, Microsoft.Maui.Controls.DragGestureRecognizer, Arg_NoDefCTorWithoutTypeName
    at System.RuntimeType+ActivatorCache..ctor + 0x99(Unknown Source)
    at System.RuntimeType+ActivatorCache.Create + 0x0(Unknown Source)

The trimmer is removing the default (parameterless) constructor of DragGestureRecognizer, which is needed by Activator.CreateInstance at runtime. This is the same class of issue as the Shell MenuShellItem fix — code paths reached via reflection/activation that the trimmer cannot statically trace.

iOS Mono shows the same APP_CRASH (exit code 80) pattern — app dies before producing any test results.

Evidence this is PR-specific:

Build Android Mono iOS Mono
net11.0 build 1363873
net11.0 build 1360789
net11.0 build 1360076
net11.0 build 1358994
net11.0 build 1357881
PR #34573 build 1362029 ❌ crash ❌ crash

Likely needs [DynamicDependency] annotations on DragGestureRecognizer (and possibly other gesture recognizer types that are activated via reflection).

rolfbjarne
rolfbjarne previously approved these changes Apr 3, 2026
@kotlarmilos kotlarmilos dismissed stale reviews from rolfbjarne and PureWeen via ea09a18 April 7, 2026 10:24
@kotlarmilos
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

…trimming

- Cell.cs: Preserve Cell subtype constructors (TextCell, EntryCell, ImageCell, SwitchCell, ViewCell)
- ViewExtensions.cs: Preserve XAML markup extension constructors for runtime XAML parsing
- MemoryTests.cs: Add DynamicallyAccessedMembers on Type params for GestureDoesNotLeak, CellsDoNotLeak, CollectionViewHeaderFooterDoesntLeak
- ControlsViewTypesTestCases.cs: Add DynamicDependency for all view types used via ClassData
- AccessibilityTests.cs: Add DynamicDependency for all control types in AllControlsTestCase

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kotlarmilos
Copy link
Copy Markdown
Member Author

/azp run maui-pr-devicetests,maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

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.

5 participants