Firstborn/Library/PackageCache/com.unity.burst@1.7.3/Tests/Runtime/Shared/050-TestStructs.cs

1753 lines
53 KiB
C#
Raw Normal View History

2023-03-28 13:24:16 -04:00
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Burst.Compiler.IL.Tests.Helpers;
using NUnit.Framework;
using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityBenchShared;
namespace Burst.Compiler.IL.Tests
{
internal partial class TestStructs
{
[TestCompiler]
public static float test_struct_func_call_by_value()
{
var localVar = new CustomStruct();
localVar.firstfield = 94;
localVar.value = 123;
return byvalue_function_helper(localVar);
}
[TestCompiler]
public static float test_struct_func_call_by_ref()
{
var localVar = new CustomStruct
{
firstfield = 94,
value = 123
};
byref_function_helper(ref localVar);
return localVar.value;
}
[TestCompiler]
public static float test_struct_func_call_instance()
{
var localVar = new CustomStruct2 { value = 123 };
return localVar.returnDoubleValue();
}
[TestCompiler]
public static float test_struct_constructor_nondefault()
{
var localVar = new CustomStruct2(123.0f);
return localVar.value;
}
[TestCompiler]
public static float test_struct_constructor_default()
{
var localVar = new CustomStruct2();
localVar.value = 1;
return localVar.value;
}
[TestCompiler]
public static float test_struct_copysemantic()
{
var a = new CustomStruct2 { value = 123.0f };
var b = a;
b.value = 345;
return b.value;
}
[TestCompiler]
public static float test_struct_nested()
{
var a = new TestNestedStruct { v1 = { x = 5 } };
return a.v1.x;
}
[TestCompiler(1.0f)]
public static float test_struct_multiple_fields(float x)
{
var v = new TestVector4
{
x = 1.0f,
y = 2.0f,
z = 3.0f,
w = 4.0f
};
return x + v.x + v.y + v.z + v.w;
}
[TestCompiler]
public static float test_struct_multi_assign()
{
var a = new MultiAssignStruct(2.0F);
return a.x + a.y + a.z;
}
[TestCompiler]
public static int test_custom_struct_return_simple()
{
var a = return_value_helper_simple(1, 2);
return a.firstfield + a.value;
}
[TestCompiler]
public static int test_custom_struct_return_constructor()
{
var a = return_value_helper_constructor(1, 2);
return a.firstfield + a.value;
}
[TestCompiler]
public static int test_struct_self_reference()
{
var a = new SelfReferenceStruct
{
Value = 1
};
return a.Value;
}
[TestCompiler]
public static int test_struct_deep()
{
var deep = new DeepStruct2();
deep.value.value.SetValue(10);
return deep.value.value.GetValue() + deep.value.value.value;
}
[TestCompiler(2)]
public static int test_struct_empty(int x)
{
var emptyStruct = new EmptyStruct();
var result = emptyStruct.Increment(x);
return result;
}
[TestCompiler]
public static float test_struct_with_static_fields()
{
StructWithStaticVariables myStruct = new StructWithStaticVariables();
myStruct.myFloat = 5;
myStruct = copy_struct_with_static_by_value(myStruct);
mutate_struct_with_static_by_ref_value(ref myStruct);
return myStruct.myFloat;
}
[TestCompiler(true)]
[TestCompiler(false)]
public static bool TestStructWithBoolAsInt(bool value)
{
var structWithBoolAsInt = new StructWithBoolAsInt(value);
return structWithBoolAsInt;
}
[TestCompiler]
[Ignore("IL Instruction LEAVE not yet supported")]
public static int TestStructDisposable()
{
using (var structDisposable = new StructDisposable())
{
return structDisposable.x + 1;
}
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_InstructionStsfldNotSupported)]
public static void TestStructWithStaticFieldWrite()
{
var test = new StructWithStaticField();
test.CheckWrite();
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_LoadingFromNonReadonlyStaticFieldNotSupported)]
public static void TestStructWithStaticFieldRead()
{
var test = new StructWithStaticField();
test.CheckRead();
}
[TestCompiler]
public static int TestExplicitLayoutSize()
{
return UnsafeUtility.SizeOf<Color>();
}
[TestCompiler]
public static int TestExplicitLayoutStruct()
{
var color = new Color() { Value = 0xAABBCCDD };
var a = color.Value + GetColorR(ref color) + GetColorG(color) + color.GetColorB() + color.A;
var pair = new NumberPair()
{
SignedA = -13,
UnsignedB = 37
};
var b = pair.SignedA - ((int)pair.UnsignedA) + pair.SignedB - ((int)pair.UnsignedB);
return ((int)a) + b;
}
static uint GetColorR(ref Color color)
{
return color.R;
}
static uint GetColorG(Color color)
{
return color.G;
}
[TestCompiler]
public static uint TestExplicitLayoutWrite()
{
var color = new Color() { Value = 0xAABBCCDD };
color.G = 3;
ColorWriteBByRef(ref color, 7);
return color.Value;
}
static void ColorWriteBByRef(ref Color color, byte v)
{
color.B = v;
}
[StructLayout(LayoutKind.Explicit)]
private unsafe struct ExplicitLayoutStructUnaligned
{
[FieldOffset(0)] public int a;
[FieldOffset(4)] public sbyte b;
[FieldOffset(5)] public int c;
[FieldOffset(9)] public fixed int d[4];
}
[TestCompiler]
public static unsafe int TestExplicitLayoutStructUnaligned()
{
var value = new ExplicitLayoutStructUnaligned
{
a = -2,
b = -5,
c = 9
};
value.d[0] = 1;
value.d[1] = 2;
value.d[2] = 3;
value.d[3] = 4;
return value.a + value.b + value.c + value.d[0] + value.d[1] + value.d[2] + value.d[3];
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct ExplicitLayoutStructFixedBuffer
{
[FieldOffset(0)]
public int First;
[FieldOffset(4)]
public fixed int Data[128];
public struct Provider : IArgumentProvider
{
public object Value => new ExplicitLayoutStructFixedBuffer(3);
}
public ExplicitLayoutStructFixedBuffer(int x)
{
First = x;
fixed (int* dataPtr = Data)
{
dataPtr[8] = x + 2;
}
}
}
#if UNITY_ANDROID || UNITY_IOS
[Ignore("This test fails on mobile platforms")]
#endif
[TestCompiler(typeof(ExplicitLayoutStructFixedBuffer.Provider))]
public static unsafe int TestExplicitLayoutStructFixedBuffer(ref ExplicitLayoutStructFixedBuffer x)
{
return x.First + x.Data[8];
}
[StructLayout(LayoutKind.Explicit, Size = 9)]
public struct ExplicitStructWithSize
{
[FieldOffset(0)] public int a;
[FieldOffset(4)] public sbyte b;
[FieldOffset(5)] public int c;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructSizingExplicitStructWithSize()
{
return UnsafeUtility.SizeOf<ExplicitStructWithSize>();
}
[StructLayout(LayoutKind.Sequential, Size = 9)]
public struct SequentialStructWithSize
{
public int a;
public int b;
public sbyte c;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructSizingSequentialStructWithSize()
{
return UnsafeUtility.SizeOf<SequentialStructWithSize>();
}
[StructLayout(LayoutKind.Sequential, Size = 13)]
public struct SequentialStructWithSize2
{
public int a;
public int b;
public sbyte c;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructSizingSequentialStructWithSize2()
{
return UnsafeUtility.SizeOf<SequentialStructWithSize2>();
}
[StructLayout(LayoutKind.Sequential, Size = 12, Pack = 8)]
private struct StructSequentialWithSizeAndPack8
{
public double FieldA;
public int FieldB;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructSizingSequentialStructWithSizeAndPack8()
{
return UnsafeUtility.SizeOf<StructSequentialWithSizeAndPack8>();
}
[StructLayout(LayoutKind.Explicit, Size = 12, Pack = 8)]
private struct StructExplicitWithSizeAndPack8
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructSizingSequentialStructWithSizeAndPack()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack8>();
}
private struct StructExplicitWithSizeAndPack8Wrapper
{
public byte FieldA;
public StructExplicitWithSizeAndPack8 FieldB;
public StructExplicitWithSizeAndPack8Wrapper(byte a, StructExplicitWithSizeAndPack8 b)
{
FieldA = a;
FieldB = b;
}
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructExplicitWithSizeAndPack8Wrapper()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndPack8Wrapper>();
}
[StructLayout(LayoutKind.Sequential, Size = 10)]
private struct StructSequentialWithSizeSmallerThanActual
{
public double FieldA;
public int FieldB;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructSequentialWithSizeSmallerThanActual()
{
return UnsafeUtility.SizeOf<StructSequentialWithSizeSmallerThanActual>();
}
[StructLayout(LayoutKind.Explicit, Size = 10)]
private struct StructExplicitWithSizeSmallerThanActual
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructExplicitWithSizeSmallerThanActual()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeSmallerThanActual>();
}
[StructLayout(LayoutKind.Explicit, Size = 12)]
private struct StructExplicitWithSizeAndOverlappingFields
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(4)]
public int FieldB;
[FieldOffset(8)]
public int FieldC;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructExplicitWithSizeAndOverlappingFields()
{
return UnsafeUtility.SizeOf<StructExplicitWithSizeAndOverlappingFields>();
}
[StructLayout(LayoutKind.Explicit, Size = 12)]
private struct StructExplicitWithSize
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructExplicitWithSize()
{
return UnsafeUtility.SizeOf<StructExplicitWithSize>();
}
[StructLayout(LayoutKind.Explicit, Size = 17)]
private struct StructExplicitWithSize17
{
[FieldOffset(0)]
public double FieldA;
[FieldOffset(8)]
public int FieldB;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
public static unsafe int TestStructExplicitWithSize17()
{
return UnsafeUtility.SizeOf<StructExplicitWithSize17>();
}
[StructLayout(LayoutKind.Explicit)]
public struct ExplicitStructEmpty { }
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
public static unsafe int TestStructSizingExplicitStructEmpty()
{
return UnsafeUtility.SizeOf<ExplicitStructEmpty>();
}
public struct ExplicitStructEmptyContainer
{
public ExplicitStructEmpty A;
public int B;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
public static unsafe int TestEmptyStructEmbeddedInStruct()
{
return UnsafeUtility.SizeOf<ExplicitStructEmptyContainer>();
}
[StructLayout(LayoutKind.Explicit, Size = 0)]
public struct ExplicitStructEmptyWithSize { }
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
public static unsafe int TestStructSizingExplicitStructEmptyWithSize()
{
return UnsafeUtility.SizeOf<ExplicitStructEmptyWithSize>();
}
public struct SequentialStructEmptyNoAttributes { }
[TestCompiler]
public static unsafe int TestStructSizingSequentialStructEmptyNoAttributes()
{
return UnsafeUtility.SizeOf<SequentialStructEmptyNoAttributes>();
}
[StructLayout(LayoutKind.Sequential)]
public struct SequentialStructEmpty { }
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
public static unsafe int TestStructSizingSequentialStructEmpty()
{
return UnsafeUtility.SizeOf<SequentialStructEmpty>();
}
[StructLayout(LayoutKind.Sequential, Size = 0)]
public struct SequentialStructEmptyWithSize { }
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructZeroSizeNotSupported)]
public static unsafe int TestStructSizingSequentialStructEmptyWithSize()
{
return UnsafeUtility.SizeOf<SequentialStructEmptyWithSize>();
}
[StructLayout(LayoutKind.Sequential, Size = 1)]
public struct SequentialStructEmptyWithNonZeroSize { }
[TestCompiler]
public static unsafe int TestStructSizingSequentialStructEmptyWithNonZeroSize()
{
return UnsafeUtility.SizeOf<SequentialStructEmptyWithNonZeroSize>();
}
[StructLayout(LayoutKind.Auto)]
public struct AutoStruct
{
public int a;
}
[TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructWithAutoLayoutNotSupported)]
public static unsafe int TestAutoStruct()
{
return UnsafeUtility.SizeOf<AutoStruct>();
}
[TestCompiler]
public static int TestNestedExplicitLayouts()
{
var nested = new NestedExplicit0()
{
Next = new NestedExplicit1()
{
Next = new NestedExplicit2()
{
FValue = 13.37f
}
}
};
var a = nested.NextAsInt + nested.Next.NextAsInt + nested.Next.Next.IValue;
nested.Next.Next.FValue = 0.0042f;
var b = nested.NextAsInt + nested.Next.NextAsInt + nested.Next.Next.IValue;
return a + b;
}
[TestCompiler]
public static int TestNestedExplicitLayoutsSize()
{
return UnsafeUtility.SizeOf<NestedExplicit0>();
}
[TestCompiler]
public static uint TestBitcast()
{
return new FloatRepr()
{
Value = 13.37f
}.AsUint;
}
[TestCompiler]
public static uint TestExplicitStructFromCall()
{
return ReturnStruct().Value + ReturnStruct().R;
}
static Color ReturnStruct()
{
return new Color()
{
R = 10,
G = 20,
B = 30,
A = 255
};
}
[TestCompiler]
public static unsafe uint TestExplicitLayoutStructWithFixedArray()
{
var x = new FixedArrayExplitLayoutStruct()
{
UpperUInt = 0xAABBCCDD,
LowerUInt = 0xEEFF3344
};
uint sum = 0;
for (int i = 0; i < 8; i++)
{
sum += x.Bytes[i];
if (i < 4) sum += x.Shorts[i];
}
return x.UpperUInt + x.LowerUInt + sum;
}
[TestCompiler]
public static unsafe int TestExplicitLayoutStructWithFixedArraySize()
{
return UnsafeUtility.SizeOf<FixedArrayExplitLayoutStruct>();
}
public struct StructInvalid
{
public string WowThatStringIsNotSupported;
}
//private struct StructInvalidProvider : IArgumentProvider
//{
// public object[] Arguments => new object[] { new StructInvalid() };
//}
private static CustomStruct return_value_helper_simple(int a, int b)
{
CustomStruct val;
val.firstfield = a;
val.value = b;
return val;
}
private static CustomStruct return_value_helper_constructor(int a, int b)
{
return new CustomStruct(a, b);
}
private static float byvalue_function_helper(CustomStruct customStruct)
{
return customStruct.value * 2;
}
private static void byref_function_helper(ref CustomStruct customStruct)
{
customStruct.value = customStruct.value * 2;
}
static StructWithStaticVariables copy_struct_with_static_by_value(StructWithStaticVariables byValue)
{
byValue.myFloat += 2;
return byValue;
}
static void mutate_struct_with_static_by_ref_value(ref StructWithStaticVariables byValue)
{
byValue.myFloat += 2;
}
private struct EmptyStruct
{
public int Increment(int x)
{
return x + 1;
}
}
private struct CustomStruct
{
public int firstfield;
public int value;
public CustomStruct(int a, int b)
{
firstfield = a;
value = b;
}
}
struct DeepStruct2
{
#pragma warning disable 0649
public DeepStruct1 value;
#pragma warning restore 0649
}
struct DeepStruct1
{
#pragma warning disable 0649
public DeepStruct0 value;
#pragma warning restore 0649
}
struct DeepStruct0
{
public int value;
public void SetValue(int value)
{
this.value = value;
}
public int GetValue()
{
return value;
}
}
private struct CustomStruct2
{
public float value;
public float returnDoubleValue()
{
return value;
}
public CustomStruct2(float initialValue)
{
value = initialValue;
}
}
private struct TestVector4
{
public float x;
public float y;
public float z;
public float w;
}
private struct StructWithBoolAsInt
{
private int _value;
public StructWithBoolAsInt(bool value)
{
_value = value ? 1 : 0;
}
public static implicit operator bool(StructWithBoolAsInt val)
{
return val._value != 0;
}
}
private struct TestNestedStruct
{
public TestVector4 v1;
}
private struct MultiAssignStruct
{
public float x;
public float y;
public float z;
public MultiAssignStruct(float val)
{
x = y = z = val;
}
}
private struct SelfReferenceStruct
{
#pragma warning disable 0649
public int Value;
public unsafe SelfReferenceStruct* Left;
public unsafe SelfReferenceStruct* Right;
#pragma warning restore 0649
}
private struct StructForSizeOf
{
#pragma warning disable 0649
public IntPtr Value1;
public Float4 Vec1;
public IntPtr Value2;
public Float4 Vec2;
#pragma warning disable 0649
}
private struct StructWithStaticField
{
public static int MyField;
public void CheckWrite()
{
MyField = 0;
}
public int CheckRead()
{
return MyField;
}
}
private struct Float4
{
#pragma warning disable 0649
public float x;
public float y;
public float z;
public float w;
#pragma warning restore 0649
}
private struct StructWithStaticVariables
{
#pragma warning disable 0414
#pragma warning disable 0649
const float static_const_float = 9;
static string static_string = "hello";
public float myFloat;
public Float4 myFloat4;
static float static_float_2 = 5;
#pragma warning restore 0649
#pragma warning restore 0414
}
struct StructDisposable : IDisposable
{
public int x;
public void Dispose()
{
x++;
}
}
[StructLayout(LayoutKind.Explicit)]
private struct Color
{
[FieldOffset(0)] public uint Value;
[FieldOffset(0)] public byte R;
[FieldOffset(1)] public byte G;
[FieldOffset(2)] public byte B;
[FieldOffset(3)] public byte A;
public byte GetColorB()
{
return B;
}
}
[StructLayout(LayoutKind.Explicit)]
private struct NumberPair
{
[FieldOffset(0)] public uint UnsignedA;
[FieldOffset(0)] public int SignedA;
[FieldOffset(4)] public uint UnsignedB;
[FieldOffset(4)] public int SignedB;
}
[StructLayout(LayoutKind.Explicit)]
private struct NestedExplicit0
{
[FieldOffset(0)] public NestedExplicit1 Next;
[FieldOffset(0)] public int NextAsInt;
}
[StructLayout(LayoutKind.Explicit)]
private struct NestedExplicit1
{
[FieldOffset(0)] public NestedExplicit2 Next;
[FieldOffset(0)] public int NextAsInt;
}
[StructLayout(LayoutKind.Explicit)]
private struct NestedExplicit2
{
[FieldOffset(0)] public float FValue;
[FieldOffset(0)] public int IValue;
}
[StructLayout(LayoutKind.Explicit)]
private struct FloatRepr
{
[FieldOffset(0)] public float Value;
[FieldOffset(0)] public uint AsUint;
}
[StructLayout(LayoutKind.Explicit, Size = 24)]
private struct PaddedStruct
{
[FieldOffset(8)] public int Value;
}
[StructLayout(LayoutKind.Explicit)]
private unsafe struct FixedArrayExplitLayoutStruct
{
[FieldOffset(0)] public fixed byte Bytes[8];
[FieldOffset(0)] public fixed ushort Shorts[4];
[FieldOffset(0)] public uint UpperUInt;
[FieldOffset(4)] public uint LowerUInt;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Chunk
{
[FieldOffset(0)] public Chunk* Archetype;
[FieldOffset(8)] public Chunk* metaChunkEntity;
[FieldOffset(16)] public int Count;
}
[TestCompiler]
public static unsafe int TestRegressionInvalidGetElementPtrStructLayout()
{
Chunk* c = stackalloc Chunk[1];
c[0].Archetype = null;
c[0].metaChunkEntity = null;
c[0].Count = 0;
return TestRegressionInvalidGetElementPtrStructLayoutInternal(0, 1, &c);
}
public static unsafe int TestRegressionInvalidGetElementPtrStructLayoutInternal(int index, int limit, Chunk** currentChunk)
{
int rValue = 0;
while (index >= limit + 1)
{
rValue += (*currentChunk)->Count;
index += 1;
}
return rValue;
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct Packet
{
[FieldOffset(0)] public int data;
[FieldOffset(0)] public fixed byte moreData[1500];
}
[TestCompiler]
public static unsafe int TestExplicitSizeReporting()
{
return sizeof(Packet);
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitStructPackedButWithHoles
{
[FieldOffset(0)]
public byte A;
[FieldOffset(1)]
public long B;
[FieldOffset(21)]
public byte C;
}
[TestCompiler]
public static int TestExplicitStructPackedButWithHolesSize()
{
return UnsafeUtility.SizeOf<ExplicitStructPackedButWithHoles>();
}
[TestCompiler]
public static unsafe int TestExplicitStructPackedButWithHolesOffsetC()
{
var value = new ExplicitStructPackedButWithHoles();
var addressStart = &value;
var addressField = &value.C;
return (int)((byte*)addressField - (byte*)addressStart);
}
private struct ExplicitStructPackedButWithHolesContainer
{
public ExplicitStructPackedButWithHoles A;
public int B;
public ExplicitStructPackedButWithHoles C;
}
[TestCompiler]
public static int TestExplicitStructPackedButWithHolesContainerSize()
{
return UnsafeUtility.SizeOf<ExplicitStructPackedButWithHolesContainer>();
}
[TestCompiler]
public static unsafe int TestExplicitStructPackedButWithHolesContainerOffsetC()
{
var value = new ExplicitStructPackedButWithHolesContainer();
var addressStart = &value;
var addressField = &value.C;
return (int)((byte*)addressField - (byte*)addressStart);
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitStructNotPackedWithHoles
{
[FieldOffset(4)]
public int A;
[FieldOffset(12)]
public int B;
}
[TestCompiler]
public static int TestExplicitStructNotPackedWithHolesSize()
{
return UnsafeUtility.SizeOf<ExplicitStructNotPackedWithHoles>();
}
[TestCompiler]
public static float TestExplicitStructNested()
{
StructWithNestUnion b;
b.Value.Min = 5.0f;
return b.Value.Min;
}
[TestCompiler]
public static float TestExplicitStructNestedAsArgument()
{
float Helper(StructWithNestUnion outer)
{
return outer.Value.Min;
}
return Helper(new StructWithNestUnion
{
Value = new UnionValue { Min = 5.0f }
});
}
public struct StructWithNestUnion
{
public UnionValue Value;
}
[StructLayout(LayoutKind.Explicit)]
public struct UnionValue
{
[FieldOffset(0)]
public float Min;
[FieldOffset(4)]
public float Max;
[FieldOffset(0)]
public uint Property;
}
#if UNITY_ANDROID || UNITY_IOS
[Ignore("This test fails on mobile platforms")]
#endif
[TestCompiler(typeof(NetworkEndPoint.Provider), typeof(NetworkEndPoint.Provider), ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructByValueNotSupported)]
public static bool TestABITransformIntoExplicitLayoutTransform(NetworkEndPoint a, NetworkEndPoint b)
{
return a.Compare(b);
}
[StructLayout(LayoutKind.Explicit)]
public unsafe struct NetworkEndPoint
{
internal const int ArrayLength = 2;
[FieldOffset(0)]
internal fixed byte data[ArrayLength];
[FieldOffset(0)]
internal ushort family;
[FieldOffset(28)]
internal int length;
public bool Compare(NetworkEndPoint other)
{
if (length != other.length)
return false;
return true;
}
public class Provider : IArgumentProvider
{
public object Value => default(NetworkEndPoint);
}
}
public struct SequentialStructWithPaddingAndVectorField
{
public byte a;
public float2 b;
public class Provider : IArgumentProvider
{
public object Value => new SequentialStructWithPaddingAndVectorField { a = 1, b = new float2(4, 5) };
}
}
#if UNITY_ANDROID || UNITY_IOS
[Ignore("This test fails on mobile platforms")]
#endif
[TestCompiler(typeof(SequentialStructWithPaddingAndVectorField.Provider))]
public static int TestSequentialStructWithPaddingAndVectorField(ref SequentialStructWithPaddingAndVectorField value)
{
return (int)value.b.x;
}
private static void TestSequentialStructWithPaddingAndVectorFieldRefHelper(ref SequentialStructWithPaddingAndVectorField value)
{
value.b.yx = value.b;
value.b = value.b.yx;
}
#if UNITY_ANDROID || UNITY_IOS
[Ignore("This test fails on mobile platforms")]
#endif
[TestCompiler(typeof(SequentialStructWithPaddingAndVectorField.Provider))]
public static int TestSequentialStructWithPaddingAndVectorFieldRef(ref SequentialStructWithPaddingAndVectorField value)
{
TestSequentialStructWithPaddingAndVectorFieldRefHelper(ref value);
return (int)value.b.x;
}
[TestCompiler]
public static unsafe int TestSequentialStructWithPaddingAndVectorFieldPtr()
{
var vec = new float2(1, 2);
var vecPtr = &vec;
var value = new SequentialStructWithPaddingAndVectorField();
value.b = *vecPtr;
return (int)value.b.x;
}
[TestCompiler]
public static unsafe int TestCreatingVectorTypeFromNonVectorScalarType()
{
var x = (short)4;
var value = new int4(x, x, x, x);
return value.w;
}
[StructLayout(LayoutKind.Explicit)]
public struct ExplicitVectors
{
[FieldOffset(0)]
public int A;
[FieldOffset(4)]
public int2 B; //NB: Any Vector type is sufficient
}
[TestCompiler]
public static unsafe int TestVectorLoadFromExplicitStruct()
{
var header = new ExplicitVectors { };
return header.B.x;
}
[TestCompiler(DataRange.Standard)]
public static unsafe int TestVectorStoreToExplicitStruct(ref int2 a)
{
var header = new ExplicitVectors { B = a };
return header.B.x;
}
[TestCompiler(typeof(StructWithNonBlittableTypes), ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotBlittableForFunctionPointer)]
public static unsafe int TestStructWithNonBlittableTypes(ref StructWithNonBlittableTypes a)
{
var checksum = 0;
checksum = (checksum * 397) ^ a.a0;
checksum = (checksum * 397) ^ a.b0;
checksum = (checksum * 397) ^ a.b1;
checksum = (checksum * 397) ^ (a.d0 ? 10 : 0);
checksum = (checksum * 397) ^ a.a1;
checksum = (checksum * 397) ^ a.b1;
checksum = (checksum * 397) ^ a.c1;
checksum = (checksum * 397) ^ (a.d1 ? 0 : 7);
checksum = (checksum * 397) ^ a.Check;
return checksum;
}
[TestCompiler(typeof(StructWithNonBlittableTypesWithMarshalAs))]
public static unsafe int TestStructWithBlittableTypesWithMarshalAs(ref StructWithNonBlittableTypesWithMarshalAs a)
{
var checksum = 0;
checksum = (checksum * 397) ^ a.a0;
checksum = (checksum * 397) ^ a.b0;
checksum = (checksum * 397) ^ a.b1;
checksum = (checksum * 397) ^ (a.d0 ? 10 : 0);
checksum = (checksum * 397) ^ a.a1;
checksum = (checksum * 397) ^ a.b1;
checksum = (checksum * 397) ^ a.c1;
checksum = (checksum * 397) ^ (a.d1 ? 0 : 7);
checksum = (checksum * 397) ^ a.Check;
return checksum;
}
[TestCompiler]
public static int TestSizeOfStructWithBlittableTypesWithMarshalAs()
{
return UnsafeUtility.SizeOf<StructWithNonBlittableTypesWithMarshalAs>();
}
#if BURST_TESTS_ONLY
[TestCompiler(typeof(StructWithNonBlittableTypes), ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_TypeNotBlittableForFunctionPointer)]
public static int TestStructWithNonBlittableTypesOffset(ref StructWithNonBlittableTypes a)
{
return Unsafe.ByteOffset(ref a.a0, ref a.a1).ToInt32();
}
#endif
[TestCompiler(typeof(StructWithBlittableTypes))]
public static unsafe int TestStructWithBlittableTypes(ref StructWithBlittableTypes a)
{
var checksum = 0;
checksum = (checksum * 397) ^ a.a;
checksum = (checksum * 397) ^ a.b;
checksum = (checksum * 397) ^ a.c;
checksum = (checksum * 397) ^ a.d.x;
checksum = (checksum * 397) ^ a.d.y;
return checksum;
}
[TestCompiler]
public static int TestStructWithPointerDependency()
{
var test = new StructWithPointerDependency();
return test.DirectNoDependency.Value;
}
[TestCompiler]
public static uint TestExplicitStructNestedFieldAccess()
{
var buffer = new StructWithNestUnionContainer();
return buffer.Something.Value.Property;
}
public struct StructWithNestUnionContainer
{
public StructWithNestUnion Something => new StructWithNestUnion { Value = new UnionValue { Property = 42 } };
}
public struct StructWithBlittableTypes : IArgumentProvider
{
public StructWithBlittableTypes(int a, int b, int c, int2 d)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public int a;
public int b;
public int c;
public int2 d;
public object Value => new StructWithBlittableTypes(1, 2, 3, new int2(4, 5));
}
public struct StructWithNonBlittableTypes : IArgumentProvider
{
public StructWithNonBlittableTypes(byte a0, byte b0, byte c0, bool d0, byte a1, byte b1, byte c1, bool d1, int check)
{
this.a0 = a0;
this.b0 = b0;
this.c0 = c0;
this.d0 = d0;
this.a1 = a1;
this.b1 = b1;
this.c1 = c1;
this.d1 = d1;
this.Check = check;
}
public byte a0;
public byte b0;
public byte c0;
public bool d0;
public byte a1;
public byte b1;
public byte c1;
public bool d1;
public int Check;
public object Value => new StructWithNonBlittableTypes(1, 2, 3, true, 5, 6, 7, false, 0x12345678);
}
public struct StructWithNonBlittableTypesWithMarshalAs : IArgumentProvider
{
public StructWithNonBlittableTypesWithMarshalAs(byte a0, byte b0, byte c0, bool d0, byte a1, byte b1, byte c1, bool d1, int check)
{
this.a0 = a0;
this.b0 = b0;
this.c0 = c0;
this.d0 = d0;
this.a1 = a1;
this.b1 = b1;
this.c1 = c1;
this.d1 = d1;
this.Check = check;
}
public byte a0;
public byte b0;
public byte c0;
[MarshalAs(UnmanagedType.U1)]
public bool d0;
public byte a1;
public byte b1;
public byte c1;
[MarshalAs(UnmanagedType.U1)]
public bool d1;
public int Check;
public object Value => new StructWithNonBlittableTypesWithMarshalAs(1, 2, 3, true, 5, 6, 7, false, 0x12345678);
}
public unsafe struct StructWithPointerDependency
{
public StructWithNoDependency* PointerToNoDependency;
public StructWithNoDependency DirectNoDependency;
}
public struct StructWithNoDependency
{
public int Value;
}
[StructLayout(LayoutKind.Sequential, Size = 4096)]
public unsafe struct Sized4096
{
public byte First;
public struct Provider : IArgumentProvider
{
public object Value => new Sized4096();
}
}
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
public static unsafe void TestSized4096(ref Sized4096 a, ref Sized4096 b)
{
a = b;
}
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
public static unsafe void TestSized4096ManualCopy(ref Sized4096 a, ref Sized4096 b)
{
for (int i = 0; i < UnsafeUtility.SizeOf<Sized4096>(); i++)
{
fixed (byte* aBytes = &a.First, bBytes = &b.First)
{
aBytes[i] = bBytes[i];
}
}
}
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
public static unsafe void TestSized4096CopyToAlloca(ref Sized4096 a, ref Sized4096 b)
{
Sized4096 c = b;
(&c.First)[4] = 42;
a = c;
}
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
public static unsafe void TestSized4096CopyToStackAlloc0(ref Sized4096 a, ref Sized4096 b)
{
Sized4096* c = stackalloc Sized4096[1];
(&c->First)[4] = 42;
a = *c;
}
[TestCompiler(typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
public static unsafe void TestSized4096CopyToStackAlloc1(ref Sized4096 a, ref Sized4096 b)
{
byte* bytes = stackalloc byte[4096];
Sized4096* c = (Sized4096*)bytes;
(&c->First)[4] = 42;
a = *c;
}
[StructLayout(LayoutKind.Sequential, Size = 4096)]
public struct MultipleSized4096
{
public Sized4096 a;
public Sized4096 b;
public struct Provider : IArgumentProvider
{
public object Value => new MultipleSized4096 { a = new Sized4096(), b = new Sized4096() };
}
}
[TestCompiler(typeof(MultipleSized4096.Provider), typeof(Sized4096.Provider))]
public static void TestMultipleSized4096(ref MultipleSized4096 a, ref Sized4096 b)
{
a.a = b;
a.a.First = 42;
b = a.b;
}
[TestCompiler(typeof(MultipleSized4096.Provider), typeof(Sized4096.Provider), typeof(Sized4096.Provider))]
public static void TestMultipleSized4096CopyToAlloca(ref MultipleSized4096 a, ref Sized4096 b, ref Sized4096 c)
{
MultipleSized4096 d = default;
d.a = b;
b = d.b;
c = a.a;
a = d;
}
public unsafe struct Fixed4096
{
public fixed byte First[4096];
public struct Provider : IArgumentProvider
{
public object Value => new Fixed4096();
}
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestFixed4096(ref Fixed4096 a, ref Fixed4096 b)
{
a = b;
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestFixed4096ManualCopy(ref Fixed4096 a, ref Fixed4096 b)
{
for (int i = 0; i < UnsafeUtility.SizeOf<Fixed4096>(); i++)
{
a.First[i] = b.First[i];
}
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestFixed4096CopyToAlloca(ref Fixed4096 a, ref Fixed4096 b)
{
Fixed4096 c = b;
c.First[4] = 42;
a = c;
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestFixed4096CopyToStackAlloc0(ref Fixed4096 a, ref Fixed4096 b)
{
Fixed4096* c = stackalloc Fixed4096[1];
c->First[4] = 42;
a = *c;
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestFixed4096CopyToStackAlloc1(ref Fixed4096 a, ref Fixed4096 b)
{
byte* bytes = stackalloc byte[4096];
Fixed4096* c = (Fixed4096*)bytes;
c->First[4] = 42;
a = *c;
}
public unsafe struct PointersInStruct
{
public byte* a;
public byte* b;
public struct Provider : IArgumentProvider
{
public object Value => new PointersInStruct { a = null, b = null };
}
}
[TestCompiler(typeof(PointersInStruct.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestPointersInStruct(ref PointersInStruct a, ref Fixed4096 b)
{
fixed (byte* ptr = b.First)
{
a.a = ptr;
}
a.b = a.a;
}
private static unsafe T GenericGetT<T>(T* t) where T : unmanaged
{
return *t;
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestGetStructThroughGeneric(ref Fixed4096 a, ref Fixed4096 b)
{
fixed (void* ptr = &a)
{
var elem = GenericGetT<Fixed4096>((Fixed4096*) ptr);
b = elem;
}
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestGetStructThroughReadArrayElement(ref Fixed4096 a, ref Fixed4096 b)
{
fixed (void* ptr = &a)
{
var elem = UnsafeUtility.ReadArrayElement<Fixed4096>(ptr, 0);
b = elem;
}
}
[TestCompiler(typeof(Fixed4096.Provider), typeof(Fixed4096.Provider))]
public static unsafe void TestSetStructThroughWriteArrayElement(ref Fixed4096 a, ref Fixed4096 b)
{
fixed (void* ptr = &a)
{
var elem = a;
UnsafeUtility.WriteArrayElement(ptr, 0, elem);
}
}
private struct Fixed1021
{
public unsafe fixed byte Data[1021];
}
[TestCompiler(typeof(Fixed4096.Provider))]
public static unsafe void TestGetSetStructThroughReadWriteArrayElement(ref Fixed4096 a)
{
fixed (void* ptr1 = &a)
{
var ptr2 = (byte*) ptr1 + 1;
UnsafeUtility.WriteArrayElement(ptr1, 0, UnsafeUtility.ReadArrayElement<Fixed1021>(ptr2, 0));
}
}
[TestCompiler(typeof(Fixed4096.Provider))]
public static unsafe void TestGetSetStructThroughReadWriteArrayElementNoAlias(ref Fixed4096 a)
{
fixed (void* ptr1 = &a)
{
var ptr2 = (byte*) ptr1 + UnsafeUtility.SizeOf<Fixed1021>();
UnsafeUtility.WriteArrayElement(ptr1, 0, UnsafeUtility.ReadArrayElement<Fixed1021>(ptr2, 0));
}
}
[StructLayout(LayoutKind.Sequential, Size = 2)]
public struct WithPadding
{
public byte A;
public struct Provider : IArgumentProvider
{
public object Value => new WithPadding { A = 42 };
}
}
private static readonly WithPadding withPadding = new WithPadding { A = 42 };
[TestCompiler(typeof(ReturnBox))]
public static unsafe byte TestWithPadding(WithPadding* o)
{
*o = withPadding;
return withPadding.A;
}
[CompilerGenerated]
[StructLayout(LayoutKind.Sequential)]
public unsafe struct MyCompilerGeneratedButNotReally
{
public fixed int A[1];
}
private static readonly MyCompilerGeneratedButNotReally myCompilerGeneratedButNotReally = new MyCompilerGeneratedButNotReally { };
[TestCompiler(typeof(ReturnBox))]
public static unsafe int TestMyCompilerGeneratedButNotReallyStruct(MyCompilerGeneratedButNotReally* o)
{
*o = myCompilerGeneratedButNotReally;
fixed (int* a = myCompilerGeneratedButNotReally.A)
{
return *a;
}
}
public unsafe struct UninitFieldsAreZero
{
public fixed ushort a[3];
public fixed byte b[3];
public UninitFieldsAreZero(ushort x, ushort y, ushort z)
{
a[0] = x;
a[1] = y;
a[2] = z;
}
}
[TestCompiler(typeof(ReturnBox))]
public static unsafe void TestUninitFieldsAreZero(UninitFieldsAreZero* o)
{
o->a[0] = 42;
o->a[1] = 42;
o->a[2] = 42;
o->b[0] = 42;
o->b[1] = 42;
o->b[2] = 42;
var n = new UninitFieldsAreZero(13, 53, 4);
*o = n;
}
#pragma warning disable 0649
private struct ExplicitSizesMatchB
{
public uint U;
}
private struct ExplicitSizesMatchC
{
public ulong L;
public uint U;
public ushort S;
public byte B;
}
[StructLayout(LayoutKind.Explicit)]
private struct ExplicitSizesMatch
{
[FieldOffset(0)] public int A;
[FieldOffset(4)] public ExplicitSizesMatchB B;
[FieldOffset(4)] public ExplicitSizesMatchC C;
}
#pragma warning restore 0649
[TestCompiler]
public static unsafe int TestExplicitSizesMatch()
{
return sizeof(ExplicitSizesMatch);
}
#if BURST_TESTS_ONLY
private struct ExplicitLayoutAndBoolStruct
{
[StructLayout(LayoutKind.Explicit)]
public struct ExplicitLayoutStruct
{
[FieldOffset(0)]
public byte Byte;
}
// Having `bool` AND a field whose type is an explicit-layout struct
// causes .NET to fallback to auto-layout for the containing struct.
public bool Bool;
public ExplicitLayoutStruct ExplicitLayout;
public long Int64;
}
// This test just exists to verify that .NET does indeed do the fallback
// to auto-layout that we expect it does, since this is the underlying
// reason for us to prevent the combination of
// "bool + explicit-layout struct".
[Test]
[RestrictPlatform(".NET falls back to auto-layout for this struct, but Mono doesn't", Platform.Windows)]
public static unsafe void TestExplicitLayoutAndBoolCausesDotNetToFallbackToAutoLayout()
{
var s = new ExplicitLayoutAndBoolStruct();
var offsetStart = (IntPtr)(&s);
var offsetField = (IntPtr)(&s.Int64);
var offset = offsetField.ToInt64() - offsetStart.ToInt64();
// We would expect the offset to the `Int64` field to be 8.
// But because .NET falls back to auto-layout,
// and places the `Int64` field first,
// the offset is actually 0.
Assert.AreEqual((long)0, offset);
}
[TestCompiler(EnableAutoLayoutFallbackCheck = true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_NonBlittableAndNonManagedSequentialStructNotSupported)]
public static long TestExplicitLayoutAndBoolIsNotSupported()
{
return new ExplicitLayoutAndBoolStruct { Int64 = 8 }.Int64;
}
private struct ExplicitLayoutNestedAndBoolStruct
{
public struct SequentialLayoutStruct
{
#pragma warning disable 0649
public ExplicitLayoutStruct ExplicitLayout;
#pragma warning restore 0649
}
[StructLayout(LayoutKind.Explicit)]
public struct ExplicitLayoutStruct
{
[FieldOffset(0)]
public byte Byte;
}
#pragma warning disable 0649
public bool Bool;
public SequentialLayoutStruct SequentialLayout;
#pragma warning restore 0649
public long Int64;
}
[TestCompiler(EnableAutoLayoutFallbackCheck = true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_NonBlittableAndNonManagedSequentialStructNotSupported)]
public static long TestExplicitLayoutNestedAndBoolIsNotSupported()
{
return new ExplicitLayoutNestedAndBoolStruct { Int64 = 8 }.Int64;
}
private struct ExplicitLayoutStructAndBoolWithMarshalAs
{
[StructLayout(LayoutKind.Explicit)]
public struct ExplicitLayoutStruct
{
[FieldOffset(0)]
public byte Byte;
}
#pragma warning disable 0649
[MarshalAs(UnmanagedType.U1)]
public bool Bool;
public ExplicitLayoutStruct ExplicitLayout;
#pragma warning restore 0649
public long Int64;
}
[TestCompiler(EnableAutoLayoutFallbackCheck = true, ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_NonBlittableAndNonManagedSequentialStructNotSupported)]
public static unsafe long TestExplicitLayoutAndBoolWithMarshalAsIsNotSupported()
{
return new ExplicitLayoutStructAndBoolWithMarshalAs { Int64 = 8 }.Int64;
}
#endif
private struct SequentialLayoutAndBoolStruct
{
public struct SequentialLayoutStruct
{
#pragma warning disable 0649
public byte Byte;
#pragma warning restore 0649
}
#pragma warning disable 0649
public bool Bool;
public SequentialLayoutStruct SequentialLayout;
#pragma warning restore 0649
public long Int64;
}
[TestCompiler]
public static unsafe long TestSequentialLayoutAndBoolIsSupported()
{
return new SequentialLayoutAndBoolStruct { Int64 = 8 }.Int64;
}
}
}