// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
    public class StackAllocSpanExpressionsTests : CompilingTestBase
    {
        [Fact]
        public void ConversionFromPointerStackAlloc_UserDefined_Implicit()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
unsafe class Test
{
    public void Method()
    {
        Test obj1 = stackalloc int[10];
        var obj2 = stackalloc int[10];
        Span<int> obj3 = stackalloc int[10];
        int* obj4 = stackalloc int[10];
        double* obj5 = stackalloc int[10];
    }
    
    public static implicit operator Test(int* value) 
    {
        return default(Test);
    }
}", TestOptions.UnsafeReleaseDll);

            comp.VerifyDiagnostics(
                // (11,24): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'double*' is not possible.
                //         double* obj5 = stackalloc int[10];
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[10]").WithArguments("int", "double*").WithLocation(11, 24));

            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);

            var variables = tree.GetCompilationUnitRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>();
            Assert.Equal(5, variables.Count());

            var obj1 = variables.ElementAt(0);
            Assert.Equal("obj1", obj1.Identifier.Text);

            var obj1Value = model.GetSemanticInfoSummary(obj1.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj1Value.Type).PointedAtType.SpecialType);
            Assert.Equal("Test", obj1Value.ConvertedType.Name);
            Assert.Equal(ConversionKind.ImplicitUserDefined, obj1Value.ImplicitConversion.Kind);

            var obj2 = variables.ElementAt(1);
            Assert.Equal("obj2", obj2.Identifier.Text);

            var obj2Value = model.GetSemanticInfoSummary(obj2.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj2Value.Type).PointedAtType.SpecialType);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj2Value.ConvertedType).PointedAtType.SpecialType);
            Assert.Equal(ConversionKind.Identity, obj2Value.ImplicitConversion.Kind);

            var obj3 = variables.ElementAt(2);
            Assert.Equal("obj3", obj3.Identifier.Text);

            var obj3Value = model.GetSemanticInfoSummary(obj3.Initializer.Value);
            Assert.Equal("Span", obj3Value.Type.Name);
            Assert.Equal("Span", obj3Value.ConvertedType.Name);
            Assert.Equal(ConversionKind.Identity, obj3Value.ImplicitConversion.Kind);

            var obj4 = variables.ElementAt(3);
            Assert.Equal("obj4", obj4.Identifier.Text);

            var obj4Value = model.GetSemanticInfoSummary(obj4.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj4Value.Type).PointedAtType.SpecialType);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj4Value.ConvertedType).PointedAtType.SpecialType);
            Assert.Equal(ConversionKind.Identity, obj4Value.ImplicitConversion.Kind);

            var obj5 = variables.ElementAt(4);
            Assert.Equal("obj5", obj5.Identifier.Text);

            var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj5Value.Type).PointedAtType.SpecialType);
            Assert.Equal(SpecialType.System_Double, ((IPointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType);
            Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind);
        }

        [Fact]
        public void ConversionFromPointerStackAlloc_UserDefined_Explicit()
        {
            var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
unsafe class Test
{
    public void Method()
    {
        Test obj1 = (Test)stackalloc int[10];
        var obj2 = stackalloc int[10];
        Span<int> obj3 = stackalloc int[10];
        int* obj4 = stackalloc int[10];
        double* obj5 = stackalloc int[10];
    }
    
    public static explicit operator Test(Span<int> value) 
    {
        return default(Test);
    }
}", TestOptions.UnsafeReleaseDll);

            comp.VerifyDiagnostics(
                // (11,24): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'double*' is not possible.
                //         double* obj5 = stackalloc int[10];
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[10]").WithArguments("int", "double*").WithLocation(11, 24));

            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);

            var variables = tree.GetCompilationUnitRoot().DescendantNodes().OfType<VariableDeclaratorSyntax>();
            Assert.Equal(5, variables.Count());

            var obj1 = variables.ElementAt(0);
            Assert.Equal("obj1", obj1.Identifier.Text);
            Assert.Equal(SyntaxKind.CastExpression, obj1.Initializer.Value.Kind());

            var obj1Value = model.GetSemanticInfoSummary(((CastExpressionSyntax)obj1.Initializer.Value).Expression);
            Assert.Equal("Span", obj1Value.Type.Name);
            Assert.Equal("Span", obj1Value.ConvertedType.Name);
            Assert.Equal(ConversionKind.Identity, obj1Value.ImplicitConversion.Kind);

            var obj2 = variables.ElementAt(1);
            Assert.Equal("obj2", obj2.Identifier.Text);

            var obj2Value = model.GetSemanticInfoSummary(obj2.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj2Value.Type).PointedAtType.SpecialType);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj2Value.ConvertedType).PointedAtType.SpecialType);
            Assert.Equal(ConversionKind.Identity, obj2Value.ImplicitConversion.Kind);

            var obj3 = variables.ElementAt(2);
            Assert.Equal("obj3", obj3.Identifier.Text);

            var obj3Value = model.GetSemanticInfoSummary(obj3.Initializer.Value);
            Assert.Equal("Span", obj3Value.Type.Name);
            Assert.Equal("Span", obj3Value.ConvertedType.Name);
            Assert.Equal(ConversionKind.Identity, obj3Value.ImplicitConversion.Kind);

            var obj4 = variables.ElementAt(3);
            Assert.Equal("obj4", obj4.Identifier.Text);

            var obj4Value = model.GetSemanticInfoSummary(obj4.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj4Value.Type).PointedAtType.SpecialType);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj4Value.ConvertedType).PointedAtType.SpecialType);
            Assert.Equal(ConversionKind.Identity, obj4Value.ImplicitConversion.Kind);

            var obj5 = variables.ElementAt(4);
            Assert.Equal("obj5", obj5.Identifier.Text);

            var obj5Value = model.GetSemanticInfoSummary(obj5.Initializer.Value);
            Assert.Equal(SpecialType.System_Int32, ((IPointerTypeSymbol)obj5Value.Type).PointedAtType.SpecialType);
            Assert.Equal(SpecialType.System_Double, ((IPointerTypeSymbol)obj5Value.ConvertedType).PointedAtType.SpecialType);
            Assert.Equal(ConversionKind.NoConversion, obj5Value.ImplicitConversion.Kind);
        }

        [Fact]
        public void ConversionError()
        {
            CreateCompilationWithMscorlibAndSpan(@"
class Test
{
    void M()
    {
        double x = stackalloc int[10];          // implicit
        short y = (short)stackalloc int[10];    // explicit
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (6,20): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'double' is not possible.
                //         double x = stackalloc int[10];          // implicit
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[10]").WithArguments("int", "double").WithLocation(6, 20),
                // (7,19): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'short' is not possible.
                //         short y = (short)stackalloc int[10];    // explicit
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "(short)stackalloc int[10]").WithArguments("int", "short").WithLocation(7, 19));
        }

        [Fact]
        public void MissingSpanType()
        {
            CreateCompilation(@"
class Test
{
    void M()
    {
        Span<int> a = stackalloc int [10];
    }
}").VerifyDiagnostics(
                // (6,9): error CS0246: The type or namespace name 'Span<>' could not be found (are you missing a using directive or an assembly reference?)
                //         Span<int> a = stackalloc int [10];
                Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Span<int>").WithArguments("Span<>").WithLocation(6, 9),
                // (6,23): error CS0518: Predefined type 'System.Span`1' is not defined or imported
                //         Span<int> a = stackalloc int [10];
                Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "stackalloc int [10]").WithArguments("System.Span`1").WithLocation(6, 23));
        }

        [Fact]
        public void MissingSpanConstructor()
        {
            CreateCompilation(@"
namespace System
{
    ref struct Span<T>
    {
    }
    class Test
    {
        void M()
        {
            Span<int> a = stackalloc int [10];
        }
    }
}").VerifyEmitDiagnostics(
                // (11,27): error CS0656: Missing compiler required member 'System.Span`1..ctor'
                //             Span<int> a = stackalloc int [10];
                Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "stackalloc int [10]").WithArguments("System.Span`1", ".ctor").WithLocation(11, 27));
        }

        [Fact]
        public void ConditionalExpressionOnSpan_BothStackallocSpans()
        {
            CreateCompilationWithMscorlibAndSpan(@"
class Test
{
    void M()
    {
        var x = true ? stackalloc int [10] : stackalloc int [5];
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics();
        }

        [Fact]
        public void ConditionalExpressionOnSpan_Convertible()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
    void M()
    {
        var x = true ? stackalloc int [10] : (Span<int>)stackalloc int [5];
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics();
        }

        [Fact]
        public void ConditionalExpressionOnSpan_NoCast()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
    void M()
    {
        var x = true ? stackalloc int [10] : (Span<int>)stackalloc short [5];
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (7,46): error CS8346: Conversion of a stackalloc expression of type 'short' to type 'Span<int>' is not possible.
                //         var x = true ? stackalloc int [10] : (Span<int>)stackalloc short [5];
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "(Span<int>)stackalloc short [5]").WithArguments("short", "System.Span<int>").WithLocation(7, 46));
        }

        [Fact]
        public void ConditionalExpressionOnSpan_CompatibleTypes()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
    void M()
    {
        Span<int> a = stackalloc int [10];
        var x = true ? stackalloc int [10] : a;
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics();
        }

        [Fact]
        public void ConditionalExpressionOnSpan_IncompatibleTypes()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
    void M()
    {
        Span<short> a = stackalloc short [10];
        var x = true ? stackalloc int [10] : a;
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (8,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'System.Span<int>' and 'System.Span<short>'
                //         var x = true ? stackalloc int [10] : a;
                Diagnostic(ErrorCode.ERR_InvalidQM, "true ? stackalloc int [10] : a").WithArguments("System.Span<int>", "System.Span<short>").WithLocation(8, 17)
            );
        }

        [Fact]
        public void ConditionalExpressionOnSpan_Nested()
        {
            CreateCompilationWithMscorlibAndSpan(@"
class Test
{
    bool N() => true;

    void M()
    {
        var x = N()
            ? N()
                ? stackalloc int [1]
                : stackalloc int [2]
            : N()
                ? stackalloc int[3]
                : N()
                    ? stackalloc int[4]
                    : stackalloc int[5];
    }
}", TestOptions.UnsafeReleaseDll).VerifyDiagnostics();
        }

        [Fact]
        public void BooleanOperatorOnSpan_NoTargetTyping()
        {
            var source = @"
class Test
{
    void M()
    {
        if(stackalloc int[10] == stackalloc int[10]) { }
    }
}";
            CreateCompilationWithMscorlibAndSpan(source, TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (6,12): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         if(stackalloc int[10] == stackalloc int[10]) { }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 12),
                // (6,34): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         if(stackalloc int[10] == stackalloc int[10]) { }
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 34)
                );
            CreateCompilationWithMscorlibAndSpan(source, TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (6,12): error CS0019: Operator '==' cannot be applied to operands of type 'Span<int>' and 'Span<int>'
                //         if(stackalloc int[10] == stackalloc int[10]) { }
                Diagnostic(ErrorCode.ERR_BadBinaryOps, "stackalloc int[10] == stackalloc int[10]").WithArguments("==", "System.Span<int>", "System.Span<int>").WithLocation(6, 12)
            );
        }

        [Fact]
        public void NewStackAllocSpanSyntaxProducesErrorsOnEarlierVersions_Statements()
        {
            var parseOptions = new CSharpParseOptions().WithLanguageVersion(LanguageVersion.CSharp7);

            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
    void M()
    {
        Span<int> x = stackalloc int[10];
    }
}", options: TestOptions.UnsafeReleaseDll, parseOptions: parseOptions).VerifyDiagnostics(
                // (7,23): error CS8107: Feature 'ref structs' is not available in C# 7. Please use language version 7.2 or greater.
                //         Span<int> x = stackalloc int[10];
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "stackalloc int[10]").WithArguments("ref structs", "7.2").WithLocation(7, 23));
        }

        [Fact]
        public void NewStackAllocSpanSyntaxProducesErrorsOnEarlierVersions_Expressions()
        {
            var parseOptions = new CSharpParseOptions().WithLanguageVersion(LanguageVersion.CSharp7);

            CreateCompilationWithMscorlibAndSpan(@"
class Test
{
    void M(bool condition)
    {
        var x = condition
            ? stackalloc int[10]
            : stackalloc int[100];
    }
}", options: TestOptions.UnsafeReleaseDll, parseOptions: parseOptions).VerifyDiagnostics(
                // (7,15): error CS8107: Feature 'ref structs' is not available in C# 7.0. Please use language version 7.2 or greater.
                //             ? stackalloc int[10]
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "stackalloc int[10]").WithArguments("ref structs", "7.2").WithLocation(7, 15),
                // (8,15): error CS8107: Feature 'ref structs' is not available in C# 7.0. Please use language version 7.2 or greater.
                //             : stackalloc int[100];
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "stackalloc int[100]").WithArguments("ref structs", "7.2").WithLocation(8, 15));
        }

        [Fact]
        public void StackAllocSyntaxProducesUnsafeErrorInSafeCode()
        {
            CreateCompilation(@"
class Test
{
    void M()
    {
        var x = stackalloc int[10];
    }
}", options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (6,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context
                //         var x = stackalloc int[10];
                Diagnostic(ErrorCode.ERR_UnsafeNeeded, "stackalloc int[10]").WithLocation(6, 17));
        }

        [Fact]
        public void StackAllocInUsing1()
        {
            var test = @"
public class Test
{
    unsafe public static void Main()
    {
        using (var v = stackalloc int[1])
        {
        }
    }
}
";
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics(
                // (6,16): error CS1674: 'Span<int>': type used in a using statement must be implicitly convertible to 'System.IDisposable'
                //         using (var v = stackalloc int[1])
                Diagnostic(ErrorCode.ERR_NoConvToIDisp, "var v = stackalloc int[1]").WithArguments("System.Span<int>").WithLocation(6, 16)
            );
        }

        [Fact]
        public void StackAllocInUsing2()
        {
            var test = @"
public class Test
{
    unsafe public static void Main()
    {
        using (System.IDisposable v = stackalloc int[1])
        {
        }
    }
}
";
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics(
                // (6,39): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'IDisposable' is not possible.
                //         using (System.IDisposable v = stackalloc int[1])
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[1]").WithArguments("int", "System.IDisposable").WithLocation(6, 39));
        }

        [Fact]
        public void ConstStackAllocExpression()
        {
            var test = @"
unsafe public class Test
{
    void M()
    {
        const int* p = stackalloc int[1];
    }
}
";
            CreateCompilation(test, options: TestOptions.UnsafeDebugDll).VerifyDiagnostics(
                // (6,15): error CS0283: The type 'int*' cannot be declared const
                //         const int* p = stackalloc int[1];
                Diagnostic(ErrorCode.ERR_BadConstType, "int*").WithArguments("int*").WithLocation(6, 15));
        }

        [Fact]
        public void RefStackAllocAssignment_ValueToRef()
        {
            var test = @"
using System;
public class Test
{
    void M()
    {
        ref Span<int> p = stackalloc int[1];
    }
}
";
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics(
                // (7,23): error CS8172: Cannot initialize a by-reference variable with a value
                //         ref Span<int> p = stackalloc int[1];
                Diagnostic(ErrorCode.ERR_InitializeByReferenceVariableWithValue, "p = stackalloc int[1]").WithLocation(7, 23),
                // (7,27): error CS1510: A ref or out value must be an assignable variable
                //         ref Span<int> p = stackalloc int[1];
                Diagnostic(ErrorCode.ERR_RefLvalueExpected, "stackalloc int[1]").WithLocation(7, 27));
        }

        [Fact]
        public void RefStackAllocAssignment_RefToRef()
        {
            var test = @"
using System;
public class Test
{
    void M()
    {
        ref Span<int> p = ref stackalloc int[1];
    }
}
";
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true), parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (7,31): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         ref Span<int> p = ref stackalloc int[1];
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(7, 31)
                );
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true)).VerifyDiagnostics(
                // (7,31): error CS1510: A ref or out value must be an assignable variable
                //         ref Span<int> p = ref stackalloc int[1];
                Diagnostic(ErrorCode.ERR_RefLvalueExpected, "stackalloc int[1]").WithLocation(7, 31)
            );
        }

        [Fact]
        public void InvalidPositionForStackAllocSpan()
        {
            var test = @"
using System;
public class Test
{
    void M()
    {
        N(stackalloc int[1]);
    }
    void N(Span<int> span)
    {
    }
}
";
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true), parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (7,11): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         N(stackalloc int[1]);
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(7, 11)
                );
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true), parseOptions: TestOptions.Regular8).VerifyDiagnostics(
            );
        }

        [Fact]
        public void CannotDotIntoStackAllocExpression()
        {
            var test = @"
public class Test
{
    void M()
    {
        int length = (stackalloc int [10]).Length;
    }
}
";
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true), parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (6,23): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         int length = (stackalloc int [10]).Length;
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 23)
                );
            CreateCompilationWithMscorlibAndSpan(test, options: TestOptions.ReleaseDll.WithAllowUnsafe(true), parseOptions: TestOptions.Regular8).VerifyDiagnostics(
            );
        }

        [Fact]
        public void OverloadResolution_Fail()
        {
            var test = @"
using System;
unsafe public class Test
{
    static void Main()
    {
        Invoke(stackalloc int [10]);
    }

    static void Invoke(Span<short> shortSpan) => Console.WriteLine(""shortSpan"");
    static void Invoke(Span<bool> boolSpan) => Console.WriteLine(""boolSpan"");
    static void Invoke(int* intPointer) => Console.WriteLine(""intPointer"");
    static void Invoke(void* voidPointer) => Console.WriteLine(""voidPointer"");
}
";
            CreateCompilationWithMscorlibAndSpan(test, TestOptions.UnsafeReleaseExe, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (7,16): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         Invoke(stackalloc int [10]);
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(7, 16)
                );
            CreateCompilationWithMscorlibAndSpan(test, TestOptions.UnsafeReleaseExe).VerifyDiagnostics(
                // (7,16): error CS1503: Argument 1: cannot convert from 'System.Span<int>' to 'System.Span<short>'
                //         Invoke(stackalloc int [10]);
                Diagnostic(ErrorCode.ERR_BadArgType, "stackalloc int [10]").WithArguments("1", "System.Span<int>", "System.Span<short>").WithLocation(7, 16)
            );
        }

        [Fact]
        public void StackAllocWithDynamic()
        {
            CreateCompilation(@"
class Program
{
    static void Main()
    {
        var d = stackalloc dynamic[10];
    }
}").VerifyDiagnostics(
                // (6,33): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('dynamic')
                //         var d = stackalloc dynamic[10];
                Diagnostic(ErrorCode.ERR_ManagedAddr, "dynamic").WithArguments("dynamic").WithLocation(6, 28));
        }

        [Fact]
        public void StackAllocWithDynamicSpan()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Program
{
    static void Main()
    {
        Span<dynamic> d = stackalloc dynamic[10];
    }
}").VerifyDiagnostics(
                // (7,38): error CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type ('dynamic')
                //         Span<dynamic> d = stackalloc dynamic[10];
                Diagnostic(ErrorCode.ERR_ManagedAddr, "dynamic").WithArguments("dynamic").WithLocation(7, 38));
        }

        [Fact]
        public void StackAllocAsArgument()
        {
            var source = @"
class Program
{
    static void N(object p) { }

    static void Main()
    {
        N(stackalloc int[10]);
    }
}";
            CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (8,11): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         N(stackalloc int[10]);
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(8, 11)
                );
            CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.Regular8).VerifyDiagnostics(
                // (8,11): error CS1503: Argument 1: cannot convert from 'System.Span<int>' to 'object'
                //         N(stackalloc int[10]);
                Diagnostic(ErrorCode.ERR_BadArgType, "stackalloc int[10]").WithArguments("1", "System.Span<int>", "object").WithLocation(8, 11)
            );
        }

        [Fact]
        public void StackAllocInParenthesis()
        {
            var source = @"
class Program
{
    static void Main()
    {
        var x = (stackalloc int[10]);
    }
}";
            CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (6,18): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         var x = (stackalloc int[10]);
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 18)
                );
            CreateCompilationWithMscorlibAndSpan(source).VerifyDiagnostics(
                );
        }

        [Fact]
        public void StackAllocInNullConditionalOperator()
        {
            var source = @"
class Program
{
    static void Main()
    {
        var x = stackalloc int[1] ?? stackalloc int[2];
    }
}";
            CreateCompilationWithMscorlibAndSpan(source, parseOptions: TestOptions.Regular7_3).VerifyDiagnostics(
                // (6,17): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         var x = stackalloc int[1] ?? stackalloc int[2];
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 17),
                // (6,38): error CS8652: The feature 'stackalloc in nested expressions' is not available in C# 7.3. Please use language version 8.0 or greater.
                //         var x = stackalloc int[1] ?? stackalloc int[2];
                Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "stackalloc").WithArguments("stackalloc in nested expressions", "8.0").WithLocation(6, 38)
                );
            CreateCompilationWithMscorlibAndSpan(source).VerifyDiagnostics(
                // (6,17): error CS0019: Operator '??' cannot be applied to operands of type 'Span<int>' and 'Span<int>'
                //         var x = stackalloc int[1] ?? stackalloc int[2];
                Diagnostic(ErrorCode.ERR_BadBinaryOps, "stackalloc int[1] ?? stackalloc int[2]").WithArguments("??", "System.Span<int>", "System.Span<int>").WithLocation(6, 17)
                );
        }

        [Fact]
        public void StackAllocInCastAndConditionalOperator()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
    public void Method()
    {
        Test value = true ? new Test() : (Test)stackalloc int[10];
    }
    
    public static explicit operator Test(Span<int> value) 
    {
        return new Test();
    }
}", TestOptions.ReleaseDll).VerifyDiagnostics();
        }

        [Fact]
        [WorkItem(25038, "https://github.com/dotnet/roslyn/issues/25038")]
        public void StackAllocToSpanWithRefStructType()
        {
            CreateCompilationWithMscorlibAndSpan(@"
using System;
ref struct S {}
class Test
{
    void M()
    {
        Span<S> explicitError = default;
        var implicitError = explicitError.Length > 0 ? stackalloc S[10] : stackalloc S[100];
    }
}").VerifyDiagnostics(
                // (8,14): error CS0306: The type 'S' may not be used as a type argument
                //         Span<S> explicitError = default;
                Diagnostic(ErrorCode.ERR_BadTypeArgument, "S").WithArguments("S").WithLocation(8, 14),
                // (9,67): error CS0306: The type 'S' may not be used as a type argument
                //         var implicitError = explicitError.Length > 0 ? stackalloc S[10] : stackalloc S[100];
                Diagnostic(ErrorCode.ERR_BadTypeArgument, "S[10]").WithArguments("S").WithLocation(9, 67),
                // (9,86): error CS0306: The type 'S' may not be used as a type argument
                //         var implicitError = explicitError.Length > 0 ? stackalloc S[10] : stackalloc S[100];
                Diagnostic(ErrorCode.ERR_BadTypeArgument, "S[100]").WithArguments("S").WithLocation(9, 86)
                );
        }

        [Fact]
        [WorkItem(25086, "https://github.com/dotnet/roslyn/issues/25086")]
        public void StaackAllocToSpanWithCustomSpanAndConstraints()
        {
            var code = @"
using System;
namespace System
{
    public unsafe readonly ref struct Span<T> where T : IComparable
    {
        public Span(void* ptr, int length)
        {
            Length = length;
        }
        public int Length { get; }
    }
}
struct NonComparable { }
class Test
{
    void M()
    {
        Span<NonComparable> explicitError = default;
        var implicitError = explicitError.Length > 0 ? stackalloc NonComparable[10] : stackalloc NonComparable[100];
    }
}";

            var references = new List<MetadataReference>() { MscorlibRef_v4_0_30316_17626, SystemCoreRef, CSharpRef };

            CreateEmptyCompilation(code, references, TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (19,14): error CS0315: The type 'NonComparable' cannot be used as type parameter 'T' in the generic type or method 'Span<T>'. There is no boxing conversion from 'NonComparable' to 'System.IComparable'.
                //         Span<NonComparable> explicitError = default;
                Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "NonComparable").WithArguments("System.Span<T>", "System.IComparable", "T", "NonComparable").WithLocation(19, 14),
                // (20,67): error CS0315: The type 'NonComparable' cannot be used as type parameter 'T' in the generic type or method 'Span<T>'. There is no boxing conversion from 'NonComparable' to 'System.IComparable'.
                //         var implicitError = explicitError.Length > 0 ? stackalloc NonComparable[10] : stackalloc NonComparable[100];
                Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "NonComparable[10]").WithArguments("System.Span<T>", "System.IComparable", "T", "NonComparable").WithLocation(20, 67),
                // (20,98): error CS0315: The type 'NonComparable' cannot be used as type parameter 'T' in the generic type or method 'Span<T>'. There is no boxing conversion from 'NonComparable' to 'System.IComparable'.
                //         var implicitError = explicitError.Length > 0 ? stackalloc NonComparable[10] : stackalloc NonComparable[100];
                Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedValType, "NonComparable[100]").WithArguments("System.Span<T>", "System.IComparable", "T", "NonComparable").WithLocation(20, 98));
        }

        [Fact]
        [WorkItem(26195, "https://github.com/dotnet/roslyn/issues/26195")]
        public void StackAllocImplicitConversion_TwpStep_ToPointer()
        {
            var code = @"
class Test2
{
}
unsafe class Test
{
	public void Method()
	{
		Test obj1 = stackalloc int[2];
	}
	public static implicit operator Test2(int* value) => default;
}";

            CreateCompilationWithMscorlibAndSpan(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (11,34): error CS0556: User-defined conversion must convert to or from the enclosing type
                // 	public static implicit operator Test2(int* value) => default;
                Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "Test2").WithLocation(11, 34),
                // (9,15): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'Test' is not possible.
                // 		Test obj1 = stackalloc int[2];
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[2]").WithArguments("int", "Test").WithLocation(9, 15));
        }

        [Fact]
        [WorkItem(26195, "https://github.com/dotnet/roslyn/issues/26195")]
        public void StackAllocImplicitConversion_TwpStep_ToSpan()
        {
            var code = @"
class Test2
{
}
unsafe class Test
{
	public void Method()
	{
		Test obj1 = stackalloc int[2];
	}
	public static implicit operator Test2(System.Span<int> value) => default;
}";

            CreateCompilationWithMscorlibAndSpan(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(
                // (11,34): error CS0556: User-defined conversion must convert to or from the enclosing type
                // 	public static implicit operator Test2(System.Span<int> value) => default;
                Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "Test2").WithLocation(11, 34),
                // (9,15): error CS8346: Conversion of a stackalloc expression of type 'int' to type 'Test' is not possible.
                // 		Test obj1 = stackalloc int[2];
                Diagnostic(ErrorCode.ERR_StackAllocConversionNotPossible, "stackalloc int[2]").WithArguments("int", "Test").WithLocation(9, 15));
        }
    }
}
