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 TestStructSizingExplicitStructWithSizeAndPack8()
        {
            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.Sequential, Size = 12)]
        private struct StructSequentialWithSizeSmallerThanNatural
        {
            public double FieldA;
            public int FieldB;
        }

        [TestCompiler(ExpectCompilerException = true, ExpectedDiagnosticId = DiagnosticId.ERR_StructSizeNotSupported)]
        public static unsafe int TestStructSequentialWithSizeSmallerThanNatural()
        {
            return UnsafeUtility.SizeOf<StructSequentialWithSizeSmallerThanNatural>();
        }

        [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;
        }
    }
}