[XAML] Fix XIHR DataTemplate emission for multi-set and scoped templates - #36683
Merged
Conversation
XAML Incremental Hot Reload (XIHR, #34338) emits a DataTemplate's content as a stably-named LoadTemplate_{line}_{pos} local function so Edit-and- Continue has a stable anchor (#36482). It hoisted that function to the top of the generated method via AddLocalMethod, which broke in two ways once XIHR is actually enabled across real-world XAML: - A template value set more than once in the same scope (e.g. a `required` DataTemplate property, set in the object initializer AND as an assignment) emitted the named function twice -> CS0128 "already defined" + CS8321 "declared but never used". - Hoisting to the method top lost the lambda's lexical scope, so a template body that referenced enclosing locals (the DataTemplate variable, name scopes, resources) produced out-of-scope references -> CS0103/CS1503. These stayed latent because MAUI ships XIHR opt-in (default off), so no repo project built through this path. Fix: emit the named local function INLINE at the point of use (restoring the exact lexical scope the anonymous lambda had) and reserve each method name once per compilation unit so it is declared a single time, with every set-site re-pointing LoadTemplate at it. Adds a regression test covering a `required` DataTemplate property under XIHR (fails with CS0128 without the fix). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
StephaneDelcroix
temporarily deployed
to
copilot-pat-pool
July 20, 2026 16:38 — with
GitHub Actions
Inactive
Contributor
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36683Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36683" |
StephaneDelcroix
temporarily deployed
to
copilot-pat-pool
July 20, 2026 16:38 — with
GitHub Actions
Inactive
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
StephaneDelcroix
temporarily deployed
to
copilot-pat-pool
July 20, 2026 16:39 — with
GitHub Actions
Inactive
StephaneDelcroix
temporarily deployed
to
copilot-pat-pool
July 20, 2026 16:41 — with
GitHub Actions
Inactive
StephaneDelcroix
temporarily deployed
to
copilot-pat-pool
July 20, 2026 16:42 — with
GitHub Actions
Inactive
StephaneDelcroix
temporarily deployed
to
copilot-pat-pool
July 20, 2026 16:43 — with
GitHub Actions
Inactive
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens the XAML Incremental Hot Reload (XIHR) source generator’s DataTemplate emission so the generated C# remains valid when templates are set multiple times and when template bodies depend on the surrounding lexical scope. This unblocks enabling XIHR by default by preventing duplicate local-function declarations and scope-related compilation errors in real-world XAML.
Changes:
- Emit
DataTemplatenamedLoadTemplate_{line}_{pos}local functions inline at the point of use (instead of hoisting), preserving lexical scope for captured locals. - Add per-generation deduplication (
TryReserveTemplateMethod) so the namedLoadTemplatelocal function is declared once even if the template is assigned multiple times in the same generated method. - Add a regression test covering the “required
DataTemplateproperty set twice” scenario and reformat a couple of test blocks for readability.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/Controls/tests/SourceGen.UnitTests/XamlIncrementalHotReloadPipelineTests.cs | Adds a regression test ensuring a multiply-assigned DataTemplate emits a single named LoadTemplate_* method and still compiles under XIHR. |
| src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs | Switches XIHR template generation to inline local-function emission and uses a reservation mechanism to avoid duplicate declarations. |
| src/Controls/src/SourceGen/SourceGenContext.cs | Introduces TryReserveTemplateMethod backed by a root-context HashSet to dedupe emitted template method names. |
This was referenced Jul 21, 2026
…er XIHR Follow-up to the review of #36683. DataTemplate LoadTemplate inline emission is now deferred out of the required-property (and x:Array) value-precompute prepass to the main SetPropertiesVisitor pass under Incremental Hot Reload. The prepass runs before namescope registration, so a `required` DataTemplate property whose body used {x:Reference} or a binding to an outer element emitted a runtime-resolved (XamlServiceProvider) body, and first-wins dedup kept that instead of the main pass's compile-time optimized body. Deferring to the main pass (which runs after namescopes are registered) restores compile-time resolution and drops the redundant second LoadTemplate assignment. Non-HR builds are unchanged (the deferral is gated on EnableIncrementalHotReload). Adds a regression test asserting the required-template x:Reference resolves to __root at compile time (no SimpleValueTargetProvider), and updates the multi-set test for the single-assignment result. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| { | ||
| //we might want to move this to a separate method | ||
| var visitor = new SetPropertiesVisitor(Context); | ||
| var visitor = new SetPropertiesVisitor(Context, valuePrecomputePass: true); |
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.
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!
Description
Hardens the XAML Incremental Hot Reload (XIHR, #34338)
DataTemplatesource-gen so it produces valid code across real-world XAML. This is a prerequisite for enabling XIHR by default (#36682) — with XIHR on, several repo projects (Essentials.AI.Sample,Controls.TestCases.HostApp,Controls.Xaml.UnitTests) currently fail to build.XIHR emits a
DataTemplate's content as a stably-namedLoadTemplate_{line}_{pos}local function so Edit-and-Continue has a stable anchor (#36482). That function was hoisted to the top of the generated method viaAddLocalMethod, which breaks in two ways once XIHR is actually enabled:requiredDataTemplateproperty, which the generator sets both in the object initializer and as an assignment — emitted the named function twice →error CS0128: 'LoadTemplate_L_P' is already defined(+CS8321unused).DataTemplatevariable, name scopes, resources) generated references to names that don't exist at that scope →CS0103, with a cascadingCS1503.These stayed latent because MAUI ships XIHR opt-in (default off), so no repo project built through this path until default-on was attempted.
Fix
Emit the named local function inline at the point of use (not hoisted), which restores the exact lexical scope the anonymous lambda had, and reserve each method name once per compilation unit so it is declared a single time — every set-site just re-points
LoadTemplateat that one function. Also removes ~25 lines of buffering. Non-HR builds are unchanged (still an anonymous lambda).Tests
DataTemplate_HotReload_SetMultipleTimes_EmitsSingleNamedMethod— arequiredDataTemplateproperty under XIHR. Verified it fails without the fix (CS0128 'LoadTemplate_9_18' already defined) and passes with it.SourceGen.UnitTestssuite green (452/452), including all existing [XAML Hot Reload] SourceGen HR: successive DataTemplate edits under dotnet watch crash the app / poison HR / kill the watcher (PR #34338) #36482 XIHR tests.Controls.Xaml.UnitTestsbuilds withEnableMauiIncrementalHotReload=truewith noLoadTemplateerrors (the CS0128/CS8321 pair is gone).Related