Skip to content

Fix for RelativeSource AncestorType bindings not generating compiled bindings under AOT#34408

Open
BagavathiPerumal wants to merge 2 commits intodotnet:mainfrom
BagavathiPerumal:fix-34056
Open

Fix for RelativeSource AncestorType bindings not generating compiled bindings under AOT#34408
BagavathiPerumal wants to merge 2 commits intodotnet:mainfrom
BagavathiPerumal:fix-34056

Conversation

@BagavathiPerumal
Copy link
Copy Markdown
Contributor

@BagavathiPerumal BagavathiPerumal commented Mar 10, 2026

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause:

The issue occurs because KnownMarkups.cs::ProvideValueForBindingExtension had a hasRelativeSource gate that routed all RelativeSource bindings to new Binding(string, ...) — a [RequiresUnreferencedCode] constructor that gets trimmed under AOT Release builds. The gate was necessary to prevent using parent-scope x:DataType as the source type for {RelativeSource Self} bindings. However, it was too broad: when AncestorType={x:Type PageViewModel} is explicitly set, the source type is known at compile time, and a trim-safe TypedBinding should have been generated instead.

Fix Description:

The fix involves restructuring ProvideValueForBindingExtension with a two-step source type resolution:

  • TryGetRelativeSourceAncestorType (new method): looks up the AncestorType's already-resolved ITypeSymbol from the generator's context.Types dictionary. When AncestorType is present and resolvable, it produces a trim-safe TypedBinding<AncestorType, TProperty>.

  • HasExplicitBindingSource (renamed from HasRelativeSourceBinding): guards all other explicit source scenarios — RelativeSource without a resolvable AncestorType (Self, TemplatedParent, FindAncestor without a type) and x:Reference bindings. These bindings fall through to the string-based Binding fallback, since x:DataType does not describe the actual binding source in either case.

This enables compiled bindings precisely where the source type is known at compile time, while keeping the correct fallback for all other RelativeSource modes and x:Reference bindings. Zero new type resolution is needed — it reuses the ITypeSymbol already resolved by the SourceGen pipeline.

Issues Fixed

Fixes #34056

Tested the behaviour in the following platforms

  • iOS
  • Mac
  • Android
  • Windows

Note: NativeAOT scenarios are applicable only to iOS and Mac Catalyst platforms as per the .NET MAUI documentation.

Output Screenshot

Before Issue Fix After Issue Fix
34056-BeforeFix.mov
34056-AfterFix.mov

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 10, 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 -- 34408

Or

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

@dotnet-policy-service dotnet-policy-service bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Mar 10, 2026
@vishnumenon2684 vishnumenon2684 added the community ✨ Community Contribution label Mar 11, 2026
@sheiksyedm sheiksyedm requested a review from Copilot March 11, 2026 05:55
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 fixes a .NET MAUI XAML SourceGen limitation where {RelativeSource AncestorType=...} bindings inside templates were incorrectly routed to string-path Binding(...) creation, which is not trim-safe under AOT. The update enables SourceGen to produce trim-safe TypedBinding when the ancestor type is known at compile time, while preserving the existing fallback behavior for other RelativeSource modes.

Changes:

  • Update KnownMarkups.ProvideValueForBindingExtension to prefer generating a compiled TypedBinding when a RelativeSource has a resolvable AncestorType.
  • Retain the guard that prevents compiling other RelativeSource modes (e.g., Self, TemplatedParent, and untyped ancestor lookups) using ambient x:DataType.
  • Add new XAML unit tests (issue #34056) covering both the fixed ancestor-type scenario and the protected {RelativeSource Self} scenario.

Reviewed changes

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

File Description
src/Controls/src/SourceGen/KnownMarkups.cs Refines SourceGen binding compilation logic to allow compiled bindings for RelativeSource AncestorType when the type is already resolved, avoiding trim-unsafe string-path bindings under AOT.
src/Controls/tests/Xaml.UnitTests/Issues/Maui34056.xaml Adds a minimal repro XAML page covering both RelativeSource AncestorType in a DataTemplate and {RelativeSource Self} in a DataTemplate.
src/Controls/tests/Xaml.UnitTests/Issues/Maui34056.xaml.cs Adds unit tests validating the generated binding types across Runtime/XamlC/SourceGen inflators.

@sheiksyedm sheiksyedm marked this pull request as ready for review March 11, 2026 06:15
@sheiksyedm sheiksyedm added the xsg Xaml sourceGen label Mar 11, 2026
@sheiksyedm sheiksyedm added this to the .NET 10 SR6 milestone Mar 11, 2026
@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@sheiksyedm
Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Mar 14, 2026

@simonrozsival @StephaneDelcroix could you please review this one?

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 25, 2026

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

2 similar comments
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 25, 2026

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 25, 2026

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

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

Could you please resolve conflicts?

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 26, 2026

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

…iveSource AncestorType (trim-safe under AOT), while preserving the string-based Binding fallback for other RelativeSource modes where the source type is resolved at runtime.
@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

Could you please resolve conflicts?

@kubaflo, I have resolved the merge conflicts in this PR.

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 26, 2026

🤖 AI Summary

📊 Expand Full Review97c3680 · fix-34056-Changes updated.
🔍 Pre-Flight — Context & Validation

Issue: #34056 - iOS wrong trimmed Relative bindings under config XamlInflator and AOT
PR: #34408 - Fix for RelativeSource AncestorType bindings not generating compiled bindings under AOT
Platforms Affected: iOS, Mac Catalyst (NativeAOT/AOT Release; author did not test Android/Windows — issue is SourceGen path)
Files Changed: 1 implementation, 2 test

Key Findings

  • The SourceGen pipeline in KnownMarkups.cs::ProvideValueForBindingExtension had a HasExplicitBindingSource gate that routed ALL RelativeSource bindings (including AncestorType) to the RequiresUnreferencedCode Binding(string, ...) constructor.
  • This broke RelativeSource AncestorType bindings under NativeAOT/AOT trimming because the string-based Binding gets trimmed.
  • The fix adds TryGetRelativeSourceAncestorType to detect when AncestorType is set AND its ITypeSymbol is already registered in context.Types (populated by ProvideValueForRelativeSourceExtension). In that case it produces a trim-safe TypedBinding.
  • All other RelativeSource modes (Self, TemplatedParent, FindAncestor without type) still correctly fall through to the string-based Binding fallback.
  • Unresolved reviewer concern (@simonrozsival): Source={RelativeSource AncestorType={local:Maui34056PageViewModel}} (type shorthand syntax without x:Type) would produce a ValueNode instead of ElementNode for ancestorTypeNode, which the current TryGetRelativeSourceAncestorType implementation does NOT handle (it only handles ElementNode). This is an uncovered edge case.
  • Test class uses Maui34056Tests (instead of Tests per convention) — resolved as won't fix by author.
  • Test uses [XamlInflatorData] attribute (xUnit) correctly per repo testing conventions.
  • Gate: Tests FAIL without fix, PASS with fix (Gate ✅ PASSED).

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34408 Add TryGetRelativeSourceAncestorType to detect ElementNode AncestorType and reuse already-resolved ITypeSymbol from context.Types for trim-safe TypedBinding ✅ PASSED (Gate) KnownMarkups.cs, Maui34056.xaml, Maui34056.xaml.cs Does not handle ValueNode AncestorType shorthand syntax

🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Pre-register AncestorType on RelativeSource node in context.Types[markupNode] during ProvideValueForRelativeSourceExtension; trivial 10-line TryGetResolvedSourceType lookup in binding step ✅ PASS 1 file (KnownMarkups.cs) Handles both ElementNode (x:Type) and ValueNode shorthand — addresses reviewer gap
2 try-fix (claude-sonnet-4.6) Extend HasExplicitBindingSource gate itself with out ITypeSymbol? ancestorType parameter + TryResolveRelativeSourceAncestorType helper; gate returns false + resolved type when AncestorType is resolvable ✅ PASS 1 file (KnownMarkups.cs) Gate IS the resolver; handles both x:Type and ValueNode shorthand
3 try-fix (gpt-5.3-codex) Narrow explicit-source gate: allow compiled binding when binding has binding-level x:DataType AND Source is RelativeSource with AncestorType property present ✅ PASS 1 file (KnownMarkups.cs) Minimal gate-refinement; uses binding-level x:DataType as authoritative source type
4 try-fix (gpt-5.4) Source-policy narrowing: refactor gate into source policies; allow AncestorType RelativeSource with binding-local x:DataType; block x:Reference and non-ancestor RelativeSource ✅ PASS 1 file (KnownMarkups.cs) Policy-based classification; avoids type symbol resolution entirely
PR PR #34408 Add TryGetRelativeSourceAncestorType: detect ElementNode AncestorType, reuse ITypeSymbol from context.Types for trim-safe TypedBinding ✅ PASSED (Gate) 3 files Missing ValueNode shorthand support per reviewer

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 NO NEW IDEAS All major approaches covered
claude-sonnet-4.6 2 NO NEW IDEAS All major approaches covered
gpt-5.3-codex 2 NO NEW IDEAS All major approaches covered
gpt-5.4 2 NO NEW IDEAS All major approaches covered

Exhausted: Yes
Selected Fix: Candidate #1 (claude-opus-4.6) — Pre-register AncestorType in ProvideValueForRelativeSourceExtension via context.Types[markupNode]. Simplest approach (fewest lines), handles both ElementNode and ValueNode shorthand (addresses reviewer gap), co-locates type registration with type resolution, minimal invasiveness.


📋 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #34056, SourceGen/AOT trim-safety, 1 impl + 2 test files
Gate ✅ PASSED Android — tests fail without fix, pass with fix
Try-Fix ✅ COMPLETE 4 attempts, 4 passing
Report ✅ COMPLETE

Summary

PR #34408 fixes a real and important bug: {RelativeSource AncestorType=...} bindings under MauiXamlInflator + AOT fell through to the [RequiresUnreferencedCode] Binding(string,...) constructor and got trimmed at runtime. The fix is functionally correct for the primary scenario (tested) and the Gate confirms it.

However, an unresolved reviewer concern from @simonrozsival (line 708, KnownMarkups.cs) identifies a gap the fix does not address: when AncestorType is written in shorthand syntax — Source={RelativeSource AncestorType={local:PageViewModel}} — the ancestorTypeNode is a ValueNode rather than an ElementNode, and TryGetRelativeSourceAncestorType returns false, silently falling through to the string-based Binding fallback. The PR has no test for this variant.

Try-Fix exploration found a simpler alternative (Candidate #1) that resolves this gap with fewer lines of code.

Root Cause

KnownMarkups.cs::ProvideValueForBindingExtension used HasExplicitBindingSource to gate ALL RelativeSource bindings (including AncestorType) away from the TypedBinding path. This was correct for Self/TemplatedParent/FindAncestor without a type (runtime-resolved sources) but overly broad for AncestorType where the source type IS known at compile time and already registered in context.Types.

Fix Quality

PR's fix (TryGetRelativeSourceAncestorType):

  • ✅ Passes tests for {x:Type ...} syntax
  • ✅ Correctly gates Self/TemplatedParent away
  • ❌ Only handles ElementNode (x:Type extension) — does NOT handle ValueNode shorthand syntax {local:SomeVM}
  • ❌ Unresolved reviewer concern from @simonrozsival is blocking
  • ❌ 50-line method when the problem can be solved in ~10 lines

Candidate #1 (Try-Fix, claude-opus-4.6) — Recommended Alternative:

  • ✅ Passes all tests (6/6)
  • ✅ Handles BOTH ElementNode ({x:Type ...}) AND ValueNode shorthand syntax
  • ✅ Addresses the reviewer's unresolved concern
  • ✅ ~27 lines changed vs PR's ~44 lines
  • ✅ Co-locates type registration with type resolution (pre-registers ancestorTypeSymbol on the RelativeSource node via context.Types[markupNode] in ProvideValueForRelativeSourceExtension; then ProvideValueForBindingExtension does a single context.Types.TryGetValue(sourceElementNode, out sourceType) lookup)

Recommended Changes

  1. Address the ValueNode shorthand gap — Either adopt Candidate [Draft] Readme WIP #1's pre-registration approach (context.Types[markupNode] = ancestorTypeSymbol in ProvideValueForRelativeSourceExtension) or extend TryGetRelativeSourceAncestorType to also handle ValueNode via context.Compilation.GetTypeByMetadataName(...).

  2. Add a test for the shorthand syntax — Add a scenario to Maui34056.xaml that uses Source={RelativeSource AncestorType={local:Maui34056PageViewModel}} (without explicit x:Type) to prevent regression.

  3. Test class naming (minor) — Consider renaming Maui34056Tests to Tests per convention for XAML unit test nested classes (low priority — author declined but it's per-convention).

Fix Comparison

Criterion PR's Fix Candidate #1
Tests pass
Handles {x:Type T} AncestorType
Handles {local:T} shorthand AncestorType
Lines changed ~44 ~27
Reviewer concern addressed
Code co-location New method in binding step Registration in RelativeSource step

Selected Fix: PR (functionally correct for tested cases, but gaps remain) — recommend adopting Candidate #1 approach or addressing gap before merge.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates labels Mar 26, 2026
@BagavathiPerumal
Copy link
Copy Markdown
Contributor Author

BagavathiPerumal commented Mar 27, 2026

🤖 AI Summary

📊 Expand Full Review97c3680 · fix-34056-Changes updated.
🔍 Pre-Flight — Context & Validation
Issue: #34056 - iOS wrong trimmed Relative bindings under config XamlInflator and AOT PR: #34408 - Fix for RelativeSource AncestorType bindings not generating compiled bindings under AOT Platforms Affected: iOS, Mac Catalyst (NativeAOT scenarios; tested by author on iOS + Mac) Files Changed: 1 implementation, 2 test

Key Findings

  • RelativeSource AncestorType={x:Type ...} bindings inside DataTemplates fell through to new Binding(string, ...) — a [RequiresUnreferencedCode] constructor — under SourceGen + AOT, causing IL2026 warnings and runtime failures in Release mode.
  • Root cause: ProvideValueForBindingExtension had a broad HasExplicitBindingSource gate that blocked all RelativeSource bindings from the compiled binding path, even when AncestorType was explicitly set and resolvable at compile time.
  • Fix adds TryGetRelativeSourceAncestorType helper that looks up the already-resolved ITypeSymbol from context.Types (populated by ProvideValueForRelativeSourceExtension) and uses it as the source type for trim-safe TypedBinding generation.
  • Fix correctly preserves the fallback path for RelativeSource Self, TemplatedParent, and FindAncestor without a type (those remain string-based Binding since source type is unknown at compile time).
  • PR has a merge conflict (detected by MauiBot repeatedly on March 25-26); author claims to have resolved it in the last comment (March 26).
  • Test class named Maui34056Tests instead of Tests — author declined review suggestion to rename, citing clarity in stack traces. This is inconsistent with XAML unit test conventions.
  • PR not tested on Android or Windows; NativeAOT is iOS/Mac only so this is acceptable.
  • Test type: XAML Unit Tests (xUnit, [XamlInflatorData] covers Runtime, XamlC, SourceGen)
  • Test project: src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj
  • Test filter: FullyQualifiedName~Maui34056

Fix Candidates

Source Approach Test Result Files Changed Notes

PR PR #34408 Add TryGetRelativeSourceAncestorType to resolve AncestorType symbol from context.Types; restructure ProvideValueForBindingExtension to use AncestorType as source type when present ⏳ PENDING (Gate) KnownMarkups.cs Original PR
Issue: #34056 - iOS wrong trimmed Relative bindings under XamlInflator and AOT PR: #34408 - Fix for RelativeSource AncestorType bindings not generating compiled bindings under AOT Platforms Affected: iOS, Mac Catalyst (NativeAOT scenarios) Files Changed: 1 implementation, 2 test

Key Findings

  • Bug regressed in .NET 10 RC1: RelativeSource AncestorType bindings in SourceGen (MauiXamlInflator) path emit IL2026 warning and use string-based Binding(string, ...) constructor which is [RequiresUnreferencedCode] and gets trimmed under AOT Release builds, causing silent failures at runtime.
  • Root cause: ProvideValueForBindingExtension in KnownMarkups.cs had a broad HasExplicitBindingSource guard that routed ALL RelativeSource bindings (including AncestorType) to the string-based fallback, even when the AncestorType's ITypeSymbol is already known at compile time.
  • Fix: New TryGetRelativeSourceAncestorType method looks up the already-resolved AncestorType symbol from context.Types (populated by ProvideValueForRelativeSourceExtension). When present and resolvable, it sets dataTypeSymbol to produce a trim-safe TypedBinding.
  • HasExplicitBindingSource (renamed from HasRelativeSourceBinding) is kept as a guard for RelativeSource Self, TemplatedParent, FindAncestor without type, and x:Reference — where source type is resolved at runtime.
  • Tests cover: (1) RelativeSource AncestorType inside DataTemplate → must produce TypedBinding<PageViewModel, ICommand> for XamlC/SourceGen; (2) RelativeSource Self inside DataTemplate with x:DataType → must NOT produce compiled binding (SourceGen path).
  • PR has merge conflicts (multiple MauiBot notifications); author claims resolved in latest commit.
  • Issue validated: regresses from 10.0.0-rc1 on iOS only.
  • Copilot review comments (all resolved): nested class naming (Tests vs Maui34056Tests), null-conditional operator replaced with Assert.NotNull.
  • Assignees: @StephaneDelcroix, @simonrozsival

Fix Candidates

Source Approach Test Result Files Changed Notes

PR PR #34408 Add TryGetRelativeSourceAncestorType to reuse already-resolved ITypeSymbol from context.Types for AncestorType bindings; restructure ProvideValueForBindingExtension to check AncestorType first, then x:DataType, then fall back ⏳ PENDING (Gate) KnownMarkups.cs Original PR
🚦 Gate — Test Verification

Gate Result: ✅ PASSED

Platform: Windows

Type Test Name Filter

1 XamlUnitTest Maui34056 Maui34056
Step Expected Actual Result
Without fix FAIL FAIL (1/6 failed — RelativeSourceAncestorTypeInDataTemplateGeneratesCompiledBinding SourceGen) ✅
With fix PASS PASS (6/6 passed) ✅

Gate Result: ✅ PASSED

Platform: ios (XamlUnitTest — no platform required)

Type Test Name Filter

1 XamlUnitTest RelativeSourceAncestorTypeInDataTemplateGeneratesCompiledBinding Maui34056
2 XamlUnitTest RelativeSourceSelfInDataTemplateWithXDataTypeUsesStringBinding Maui34056
Step Expected Actual Result
Without fix FAIL 1 FAIL (SourceGen inflator: RelativeSourceAncestorTypeInDataTemplateGeneratesCompiledBinding) ✅
With fix PASS 6 PASS (all inflators: Runtime, XamlC, SourceGen) ✅
🔧 Fix — Analysis & Comparison

Gate Result: ✅ PASSED

Platform: Windows

Type Test Name Filter

1 XamlUnitTest Maui34056 Maui34056
Step Expected Actual Result
Without fix FAIL FAIL (1/6 failed — RelativeSourceAncestorTypeInDataTemplateGeneratesCompiledBinding SourceGen) ✅
With fix PASS PASS (6/6 passed) ✅

Fix Candidates

Source Approach Test Result Files Changed Notes

1 claude-opus-4.6 Extend HasExplicitBindingSource with out ITypeSymbol? + SourceGenContext — returns false+type when AncestorType is resolvable, eliminating need for new method ✅ PASS KnownMarkups.cs Changes existing method signature
2 claude-sonnet-4.6 Store resolved AncestorType in context.Types keyed on RelativeSource node during ProvideValueForRelativeSourceExtension; consumer retrieves with 1-hop lookup ✅ PASS KnownMarkups.cs Mutates context.Types from a different stage — potential side-effect risk
3 gpt-5.3-codex Inline AncestorType detection in ProvideValueForBindingExtension before HasExplicitBindingSource guard — no new helpers, no signature changes ✅ PASS KnownMarkups.cs Duplicates type lookup logic inline
4 gemini-3-pro-preview — ⚠️ BLOCKED — Model unsupported
PR PR #34408 Add TryGetRelativeSourceAncestorType helper; reuse ITypeSymbol from context.Types; restructure caller with clear 3-case comment block ✅ PASSED (Gate) KnownMarkups.cs Original PR — most explicit and self-documenting

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 2 No NO NEW IDEAS
Exhausted: Yes Selected Fix: PR #34408 — The PR's approach is the best: adds a clearly-named focused helper (TryGetRelativeSourceAncestorType), includes a well-documented 3-case comment block explaining each scenario, does not change existing method signatures or mutate unrelated state, and reuses already-resolved type symbols without side-effects. All 3 alternatives are functionally equivalent but trade off readability or introduce signature/state side effects.

📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #34056, 1 impl file + 2 test files
Gate ✅ PASSED Windows — 1 failed without fix, 6/6 passed with fix
Try-Fix ✅ COMPLETE 6 attempts, 6 passing; PR fix selected as best
Report ✅ COMPLETE

Summary

PR #34408 correctly fixes a regression where RelativeSource AncestorType bindings inside DataTemplates fell through to the string-based Binding([RequiresUnreferencedCode]) constructor under XamlInflator SourceGen + AOT, causing IL2026 warnings and runtime failures in iOS/Mac Catalyst Release builds. The fix is well-scoped, passes gate verification, and was confirmed as the best approach among 6 independently validated alternatives.

Merge conflict: The PR has a merge conflict (MauiBot detected it repeatedly on March 25-26). The author says it was resolved as of March 26 — reviewer should confirm before merging.

Root Cause

ProvideValueForBindingExtension had a broad HasExplicitBindingSource gate that blocked all RelativeSource bindings from the compiled TypedBinding path. When AncestorType is explicitly set, the source type is fully known at compile time — the gate was too conservative for this case, forcing a fallback to new Binding(string, ...) which is [RequiresUnreferencedCode].

Fix Quality

Strong. The fix:

  • Adds a well-named TryGetRelativeSourceAncestorType helper that reuses the ITypeSymbol already resolved and cached by ProvideValueForRelativeSourceExtension (zero new type resolution)
  • Correctly preserves the string-based Binding fallback for Self, TemplatedParent, and FindAncestor without a type (runtime-only sources)
  • 6 independent model attempts all converged on passing — the fix logic is sound
  • Tests cover both the AncestorType (should compile) and Self (should not compile) cases across all 3 inflators

Minor concern: The test class is named Maui34056Tests instead of Tests (convention for XAML unit tests). The PR author declined the review suggestion to rename it, citing stack trace clarity. Reviewers may want to decide whether to require consistency.

Selected Fix: PR #34408 (over alternatives: A1 out-param, A2 unified TryGetXDataType, A3 side-channel, A4 minimal coerce, A5 local x:DataType bypass, A6 push-model).

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #34056, iOS/Mac AOT regression from .NET 10 RC1; 3 files changed (1 fix, 2 tests)
Gate ✅ PASSED XamlUnitTest — RelativeSourceAncestorTypeInDataTemplateGeneratesCompiledBinding FAILS without fix (SourceGen inflator), 6/6 PASS with fix
Try-Fix ✅ COMPLETE 3 attempts (1 blocked: gemini unavailable), 3 passing; PR's fix selected as best
Report ✅ COMPLETE

Summary

PR #34408 fixes a .NET 10 RC1 regression where RelativeSource AncestorType bindings in the SourceGen (MauiXamlInflator) path generated a string-based Binding with [RequiresUnreferencedCode] instead of a trim-safe TypedBinding. This caused silent runtime failures in iOS/Mac AOT Release builds. The fix is clean, targeted, well-documented, and passes all tests. Three independent alternative approaches all confirmed the root cause and fix are correct.

Root Cause

ProvideValueForBindingExtension in KnownMarkups.cs had an overly broad HasExplicitBindingSource guard that returned true for ALL RelativeSource bindings — including AncestorType ones where the ITypeSymbol was already resolved in context.Types by ProvideValueForRelativeSourceExtension. The guard incorrectly prevented the trim-safe TypedBinding code path for AncestorType bindings.

Fix Quality

The PR's fix is well-structured:

  • Adds a focused TryGetRelativeSourceAncestorType helper that reuses already-resolved ITypeSymbol from context.Types — zero new type resolution
  • Restructures ProvideValueForBindingExtension with a clear 3-case documented block: (1) AncestorType → TypedBinding, (2) other RelativeSource/x:Reference → string fallback, (3) no explicit source → use x:DataType
  • Does not modify existing method signatures or mutate unrelated state
  • HasExplicitBindingSource is correctly renamed from HasRelativeSourceBinding to reflect its broader role
  • Tests cover both the fixed scenario (AncestorType → TypedBinding for XamlC/SourceGen) and the guard scenario (Self → string Binding for SourceGen)
  • Three independent alternative fixes by different AI models all passed, confirming the approach is sound
  • Minor: PR has a merge conflict (MauiBot notifications) that needs resolution before merge; author claims resolved in latest push

I’ve reviewed the AI summary comments. Only minor concerns were raised, specifically regarding the test class name. As with similar cases we’ve addressed before, the name Maui34056Tests is intentional. Because the file resides in a flat Issues/ folder with many other test files, including the issue number in the class name provides immediate clarity when examining stack traces, test runner output, or search results. Using a generic class name such as Tests would create ambiguity in this context. Therefore, retaining the existing class name is preferable.

In addition, based on the review concern regarding AncestorType={local:Maui34056PageViewModel}, this concern is not valid. According to the documentation, AncestorType must reference a UI element type via {x:Type ...}, and ViewModel types are not valid ancestors in the visual tree. Hence, the suggested syntax cannot work and does not require changes.

// The AncestorType is typically an x:Type extension (ElementNode).
// ProvideValueForRelativeSourceExtension already resolved this type
// and registered it in context.Types — just look it up.
if (ancestorTypeNode is ElementNode typeExtNode)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Source={RelativeSource AncestorType={local:Maui34056PageViewModel}} is also valid syntax and in that case I believe ancestorTypeNode would be a ValueNode with a string value, although I might be wrong. We should have a test case for this scenario too and make sure it works too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@simonrozsival, I investigated the syntax {local:Maui34056PageViewModel} and confirmed that it is not valid XAML.

The XAML compiler throws MAUIG1001: "Invalid RelativeSource Mode '(none)'" at build time because {local:...} is not a markup extension and cannot return a System.Type.

Based on official documentation, the AncestorType property must always receive a valid Type. As documented:
“The AncestorType property must be set to a Type… otherwise a XamlParseException is thrown.”
Therefore, the only valid syntax is:

{x:Type local:Maui34056PageViewModel}

This valid form is already handled by the current fix, so no additional code change or test is required for this scenario.

Documentation: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/relative-bindings?view=net-maui-10.0#bind-to-an-ancestor

Copy link
Copy Markdown
Member

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! This is a step in the right direction for compiled bindings. I hope we can build on this and build comprehensive binding source type inference in .NET 11 (#34696).

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 28, 2026

🚦 Gate — Test Verification

📊 Expand Full Gate97c3680 · fix-34056-Changes updated.

Gate Result: ✅ PASSED

Platform: ANDROID

Tests Detected

# Type Test Name Filter
1 XamlUnitTest Maui34056 Maui34056

Verification

Step Expected Actual Result
Without fix FAIL FAIL
With fix PASS PASS

Details

  • 📋 Error: Assert.IsType() Failure: Value is not the exact type
    Expected: typeof(Microsoft.Maui.Controls.Internals.TypedBinding<Maui34056PageViewModel, ICommand>)
    Actual: typeof(Microsoft.Maui.Controls.Binding...

Fix Files Reverted

  • eng/pipelines/ci-copilot.yml
  • src/Controls/src/SourceGen/KnownMarkups.cs

Base Branch: main | Merge Base: 720a9d4


@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR and removed s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates labels Mar 28, 2026
@sheiksyedm
Copy link
Copy Markdown
Contributor

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

@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

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) xsg Xaml sourceGen

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS wrong trimmed Relative bindings under config XamlInflator and AOT

9 participants