Skip to content

Non-boxing union access pattern does not work with IUnionMembers #84676

Description

@BarionLP

I tried combining the non boxing access pattern for unions with the union member providers interface documented here:
when looking at the generated code in .NET Lab it uses the Value property instead of the TryGetValue methods

Version Used:
5.10.0-1.26367.9 via https://lab.razor.fyi/#csharp

Steps to Reproduce:
look at the lowered code for this:

IntOrBool foo = 1;

int bar = foo switch
{
    int i => i,
    bool => -1,
};

Console.WriteLine(bar);


[System.Runtime.CompilerServices.Union]
public struct IntOrBool : IntOrBool.IUnionMembers
{
    private readonly int _intValue;
    private readonly bool _boolValue;
    private readonly byte _tag; // 0 = none, 1 = int, 2 = bool

    public IntOrBool(int? value)
    {
        if (value.HasValue)
        {
            _intValue = value.Value;
            _tag = 1;
        }
    }

    public IntOrBool(bool? value)
    {
        if (value.HasValue)
        {
            _boolValue = value.Value;
            _tag = 2;
        }
    }

    public object? Value => _tag switch
    {
        1 => _intValue,
        2 => _boolValue,
        _ => null
    };

    public bool HasValue => _tag != 0;

    public bool TryGetValue(out int value)
    {
        value = _intValue;
        return _tag == 1;
    }

    public bool TryGetValue(out bool value)
    {
        value = _boolValue;
        return _tag == 2;
    }

    public interface IUnionMembers
    {
        static IntOrBool Create(int? value) => new(value);
        static IntOrBool Create(bool? value) => new(value);
        object? Value { get; }
    }
}

Expected Behavior:

IntOrBool intOrBool = IntOrBool.IUnionMembers.Create(1);
int value;
int num2 = default(int);
bool value2;
if (intOrBool.TryGetValue(out value))
{
    int num = value;
    num2 = num;
}
else if (intOrBool.TryGetValue(out value2))
{
    num2 = -1;
}
else
{
    <PrivateImplementationDetails>.ThrowSwitchExpressionException(intOrBool);
}
int value3 = num2;
Console.WriteLine(value3);

Actual Behavior:

IntOrBool intOrBool = IntOrBool.IUnionMembers.Create(1);
object value = ((IntOrBool.IUnionMembers)intOrBool).Value;
int num2 = default(int);
if (value is int)
{
	int num = (int)value;
	num2 = num;
}
else if (value is bool)
{
	num2 = -1;
}
else
{
	<PrivateImplementationDetails>.ThrowSwitchExpressionException(intOrBool);
}
int value2 = num2;
Console.WriteLine(value2);

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions