microsoft / microsoft/microsoft-ui-xaml

C++/WinRT XAML compiler does not box an Int32 value for a deferred FrameworkElement.Tag bind

Open
#11,470 1 comment 1 reaction 0 assignees View on GitHub
area-Binding area-XamlCompiler bug team-Core
Dominant language
C++
Stars
8.4k
Forks
942
Avg merge
2d 7h
Merged PRs (30d)
105

Description

### Describe the bug

In a WinUI 3 C++/WinRT project, the XAML compiler generates code that assigns an int32_t directly to an IInspectable deferred value when an element controlled by x:Load binds an Int32 property to FrameworkElement.Tag.
The compiler correctly calls winrt::box_value when the element already exists, but does not box the same value when the element has not yet been loaded.
The resulting generated .xaml.g.hpp file does not compile.
I have not yet been able to isolate this behavior in a standalone minimal reproduction project. The original XAML fragment and the corresponding generated code are included below.

### Why is this important?

This prevents the application from compiling when x:Load and a compiled x:Bind to FrameworkElement.Tag are used together.
Tag has the type Windows.Foundation.IInspectable, so a value type such as Int32 must be boxed before it can be assigned. The immediate and deferred binding paths should apply the same conversion.
The current workarounds require changing the binding architecture, such as replacing {x:Bind} with runtime {Binding}, returning an already boxed IInspectable, or avoiding x:Load.

### Steps to reproduce the bug

I have not yet reproduced this in a standalone project. The issue occurs in an existing WinUI 3 C++/WinRT application with the following setup:

1. Create a compiled "DataTemplate" whose data type exposes "FileIndex" as an "Int32" property.
2. Add a "ComboBox" controlled by a one-way "x:Load" binding.
3. Bind "FileIndex" to "FrameworkElement.Tag" using "x:Bind":
```xaml

```
4. Build the C++/WinRT project.
5. Observe that compilation of "Generated Files/XamlTypeInfo.g.cpp" fails because the generated ".xaml.g.hpp" contains an invalid assignment.

The generated update method is:
```cpp
void Update_FileIndex(int32_t obj, int32_t phase)
{
if ((phase & ((1 << 0) | NOT_PHASED)) != 0)
{
if (obj35)
{
Set_Microsoft_UI_Xaml_FrameworkElement_Tag(
obj35,
::winrt::box_value(obj),
std::nullopt);
}
else
{
obj35TagDeferredValue = obj;
}
}
}
```
"obj35TagDeferredValue" is generated as an "IInspectable", while "obj" is an "int32_t".

A clean rebuild after deleting the generated and intermediate files produces the same code.
### Actual behavior

The immediate assignment correctly boxes the value:
```cpp
Set_Microsoft_UI_Xaml_FrameworkElement_Tag(
obj35,
::winrt::box_value(obj),
std::nullopt);
```
However, the deferred branch assigns the int32_t directly to an IInspectable:
obj35TagDeferredValue = obj;
MSVC reports:
```
error C2679: binary '=': no operator found which takes a right-hand
operand of type 'int32_t' (or there is no acceptable conversion)
```
Compilation therefore fails while compiling Generated Files/XamlTypeInfo.g.cpp.

### Expected behavior

The XAML compiler should apply the required boxing conversion consistently in both the immediate and deferred binding paths.
The deferred assignment should generate code equivalent to:`
obj35TagDeferredValue = ::winrt::box_value(obj);`
The generated .xaml.g.hpp should contain valid C++/WinRT code and compile successfully.

### Screenshots
![Image_1785858242258_810.png](https://github.com/user-attachments/assets/2792f812-c44c-43e6-a54f-63ce06e12842)

![Image_1785858240977_238.png](https://github.com/user-attachments/assets/ba0fe55f-da15-446d-8756-46f16f791dcc)

### NuGet package version

2.3.1

### Windows version

_No response_

### Additional context

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the described C++/WinRT project setup with the deferred x:Load and x:Bind to FrameworkElement.Tag, then inspect the generated .xaml.g.hpp and Generated Files/XamlTypeInfo.g.cpp. Compare the immediate and deferred assignments; done means the deferred Int32 value is boxed consistently and the generated project compiles.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers, desktop
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.