beefytech / Beef

Beef Programming Language
http://www.beeflang.org
Other
2.49k stars 128 forks source link

Incorrect values for attribute parameters #2031

Closed Grimelios closed 1 week ago

Grimelios commented 1 week ago

In the following example, the value for parameter a of the MyAttributeAttribute constructor is erroneously passed the value of the second parameter, b:

namespace MyNamespace
{
    static
    {
        public static void Main()
        {
            for (let type in Type.Types)
            {
                if (let myAttribute = type.GetCustomAttribute<MyAttributeAttribute>())
                {
                    // Prints "A = 2, B = 2" despite (1, 2) being passed to the attribute
                    Console.WriteLine(scope $"A = {myAttribute.a}, B = {myAttribute.b}");
                }
            }
        }
    }

    [AttributeUsage(.Struct, .AlwaysIncludeTarget | .ReflectAttribute, AlwaysIncludeUser = .All)]
    public struct MyAttributeAttribute : Attribute
    {
        public uint32 a;
        public uint32 b;

        internal this(uint32 a, uint32 b)
        {
            this.a = a;
            this.b = b;
        }
    }

    [MyAttribute(1, 2)]
    public struct MyStruct
    {
    }
}

There are additional permutations in which this issue arises. For example, if b is removed, but the parameter for b remains, a is still passed b's value:

namespace MyNamespace
{
    static
    {
        public static void Main()
        {
            for (let type in Type.Types)
            {
                if (let myAttribute = type.GetCustomAttribute<MyAttributeAttribute>())
                {
                    // Prints "A = 2"
                    Console.WriteLine(scope $"A = {myAttribute.a}");
                }
            }
        }
    }

    [AttributeUsage(.Struct, .AlwaysIncludeTarget | .ReflectAttribute, AlwaysIncludeUser = .All)]
    public struct MyAttributeAttribute : Attribute
    {
        public uint32 a;

        internal this(uint32 a, uint32 b)
        {
            this.a = a;
        }
    }

    [MyAttribute(1, 2)]
    public struct MyStruct
    {
    }
}

More generally, all attribute parameters seem to be assigned the value of the last parameter:

[AttributeUsage(.Struct, .AlwaysIncludeTarget | .ReflectAttribute, AlwaysIncludeUser = .All)]
public struct MyAttributeAttribute : Attribute
{
    public uint32 a;
    public uint32 b;

    internal this(uint32 a, uint32 b, uint32 c)
    {
        // Both parameter values here are 3
        this.a = a;
        this.b = b;
    }
}

[MyAttribute(1, 2, 3)]
public struct MyStruct
{
}

Finally, the issue appears to be related to unsigned primitives (although I haven't exhaustively tested all relevant permutations, primitives or otherwise). In the examples above, using int32 rather than uint32 fixes the problem. Using a different unsigned type (say, uint16) manifests the problem.

bfiete commented 1 week ago

Fixed at 5e2f0369183776a09c50de13a14e641cb7d3c487