File: Semantics\UserDefinedConversionTests.cs
Web Access
Project: src\src\Compilers\CSharp\Test\Semantic\Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Semantic.UnitTests)
// 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.Linq;
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 partial class UserDefinedConversionTests : CompilingTestBase
    {
        #region "Source"
        private readonly string _userDefinedConversionTestTemplate = @"
class C1 { }
class C2 { }
class D 
{
  public static XXX operator C1(D d) { return null; }
  public static XXX operator C2(D d) { return null; }
  public static XXX operator D(C1 c1) { return null; }
  public static XXX operator D(C2 c2) { return null; }
}
 
struct E1 { }
struct E2 { }
struct F
{
  public static XXX operator E1(F f) { return default(E1); }
  public static XXX operator E2(F f) { return default(E2); }
  public static XXX operator F(E1 e1) { return default(F); }
  public static XXX operator F(E2 e2) { return default(F); }
}
struct G {}
struct H
{
  public static XXX operator G(H h) { return default(G); }
}
struct I
{
  public static XXX operator G(I? i) { return default(G); }
}
struct J
{
  public static XXX operator G?(J j) { return default(G?); }
}
struct K
{
  public static XXX operator G?(K? k) { return default(G?); }
}
struct L
{
  public static XXX operator G(L l) { return default(G); }
  public static XXX operator G(L? l) { return default(G); }
}
 
struct M
{
  public static XXX operator G(M m) { return default(G); }
  public static XXX operator G?(M? m) { return default(G?); }
}
 
struct N
{
  public static XXX operator G(N? n) { return default(G); }
  public static XXX operator G?(N n) { return default(G?); }
}
 
struct O
{
  public static XXX operator G(O? o) { return default(G); }
  public static XXX operator G?(O? o) { return default(G?); }
}
 
struct P
{
  public static XXX operator G(P p) { return default(G); }
  public static XXX operator G(P? p) { return default(G); }
  public static XXX operator G?(P p) { return default(G?); }
}
 
struct Q
{
  public static XXX operator G(Q q) { return default(G); }
  public static XXX operator G(Q? q) { return default(G); }
  public static XXX operator G?(Q? q) { return default(G?); }
}
 
struct R
{
  public static XXX operator G(R r) { return default(G); }
  public static XXX operator G?(R r) { return default(G?); }
  public static XXX operator G?(R? r) { return default(G?); }
}
 
struct S
{
  public static XXX operator G(S? s) { return default(G); }
  public static XXX operator G?(S s) { return default(G?); }
  public static XXX operator G?(S? s) { return default(G?); }
}
 
struct T
{
  public static XXX operator G(T t) { return default(G); }
  public static XXX operator G(T? t) { return default(G); }
  public static XXX operator G?(T t) { return default(G?); }
  public static XXX operator G?(T? t) { return default(G?); }
}
";
        #endregion
 
        [Fact]
        public void TestUserDefinedImplicitConversionOverloadResolution()
        {
            string source1 = _userDefinedConversionTestTemplate.Replace("XXX", "implicit");
            string source2 = @"
class Z
{
  static void MC1(C1 c1) { }
  static void MC2(C2 c2) { }
  static void MD(D d) { }
  static void ME1(E1 e1) { }
  static void MNE1(E1? e1) { }
  static void ME2(E2 e2) { }
  static void MNE2(E2? e2) { }
  static void MF(F f) { }
  static void MNF(F? f) { }
  static void MG(G g) { }
  static void MNG(G? g) { }
 
  static void Main()
  {
    MC1(default(D));
    MC2(default(D));
    MD(default(C1));
    MD(default(C2));
 
    ME1(default(F));
    MNE1(default(F));
    MNE1(default(F?));
    ME2(default(F));
    MNE2(default(F));
    MNE2(default(F?));
    MF(default(E1));
    MF(default(E2));
    MNF(default(E1));
    MNF(default(E1?));
    MNF(default(E2));
    MNF(default(E2?));
 
    MG(default(H));
    // MG(default(H?)); Not implicit
    MNG(default(H));
    MNG(default(H?));
 
    MG(default(I));
    MG(default(I?));
    MNG(default(I));
    MNG(default(I?));
 
    //MG(default(J));  Not implicit
    //MG(default(J?)); Not implicit
    MNG(default(J));
    MNG(default(J?)); // Invalid according to specification. Native compiler and Roslyn allow it
                      // by improperly 'half lifting' the conversion.
 
    //MG(default(K));  Not implicit
    //MG(default(K?)); Not implicit
    MNG(default(K));
    MNG(default(K?));
 
    MG(default(L));
    MG(default(L?));
    MNG(default(L)); // Ambiguous according to specification; Roslyn and native compiler allow it
    MNG(default(L?));
 
    MG(default(M));
    //MG(default(M?)); Not implicit
    MNG(default(M));  // Ambiguous according to specification; Roslyn and native compiler allow it.
    MNG(default(M?));
 
    MG(default(N));
    MG(default(N?));
    MNG(default(N));
    // MNG(default(N?)); // Valid according to specification. Native compiler and Roslyn claim this is ambiguous
                         // even though the conversion from N-->G? is not applicable because it cannot be lifted.
                         // Native compiler and Roslyn choose improperly 'half lift' the operator to N?-->G?.
 
    MG(default(O));
    MG(default(O?));
    MNG(default(O));
    MNG(default(O?));
 
    MG(default(P));
    MG(default(P?));
    MNG(default(P));
    //MNG(default(P?)); // Similarly valid according to specification, but ambiguous according to native compiler and Roslyn.
 
    MG(default(Q));
    MG(default(Q?));
    MNG(default(Q)); // Ambiguous according to specification; Roslyn and native compiler allow it.
    MNG(default(Q?));
 
    MG(default(R));
    //MG(default(R?)); Not implicit
    MNG(default(R));
    MNG(default(R?));
 
    MG(default(S));
    MG(default(S?));
    MNG(default(S));
    MNG(default(S?));
 
    MG(default(T));
    MG(default(T?));
    MNG(default(T));
    MNG(default(T?));
  }
}
";
            string source3 = @"
class Z
{
  static void MC1(C1 c1) { }
  static void MC2(C2 c2) { }
  static void MD(D d) { }
  static void ME1(E1 e1) { }
  static void MNE1(E1? e1) { }
  static void ME2(E2 e2) { }
  static void MNE2(E2? e2) { }
  static void MF(F f) { }
  static void MNF(F? f) { }
  static void MG(G g) { }
  static void MNG(G? g) { }
 
  static void Main()
  {
    // None of these are implicit conversions.
    MG(default(H?));
    MG(default(J));
    MG(default(J?));
    MG(default(K));  
    MG(default(K?)); 
    MG(default(M?)); 
    MG(default(R?)); 
  }
}
";
 
            var comp = CreateCompilation(source1 + source2);
            comp.VerifyDiagnostics();
 
            comp = CreateCompilation(source1 + source3);
            comp.VerifyDiagnostics(
// (115,8): error CS1503: Argument 1: cannot convert from 'H?' to 'G'
//     MG(default(H?));
Diagnostic(ErrorCode.ERR_BadArgType, "default(H?)").WithArguments("1", "H?", "G"),
 
// (116,8): error CS1503: Argument 1: cannot convert from 'J' to 'G'
//     MG(default(J));
Diagnostic(ErrorCode.ERR_BadArgType, "default(J)").WithArguments("1", "J", "G"),
 
// (117,8): error CS1503: Argument 1: cannot convert from 'J?' to 'G'
//     MG(default(J?));
Diagnostic(ErrorCode.ERR_BadArgType, "default(J?)").WithArguments("1", "J?", "G"),
 
// (119,8): error CS1503: Argument 1: cannot convert from 'K' to 'G'
//     MG(default(K));  
Diagnostic(ErrorCode.ERR_BadArgType, "default(K)").WithArguments("1", "K", "G"),
 
// (120,8): error CS1503: Argument 1: cannot convert from 'K?' to 'G'
//     MG(default(K?)); 
Diagnostic(ErrorCode.ERR_BadArgType, "default(K?)").WithArguments("1", "K?", "G"),
 
// (121,8): error CS1503: Argument 1: cannot convert from 'M?' to 'G'
//     MG(default(M?)); 
Diagnostic(ErrorCode.ERR_BadArgType, "default(M?)").WithArguments("1", "M?", "G"),
 
// (122,8): error CS1503: Argument 1: cannot convert from 'R?' to 'G'
//     MG(default(R?)); 
Diagnostic(ErrorCode.ERR_BadArgType, "default(R?)").WithArguments("1", "R?", "G"));
        }
 
        [Fact, WorkItem(543716, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543716")]
        public void TestUserDefinedConversionOverloadResolution_SpecViolations()
        {
            // These are all cases where the specification says the conversion should either not exist
            // or be ambiguous, but the native compiler allows the conversion. Roslyn emulates the
            // native compiler's behavior to avoid the breaking change.
 
            string implicitConversions = _userDefinedConversionTestTemplate.Replace("XXX", "implicit");
            string implicitConversionBadSuccess = @"
class Z
{
    static void MNG(G? g) { }
    static void MG(G g) { }
    static void Main()
    {
        // Implicit conversions
        MNG(default(J?)); 
        MNG(default(L));
        MNG(default(M));
        MNG(default(Q));
 
        // Explicit conversions
        MNG((G?)default(L));
        MG((G)default(M?));
        MNG((G?)default(M));
        MNG((G?)default(Q));
        MG((G)default(R?));
    }
}";
            var comp = CreateCompilation(implicitConversions + implicitConversionBadSuccess);
            comp.VerifyDiagnostics();
 
            // More cases where the specification says that the conversion should be bad, but
            // the native compiler and Roslyn allow it.
 
            string explicitConversions = _userDefinedConversionTestTemplate.Replace("XXX", "explicit");
            string explicitConversionsBadSuccess = @"
class Z
{
  static void MG(G g) { }
  static void MNG(G? g) { }
  static void Main()
  {
    MNG((G?)default(L));
    MG((G)default(M?));
    MNG((G?)default(M));
    MNG((G?)default(Q));
    MG((G)default(R?));
  }
}";
            comp = CreateCompilation(explicitConversions + explicitConversionsBadSuccess);
            comp.VerifyDiagnostics();
 
            // These are cases where the specification indicates that a conversion should be legal,
            // but the native compiler disallows it. Roslyn follows the native compiler in these cases.
 
            string implicitConversionsBadFailures = @"
class Z
{
  static void MNG(G? g) { }
  static void Main()
  {
    MNG(default(N?)); 
    MNG(default(P?)); 
  }  
};";
 
            comp = CreateCompilation(implicitConversions + implicitConversionsBadFailures);
            comp.VerifyDiagnostics(
// (103,9): error CS0457: Ambiguous user defined conversions 'N.implicit operator G(N?)' and 'N.implicit operator G?(N)' when converting from 'N?' to 'G?'
//     MNG(default(N?)); 
Diagnostic(ErrorCode.ERR_AmbigUDConv, "default(N?)").WithArguments("N.implicit operator G(N?)", "N.implicit operator G?(N)", "N?", "G?"),
// (104,9): error CS0457: Ambiguous user defined conversions 'P.implicit operator G(P)' and 'P.implicit operator G(P?)' when converting from 'P?' to 'G?'
//     MNG(default(P?)); 
Diagnostic(ErrorCode.ERR_AmbigUDConv, "default(P?)").WithArguments("P.implicit operator G(P)", "P.implicit operator G(P?)", "P?", "G?")
                );
 
            // More cases where the specification indicates that a conversion should be legal,
            // but the native compiler disallows it. Roslyn follows the native compiler in these cases.
 
            string explicitConversionsBadFailures = @"
class Z
{
  static void MNG(G? g) { }
  static void Main()
  {
    MNG((G?)default(N?)); 
    MNG((G?)default(P?)); 
  }  
};";
 
            comp = CreateCompilation(explicitConversions + explicitConversionsBadFailures);
            comp.VerifyDiagnostics(
// (103,9): error CS0457: Ambiguous user defined conversions 'N.explicit operator G(N?)' and 'N.explicit operator G?(N)' when converting from 'N?' to 'G?'
//     MNG((G?)default(N?)); 
Diagnostic(ErrorCode.ERR_AmbigUDConv, "(G?)default(N?)").WithArguments("N.explicit operator G(N?)", "N.explicit operator G?(N)", "N?", "G?"),
// (104,9): error CS0457: Ambiguous user defined conversions 'P.explicit operator G(P)' and 'P.explicit operator G(P?)' when converting from 'P?' to 'G?'
//     MNG((G?)default(P?)); 
Diagnostic(ErrorCode.ERR_AmbigUDConv, "(G?)default(P?)").WithArguments("P.explicit operator G(P)", "P.explicit operator G(P?)", "P?", "G?")
                );
        }
 
        [Fact]
        public void TestUserDefinedConversionOverloadResolution_BreakingChanges()
        {
            // Roslyn emulates most of the native compiler's spec violations for user-defined conversions.
 
            // It is possible for a user-defined implicit conversion to be ambiguous when processed
            // as an explicit conversion but unambiguous when processed as an implicit conversion.
            // In that circumstance, the native compiler reports the ambiguity, oddly enough. 
            // One might expect that if "B b = 1;" succeeds then "B b = (B)1;" ought to as well.
            // Roslyn matches the native compiler.
 
            // This is bug 11202.
 
            string source1 = @" 
class A
{
    public static implicit operator A(int d) { return null; }
}
class B : A
{
    public static implicit operator B(long d) { return null; }
}
class C
{
    static void Main()
    {
        B b = 1; 
        // Only the operator in B is applicable because we must
        // convert implicitly from the return type to B.
 
        b = (B)1; 
        // Both the operators are applicable because we must
        // convert from the return type to B via an explicit conversion,
        // and A to B is therefore legal. Therefore, were we to treat
        // this solely as an explicit conversion, we'd have an ambiguity
        // because the return type exactly matches on the operator in B,
        // and the parameter type exactly matches on the operator in A.
        // The native compiler produces an error here. Roslyn reasons
        // that if the implicit conversion succeeds then the explicit conversion
        // ought to also succeed.
    }
}";
 
            var comp = CreateCompilation(source1);
            comp.VerifyDiagnostics(
                // (18,13): error CS0457: Ambiguous user defined conversions 'B.implicit operator B(long)' and 'A.implicit operator A(int)' when converting from 'int' to 'B'
                //         b = (B)1; 
                Diagnostic(ErrorCode.ERR_AmbigUDConv, "(B)1").WithArguments("B.implicit operator B(long)", "A.implicit operator A(int)", "int", "B"));
 
            // The native compiler incorrectly treats the explicit user-defined
            // conversion operator as inapplicable here. Roslyn allows this.
            // Contrast this with the genuinely incorrect code in the method
            // TestUserDefinedConversionsTypeParameterEdgeCase below.
 
            string source2 = @"
class Animal {}
class Mammal : Animal {}
class X<T> where T : Mammal
{
  public static explicit operator X<T>(Animal a) { return null; }
  public static void M(T t)
  { 
    X<T> xt = (X<T>)t;
  }
}
";
 
            comp = CreateCompilation(source2);
            comp.VerifyDiagnostics();
        }
 
        [Fact]
        public void TestUserDefinedConversionsTypeParameterEdgeCase()
        {
            // In contrast with the code above, this code should genuinely produce
            // an error. The cast operator means that a standard explicit conversion can
            // be inserted on both sides of the implicit conversion. Though there is an
            // explicit conversion from T to Giraffe, this is not a *standard* implicit
            // conversion because there is no implicit conversion from Giraffe to T.
 
            string source = @"
class Animal {}
class Mammal : Animal {}
class Giraffe : Mammal {}
class X<T> where T : Mammal
{
  public static implicit operator X<T>(Giraffe g) { return null; }
  public static void M(T t)
  { 
    X<T> xt = (X<T>)t;
  }
}
";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (10,15): error CS0030: Cannot convert type 'T' to 'X<T>'
                //     X<T> xt = (X<T>)t;
                Diagnostic(ErrorCode.ERR_NoExplicitConv, "(X<T>)t").WithArguments("T", "X<T>"));
        }
 
        [Fact, WorkItem(605100, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/605100")]
        public void TestUserDefinedConversions_DynamicIdentityBetweenBaseTypes()
        {
            string source = @"
public class A<U>
{
   public static explicit operator T(A<U> a) { return null; }
}
 
public class S : A<dynamic>
{
    
}
 
public class T : A<object>
{
 
}
 
public class X
{
    static T F(S s)
    {
        return (T)s;
    }
}
";
            // Dev11 doesn't use identity conversion and reports an error, which is wrong:
            // error CS0457: Ambiguous user defined conversions 'A<dynamic>.explicit operator T(A<dynamic>)' and 'A<object>.explicit operator T(A<object>)' when converting from 'S' to 'T'
            CreateCompilationWithMscorlib40AndSystemCore(source).VerifyDiagnostics();
        }
 
        [Fact, WorkItem(605326, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/605326")]
        public void TestUserDefinedConversions_DynamicIdentityBetweenBaseTypeAndTargetType()
        {
            string source = @"
public class A<T>
{
}
 
public class B : A<dynamic>
{
    public static explicit operator A<object>(B x)
    {
        return null;
    }
}
";
            // TODO (tomat): This should report ERR_ConversionWithBase 
            CreateCompilationWithMscorlib40AndSystemCore(source).VerifyDiagnostics();
        }
 
        [Fact]
        public void TestUserDefinedExplicitConversionOverloadResolution()
        {
            // Explicit conversions should use implicit conversions.
            string source1 = _userDefinedConversionTestTemplate.Replace("XXX", "implicit");
 
            string source2 = _userDefinedConversionTestTemplate.Replace("XXX", "explicit");
 
            string source3 = @"
class Z
{
  static void MC1(C1 c1) { }
  static void MC2(C2 c2) { }
  static void MD(D d) { }
  static void ME1(E1 e1) { }
  static void MNE1(E1? e1) { }
  static void ME2(E2 e2) { }
  static void MNE2(E2? e2) { }
  static void MF(F f) { }
  static void MNF(F? f) { }
  static void MG(G g) { }
  static void MNG(G? g) { }
 
  static void Main()
  {
    MC1((C1)default(D));
    MC2((C2)default(D));
    MD((D)default(C1));
    MD((D)default(C2));
 
    ME1((E1)default(F));
    MNE1((E1?)default(F));
    MNE1((E1?)default(F?));
    ME2((E2)default(F));
    MNE2((E2?)default(F));
    MNE2((E2?)default(F?));
    MF((F)default(E1));
    MF((F)default(E2));
    MNF((F?)default(E1));
    MNF((F?)default(E1?));
    MNF((F?)default(E2));
    MNF((F?)default(E2?));
 
    MG((G)default(H));
    MG((G)default(H?));
    MNG((G?)default(H));
    MNG((G?)default(H?));
 
    MG((G)default(I));
    MG((G)default(I?));
    MNG((G?)default(I));
    MNG((G?)default(I?));
 
    MG((G)default(J));
    MG((G)default(J?));
    MNG((G?)default(J));
    MNG((G?)default(J?));
 
    MG((G)default(K)); 
    MG((G)default(K?));
    MNG((G?)default(K));
    MNG((G?)default(K?));
 
    MG((G)default(L));
    MG((G)default(L?));
    MNG((G?)default(L)); // Spec says this should be ambiguous; native compiler and Roslyn allow it.
    MNG((G?)default(L?));
 
    MG((G)default(M));
    MG((G)default(M?)); // Spec says this should be ambiguous; native compiler and Roslyn allow it.
    MNG((G?)default(M)); // Spec says this should be ambiguous; native compiler and Roslyn allow it.
    MNG((G?)default(M?));
 
    // MG((G)default(N));  This is an interesting one. The conversion is ambiguous when declared as explicit, unambiguous when implicit. See below.
    MG((G)default(N?));
    MNG((G?)default(N));
    // MNG((G?)default(N?)); // Spec says this should be unambiguous; native compiler and Roslyn make it ambiguous.
 
    MG((G)default(O));
    MG((G)default(O?));
    MNG((G?)default(O));
    MNG((G?)default(O?));
 
    MG((G)default(P));
    MG((G)default(P?));
    MNG((G?)default(P));
    // MNG((G?)default(P?)); // Spec says this should be unambiguous; native compiler and Roslyn make it ambiguous.
 
    MG((G)default(Q));
    MG((G)default(Q?));
    MNG((G?)default(Q)); // Spec says this should be ambiguous; native compiler and Roslyn allow it.
    MNG((G?)default(Q?));
 
    MG((G)default(R));
    MG((G)default(R?));  // Spec says this should be ambiguous; native compiler and Roslyn allow it.
    MNG((G?)default(R));
    MNG((G?)default(R?));
 
    // MG((G)default(S)); Another one that is ambiguous when declared as explicit, unambiguous when implicit. See below.
    MG((G)default(S?));
    MNG((G?)default(S));
    MNG((G?)default(S?));
 
    MG((G)default(T));
    MG((G)default(T?));
    MNG((G?)default(T));
    MNG((G?)default(T?));
  }
}
";
 
            string source4 = @"
class Z
{
  static void MG(G g) { }
  static void MNG(G? g) { }
 
  static void Main()
  {
    MG((G)default(N));
    MG((G)default(S));
  }
}
";
 
            var comp = CreateCompilation(source1 + source3);
            comp.VerifyDiagnostics();
 
            comp = CreateCompilation(source2 + source3);
            comp.VerifyDiagnostics();
 
            // The native compiler produces errors for all of these because
            // it does not consider whether or not an *implicit* conversion
            // would have succeeded; it only considers whether an *explicit*
            // conversion is unambiguous.
 
            // Roslyn originally had a breaking change here, but now matches dev11.
 
            comp = CreateCompilation(source1 + source4);
            comp.VerifyDiagnostics(
                // (105,8): error CS0457: Ambiguous user defined conversions 'N.implicit operator G(N?)' and 'N.implicit operator G?(N)' when converting from 'N' to 'G'
                //     MG((G)default(N));
                Diagnostic(ErrorCode.ERR_AmbigUDConv, "(G)default(N)").WithArguments("N.implicit operator G(N?)", "N.implicit operator G?(N)", "N", "G"),
                // (106,8): error CS0457: Ambiguous user defined conversions 'S.implicit operator G(S?)' and 'S.implicit operator G?(S)' when converting from 'S' to 'G'
                //     MG((G)default(S));
                Diagnostic(ErrorCode.ERR_AmbigUDConv, "(G)default(S)").WithArguments("S.implicit operator G(S?)", "S.implicit operator G?(S)", "S", "G"));
 
            // When restricted to only use explicit conversions, the conversions
            // truly are ambiguous; the native and Roslyn compilers agree on these cases:
 
            comp = CreateCompilation(source2 + source4);
            comp.VerifyDiagnostics(
                // (105,8): error CS0457: Ambiguous user defined conversions 'N.explicit operator G(N?)' and 'N.explicit operator G?(N)' when converting from 'N' to 'G'
                Diagnostic(ErrorCode.ERR_AmbigUDConv, "(G)default(N)").WithArguments("N.explicit operator G(N?)", "N.explicit operator G?(N)", "N", "G"),
                // (106,9): error CS0457: Ambiguous user defined conversions 'N.explicit operator G(N?)' and 'N.explicit operator G?(N)' when converting from 'N?' to 'G?'
                Diagnostic(ErrorCode.ERR_AmbigUDConv, "(G)default(S)").WithArguments("S.explicit operator G(S?)", "S.explicit operator G?(S)", "S", "G"));
        }
 
        [Fact]
        public void TestUserDefinedConversions()
        {
            string source = @"
using System;
 
class C
{
  public static void Main()
  {
    S sx = new S('x');
    D dy = new D('y');
    E ez = new F('z');
    
    T tx = sx;
    F fg = (F)dy;
    B dz = (D)ez;
    
    Console.Write(tx);
    Console.Write(fg);
    Console.Write(dz);
  }
}
 
struct S
{
  public string str;
  public S(string str) { this.str = str; }
  public S(char chr) { this.str = chr.ToString(); }
  public override string ToString() { return 'S' + this.str; }
  public static implicit operator T(S s) { return new T(s.str); }
}
 
struct T
{
  public string str;
  public T(string str) { this.str = str; }
  public T(char chr) { this.str = chr.ToString(); }
  public override string ToString() { return 'T' + this.str; }
}
 
class B
{
  public string str;
  public B(string str) { this.str = str; }
  public B(char chr) { this.str = chr.ToString(); }
  public override string ToString() { return 'B' + this.str; }
  public static explicit operator E(B b) { return new F(b.str); }
}
 
class D : B 
{
  public D(char chr) : base(chr) {}
  public D(string str) : base(str) {}
  public static explicit operator D(F f) { return new D(f.str); }
  public override string ToString() { return 'D' + this.str; }
}
 
class E
{
  public string str;
  public E(string str) { this.str = str; }
  public E(char chr) { this.str = chr.ToString(); }
  public override string ToString() { return 'E' + this.str; }
}
 
class F : E
{
  public F(char chr) : base(chr) {}
  public F(string str) : base(str) {}
  public override string ToString() { return 'F' + this.str; }
}
";
 
            string output = "TxFyDz";
 
            CompileAndVerify(source: source, expectedOutput: output);
        }
 
        [Fact(Skip = "https://github.com/dotnet/roslyn/issues/39959")]
        [WorkItem(39959, "https://github.com/dotnet/roslyn/issues/39959")]
        public void TestIntPtrUserDefinedConversions()
        {
            // IntPtr and UIntPtr violate the rules of user-defined conversions for 
            // backwards-compatibility reasons. All of these should compile without error.
 
            string source1 = @"
using System;
unsafe class P
{
    public static void Main()
    {
        byte uint8 = 0;
        sbyte int8 = 0;
        short int16 = 0;
        ushort uint16 = 0;
        int int32 = 0;
        long int64 = 0;
        uint uint32 = 0;
        ulong uint64 = 0;
        double real64 = 0;
        float real32 = 0;
        char chr = '\0';
        DayOfWeek e = default(DayOfWeek);
        IntPtr intPtr = default(IntPtr);
        UIntPtr uintPtr = default(UIntPtr);
 
        byte? nuint8 = 0;
        sbyte? nint8 = 0;
        short? nint16 = 0;
        ushort? nuint16 = 0;
        int? nint32 = 0;
        long? nint64 = 0;
        uint? nuint32 = 0;
        ulong? nuint64 = 0;
        double? nreal64 = 0;
        float? nreal32 = 0;
        char? nchr = '\0';
        DayOfWeek? ne = default(DayOfWeek);
        IntPtr? nintPtr = default(IntPtr);
        UIntPtr? nuintPtr = default(UIntPtr);
 
";
 
            string source2 = @"
 
        decimal dec = 0;
        decimal? ndec = 0;
        void* pvoid = default(void*);
        int* pint = default(int*);
";
            string source3 = @"
 
// The spec requires that there be either an implicit conversion
// from the argument type to the parameter type, or from the 
// parameter type to the argument type. It does *not* require
// that there be an *explicit* conversion from the argument type
// to the parameter type.
//
// This means that if the argument type is short and the parameter
// type is uint, the conversion is not a candidate. There is an
// implicit conversion from short to uint, but there is no implicit
// conversion from short to uint or from uint to short.
//
// The spec also requires that the operator chosen be unambiguous.
// A conversion from byte --> IntPtr?, for example, is ambiguous because
// it could be:
// byte --> int --> IntPtr --> IntPtr?, or 
// byte --> int? --> IntPtr? (using the lifted conversion), or
// byte --> long --> IntPtr --> IntPtr? or 
// byte --> long? --> IntPtr? (using the lifted conversion)
//
// And that set is ambiguous; there is no best operator.
//
// The native compiler, and hence Roslyn, implements none of these rules.
// It allows conversions from IntPtr to any numeric type and vice versa.
// All the cases below should compile without error.
//
 
 
// numeric to IntPtr
 
        intPtr = (IntPtr) uint8;  // i32 imp
        intPtr = (IntPtr) int8;   // i32 imp
        intPtr = (IntPtr) uint16; // i32 imp 
        intPtr = (IntPtr) int16;  // i32 imp
        intPtr = (IntPtr) chr;    // i32 exp
        intPtr = (IntPtr) uint32; // i64 imp
        intPtr = (IntPtr) int32;  // i32 id
        intPtr = (IntPtr) uint64; // i64 exp *
        intPtr = (IntPtr) int64;  // i64 id
        intPtr = (IntPtr) real32; // i64 exp
        intPtr = (IntPtr) real64; // i64 exp
        intPtr = (IntPtr) e;
 
// numeric to UIntPtr
 
        uintPtr = (UIntPtr) uint8;  // u32 imp
        uintPtr = (UIntPtr) int8;   // u64 exp (ulong is bigger type than uint) *
        uintPtr = (UIntPtr) uint16; // u32 imp
        uintPtr = (UIntPtr) int16;  // u64 exp (ulong bigger) *
        uintPtr = (UIntPtr) chr;    // u32 exp
        uintPtr = (UIntPtr) uint32; // u32 id
        uintPtr = (UIntPtr) int32;  // u64 exp (ulong bigger) *
        uintPtr = (UIntPtr) uint64; // u64 id
        uintPtr = (UIntPtr) int64;  // u64 exp
        uintPtr = (UIntPtr) real32; // u64 exp
        uintPtr = (UIntPtr) real64; // u64 exp
        uintPtr = (UIntPtr) e;
 
// IntPtr to numeric
 
        uint8 = (byte) intPtr; // i32
        uint16 = (ushort) intPtr;  // i32
        chr = (char) intPtr; // i32
        uint32 = (uint) intPtr; // i32
        uint64 = (ulong) intPtr; // i64
        int8 = (sbyte) intPtr; // i32
        int16 = (short) intPtr; // i32
        int32 = (int) intPtr; // i32
        int64 = (long) intPtr; // i64
        real32 = (float) intPtr;  // i64
        real64 = (double) intPtr;  // i64
        e = (DayOfWeek) intPtr;
 
// UIntPtr to numeric
 
        uint8 = (byte) uintPtr; // u32
        uint16 = (ushort) uintPtr; // u32
        chr = (char) uintPtr; // u32
        uint32 = (uint) uintPtr; // u32
        uint64 = (ulong) uintPtr; // u64
        int8 = (sbyte) uintPtr; // u32
        int16 = (short) uintPtr; // u32
        int32 = (int) uintPtr; // u32
        int64 = (long) uintPtr; // u64
        real32 = (float) uintPtr; // u64
        real64 = (double) uintPtr; // u64
        e = (DayOfWeek) uintPtr;
 
// numeric to IntPtr?
        
        nintPtr = (IntPtr?) uint8;  // i32 imp 
        nintPtr = (IntPtr?) int8;   // i32 imp
        nintPtr = (IntPtr?) uint16; // i32 imp 
        nintPtr = (IntPtr?) int16;  // i32 imp
        nintPtr = (IntPtr?) chr;    // i32 exp
        nintPtr = (IntPtr?) uint32; // i64 imp
        nintPtr = (IntPtr?) int32;  // i32 id
        nintPtr = (IntPtr?) uint64; // i64 exp
        nintPtr = (IntPtr?) int64;  // i64 id
        nintPtr = (IntPtr?) real32; // i64 exp
        nintPtr = (IntPtr?) real64; // i64 exp
        nintPtr = (IntPtr?) e;
 
// numeric to UIntPtr?
        
        nuintPtr = (UIntPtr?) uint8;  // u32 imp
        nuintPtr = (UIntPtr?) int8;   // u64 exp (ulong is bigger type than uint)
        nuintPtr = (UIntPtr?) uint16; // u32 imp
        nuintPtr = (UIntPtr?) int16;  // u64 exp (ulong bigger)
        nuintPtr = (UIntPtr?) chr;    // u32 exp
        nuintPtr = (UIntPtr?) uint32; // u32 id
        nuintPtr = (UIntPtr?) int32;  // u64 exp (ulong bigger)
        nuintPtr = (UIntPtr?) uint64; // u64 id
        nuintPtr = (UIntPtr?) int64;  // u64 exp
        nuintPtr = (UIntPtr?) real32; // u64 exp
        nuintPtr = (UIntPtr?) real64; // u64 exp
        nuintPtr = (UIntPtr?) e;
 
// From nullable numeric to IntPtr
 
        intPtr = (IntPtr) nuint8;  // i32 imp
        intPtr = (IntPtr) nint8;   // i32 imp
        intPtr = (IntPtr) nuint16; // i32 imp 
        intPtr = (IntPtr) nint16;  // i32 imp
        intPtr = (IntPtr) nchr;    // i32 exp
        intPtr = (IntPtr) nuint32; // i64 imp
        intPtr = (IntPtr) nint32;  // i32 id
        intPtr = (IntPtr) nuint64; // i64 exp
        intPtr = (IntPtr) nint64;  // i64 id
        intPtr = (IntPtr) nreal32; // i64 exp
        intPtr = (IntPtr) nreal64; // i64 exp
        intPtr = (IntPtr) ne;
 
// From nullable numeric to UIntPtr
 
        uintPtr = (UIntPtr) nuint8;  // u32 imp
        uintPtr = (UIntPtr) nint8;   // u64 exp (ulong is bigger type than uint)
        uintPtr = (UIntPtr) nuint16; // u32 imp
        uintPtr = (UIntPtr) nint16;  // u64 exp (ulong bigger)
        uintPtr = (UIntPtr) nchr;    // u32 exp
        uintPtr = (UIntPtr) nuint32; // u32 id
        uintPtr = (UIntPtr) nint32;  // u64 exp (ulong bigger)
        uintPtr = (UIntPtr) nuint64; // u64 id
        uintPtr = (UIntPtr) nint64;  // u64 exp
        uintPtr = (UIntPtr) nreal32; // u64 exp
        uintPtr = (UIntPtr) nreal64; // u64 exp
        uintPtr = (UIntPtr) ne;
 
// From IntPtr? to numeric
 
        uint8 = (byte) nintPtr; // i32 
        uint16 = (ushort) nintPtr; // i32
        chr = (char) nintPtr; // i32
        uint32 = (uint) nintPtr; // i32
        uint64 = (ulong) nintPtr;  // i64
        int8 = (sbyte) nintPtr; // i32
        int16 = (short) nintPtr; // i32
        int32 = (int) nintPtr; // i32
        int64 = (long) nintPtr; // i64
        real32 = (float) nintPtr; // i64
        real64 = (double) nintPtr; // i64
        e = (DayOfWeek) nintPtr;
 
// From UIntPtr? to numeric
 
        uint8 = (byte) nuintPtr; // u32
        uint16 = (ushort) nuintPtr; // u32
        chr = (char) nuintPtr; // u32
        uint32 = (uint) nuintPtr; // u32
        uint64 = (ulong) nuintPtr; // u64
        int8 = (sbyte) nuintPtr; // u32
        int16 = (short) nuintPtr; // u32
        int32 = (int) nuintPtr; // u32
        int64 = (long) nuintPtr; // u64
        real32 = (float) nuintPtr; // u64
        real64 = (double) nuintPtr; // u64
        e = (DayOfWeek) nuintPtr; 
 
// From IntPtr to nullable numeric
 
        nuint8 = (byte?) intPtr;
        nuint16 = (ushort?) intPtr;
        nchr = (char?) intPtr;
        nuint32 = (uint?) intPtr;
        nuint64 = (ulong?) intPtr;
        nint8 = (sbyte?) intPtr;
        nint16 = (short?) intPtr;
        nint32 = (int?) intPtr;
        nint64 = (long?) intPtr;
        nreal32 = (float?) intPtr;
        nreal64 = (double?) intPtr;
        ne = (DayOfWeek?) intPtr;
        
// From UIntPtr to nullable numeric
 
        nuint8 = (byte?) uintPtr;
        nuint16 = (ushort?) uintPtr;
        nchr = (char?) uintPtr;
        nuint32 = (uint?) uintPtr;
        nuint64 = (ulong?) uintPtr;
        nint8 = (sbyte?) uintPtr;
        nint16 = (short?) uintPtr;
        nint32 = (int?) uintPtr;
        nint64 = (long?) uintPtr;
        nreal32 = (float?) uintPtr;
        nreal64 = (double?) uintPtr;
        ne = (DayOfWeek?) uintPtr;
 
";
 
            string source4 = @"
 
// Decimal conversion lowering is not yet implemented:
 
        uintPtr = (UIntPtr) dec;    // u64 exp
        intPtr = (IntPtr) dec;    // i64 exp
        dec = (decimal) intPtr;  // i64
        dec = (decimal) uintPtr; // u64
        nintPtr = (IntPtr?) dec;    // i64 exp
        nuintPtr = (UIntPtr?) dec;    // u64 exp
        intPtr = (IntPtr) ndec;    // i64 exp
        uintPtr = (UIntPtr) ndec;    // u64 exp
        dec = (decimal) nintPtr; // i64
        dec = (decimal) nuintPtr; // u64
        ndec = (decimal?) intPtr;
        ndec = (decimal?) uintPtr;
        ndec = (decimal?) nintPtr;
        ndec = (decimal?) nuintPtr;
 
// Lifted numeric To IntPtr
       
        nintPtr = (IntPtr?) nuint8;  // i32 imp
        nintPtr = (IntPtr?) nint8;   // i32 imp
        nintPtr = (IntPtr?) nuint16; // i32 imp 
        nintPtr = (IntPtr?) nint16;  // i32 imp
        nintPtr = (IntPtr?) nchr;    // i32 exp
        nintPtr = (IntPtr?) nuint32; // i64 imp
        nintPtr = (IntPtr?) nint32;  // i32 id
        nintPtr = (IntPtr?) nuint64; // i64 exp
        nintPtr = (IntPtr?) nint64;  // i64 id
        nintPtr = (IntPtr?) ndec;    // i64 exp
        nintPtr = (IntPtr?) nreal32; // i64 exp
        nintPtr = (IntPtr?) nreal64; // i64 exp
        nintPtr = (IntPtr?) ne;
 
// Lifted numeric to UIntPtr
 
        nuintPtr = (UIntPtr?) nuint8;  // u32 imp
        nuintPtr = (UIntPtr?) nint8;   // u64 exp (ulong is bigger type than uint)
        nuintPtr = (UIntPtr?) nuint16; // u32 imp
        nuintPtr = (UIntPtr?) nint16;  // u64 exp (ulong bigger)
        nuintPtr = (UIntPtr?) nchr;    // u32 exp
        nuintPtr = (UIntPtr?) nuint32; // u32 imp
        nuintPtr = (UIntPtr?) nint32;  // u64 exp (ulong bigger)
        nuintPtr = (UIntPtr?) nuint64; // u64 id
        nuintPtr = (UIntPtr?) nint64;  // u64 exp
        nuintPtr = (UIntPtr?) ndec;    // u64 exp
        nuintPtr = (UIntPtr?) nreal32; // u64 exp
        nuintPtr = (UIntPtr?) nreal64; // u64 exp
        nuintPtr = (UIntPtr?) ne;
 
// Lifted IntPtr to numeric:
 
        nuint8 = (byte?) nintPtr;
        nuint16 = (ushort?) nintPtr;
        nchr = (char?) nintPtr;
        nuint32 = (uint?) nintPtr;
        nuint64 = (ulong?) nintPtr;
        nuint32 = (uint?) nuintPtr;
        nint8 = (sbyte?) nintPtr;
        nint16 = (short?) nintPtr;
        nint32 = (int?) nintPtr;
        nint64 = (long?) nintPtr;
        nreal32 = (float?) nintPtr;
        nreal64 = (double?) nintPtr;
        ne = (DayOfWeek?) nintPtr;
 
// Lifted UIntPtr to numeric:
 
        nuint8 = (byte?) nuintPtr;
        nuint16 = (ushort?) nuintPtr;
        nchr = (char?) nuintPtr;
        nuint64 = (ulong?) nuintPtr;
        nint8 = (sbyte?) nuintPtr;
        nint16 = (short?) nuintPtr;
        nint32 = (int?) nuintPtr;
        nint64 = (long?) nuintPtr;
        nreal32 = (float?) nuintPtr;
        nreal64 = (double?) nuintPtr;
        ne = (DayOfWeek?) nuintPtr;
 
// From pointer
 
        intPtr = (IntPtr) pvoid;  // pv id
        uintPtr = (UIntPtr) pvoid;  // pv id
        nintPtr = (IntPtr?) pvoid;  // pv id
        nuintPtr = (UIntPtr?) pvoid;  // pv id
        intPtr = (IntPtr) pint;   // pv ptr
        uintPtr = (UIntPtr) pint;   // pv ptr
        nintPtr = (IntPtr?) pint;   // pv ptr
        nuintPtr = (UIntPtr?) pint;   // pv ptr
 
// To pointer
 
        pvoid = (void*) intPtr; // pv
        pvoid = (void*) uintPtr; // pv
        pvoid = (void*) nintPtr; // pv
        pvoid = (void*) nuintPtr; // pv
        pint = (int*) intPtr; // pv
        pint = (int*) uintPtr; // pv
        pint = (int*) nintPtr; // pv
        pint = (int*) nuintPtr; // pv
 
";
            string source5 = "}}";
 
            // All of the cases above should pass semantic analysis:
            var comp = CreateCompilation(source1 + source2 + source3 + source4 + source5, options: TestOptions.UnsafeReleaseDll);
            comp.VerifyDiagnostics();
            comp.VerifyEmitDiagnostics();
 
            // However, we have not yet implemented lowering and code generation for decimal,
            // lifted operators, nullable conversions and unsafe code so only generate code for
            // the straightforward intptr/number conversions:
 
            var verifier = CompileAndVerify(source: source1 + source3 + source5, options: TestOptions.UnsafeReleaseExe, expectedOutput: "");
        }
 
        [Fact, WorkItem(543427, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543427")]
        public void Bug11203()
        {
            string source = @"
class Program
{
    static void Main()
    {
        A x = 1;
    }
}
 
class A
{
    public static implicit operator A(ulong x)
    {
        return null;
    }
 
    public static implicit operator A(long x)
    {
        return null;
    }
}";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (6,15): error CS0457: Ambiguous user defined conversions 'A.implicit operator A(ulong)' and 'A.implicit operator A(long)' when converting from 'int' to 'A'
                Diagnostic(ErrorCode.ERR_AmbigUDConv, "1").WithArguments("A.implicit operator A(ulong)", "A.implicit operator A(long)", "int", "A"));
        }
 
        [Fact, WorkItem(543430, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543430")]
        public void Bug11205()
        {
            string source1 = @"
using System;
class A
{
    public A(short i) : this(i.ToString()) {}
    public A(string str) { this.str = str; }
    public readonly string str;
 
    public static implicit operator short(A x)
    {
        return (short)int.Parse(x.str);
    }
 
    public static implicit operator A(short x)
    {
        return new A(x.ToString());
    }
 
    public override string ToString() { return 'A' + str; }
 
    static void Main()
    {
        var a = new A(5);
        Console.Write((object)(a));
        Console.Write((object)(a++));
        Console.Write((object)(a));
        Console.Write((object)(++a));
        Console.Write((object)(a));
        Console.Write((object)(a--));
        Console.Write((object)(a));
        Console.Write((object)(--a));
        Console.Write((object)(a));
    }
}
";
 
            string source2 = @"
class A
{
    public static implicit operator int(A x)
    {
        return 1;
    }
 
    static void Main()
    {
        A a = new A();
        a++;
    }
}
";
 
            CompileAndVerify(source1, expectedOutput: "A5A5A6A7A7A7A6A5A5");
 
            var comp = CreateCompilation(source2);
            comp.VerifyDiagnostics(
// (13,9): error CS0029: Cannot implicitly convert type 'int' to 'A'
//         a++;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "a++").WithArguments("int", "A")
                );
        }
 
        [Fact, WorkItem(543435, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543435")]
        public void TestBug11210()
        {
            // If we have both a user-defined implicit conversion and a built-in explicit conversion
            // then the built-in explicit conversion "wins" when cast, and the user-defined implicit
            // conversion "wins" when there is no cast. 
 
            string source = @"
using System;
class B
{
    static void Main()
    {
        C<int> x = new D<int>();
        D<int> y = (D<int>)x;
        Console.Write(y == null ? 1 : 2);
        y = x;
        Console.Write(y == null ? 3 : 4);
    }
}
class C<T> { }
class D<T> : C<T>
{
    public static implicit operator D<T>(C<int> x)
    {
        return null;
    }
}";
 
            var verifier = CompileAndVerify(source, expectedOutput: "23");
        }
 
        [Fact, WorkItem(543436, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543436")]
        public void TestBug11211()
        {
            string source = @"
using System;
class C
{
    string str;
    public static explicit operator C(Func<string> x)
    {
        C c = new C();
        c.str = x();
        return c;
    }
 
    static void Main()
    {
        string x = 'A'.ToString();
        var c = (C)(() => x.ToLower());
        Console.WriteLine(c.str);
    }
}";
 
            var verifier = CompileAndVerify(source, expectedOutput: "a");
        }
 
        [Fact, WorkItem(543439, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543439")]
        public void TestBug11214()
        {
            // The specification describes analysis of user-defined conversions only in
            // terms of the types of the source and destination; it strongly implies that
            // analysis should only consider the type of the source expression and not the
            // source expression itself. The dev10 compiler certainly does not do this;
            // it allows user-defined conversions from null literals, lambdas, method groups,
            // and so on. 
            //
            // We should update the specification to say that the algorithm takes the 
            // expression being converted into account, not just its type.
 
            string source = @"
struct C
{
    string str;
    public static implicit operator C?(string x)
    {
        C c = new C();
        c.str = x.ToLower();
        return c;
    }
 
    static void Main()
    {
        C c = (C)'B'.ToString();
        System.Console.WriteLine(c.str);
    }
}";
            var verifier = CompileAndVerify(source, expectedOutput: "b");
        }
 
        [Fact, WorkItem(543440, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543440")]
        public void TestBug11215()
        {
            string source = @"
using System;
static class A
{
    static void Main()
    {
        C x = 1;
        Console.WriteLine(x.str);
    }
}
class C
{
    public string str;
    public static implicit operator C(byte s)
    {
        C c = new C();
        c.str = s.ToString();
        return c;
    }
}";
            var verifier = CompileAndVerify(source, expectedOutput: "1");
        }
 
        [Fact, WorkItem(543441, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543441")]
        public void TestBug11216()
        {
            // An ambiguous user-defined conversion should be considered a valid conversion
            // for the purposes of overload resolution; that is, this program should say that
            // Goo(B) and Goo(C) are both applicable candidates, and that neither is better,
            // even though the conversion from A to B is ambiguous.
 
            string source = @"
        class Program
        {
            static void Main()
            {
                A x = null;
                Goo(x);
            }
 
            static void Goo(B x) { }
            static void Goo(C x) { }
        }
 
        class A
        {
            public static implicit operator B(A x) { return null; }
        }
 
        class B
        {
            public static implicit operator B(A x) { return null; }
        }
 
        class C
        {
            public static implicit operator C(A x) { return null; }
        }
 ";
 
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(// (7,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Goo(B)' and 'Program.Goo(C)'
                                   //                 Goo(x);
                                   Diagnostic(ErrorCode.ERR_AmbigCall, "Goo").WithArguments("Program.Goo(B)", "Program.Goo(C)"));
        }
 
        [Fact, WorkItem(543446, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543446")]
        public void TestBug11223()
        {
            string source = @"
using System;
class C
{
    string str;
    public static explicit operator C(Func<string, string> x)
    {
        C c = new C();
        c.str = x('A'.ToString());
        return c;
    }
 
    static string M(string s) { return s.ToLower(); }
 
    static void Main()
    {
        var c = (C)M;
        Console.WriteLine(c.str);
    }
}";
 
            var verifier = CompileAndVerify(source, expectedOutput: "a");
        }
 
        [Fact, WorkItem(543595, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543595")]
        public void CompoundAssignment()
        {
            string source1 = @"
class A
{
    public static implicit operator A(int a)
    {
        return new A();
    }
    public static int operator +(A a, int b)
    {
        return 1;
    }
}
 
class Program
{
    static void Main()
    {
        A a = new A();
        a += 5;
    }
}
";
 
            var comp = CreateCompilation(source1);
            comp.VerifyDiagnostics();
 
            var verifier = CompileAndVerify(source: source1, expectedOutput: "");
        }
 
        [Fact, WorkItem(543598, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543598")]
        public void ConvertByteLiteralToUserDefinedType()
        {
            var source = @"
public class Conv
{
    static public implicit operator byte(Conv test) { return 1; }
    static public implicit operator Conv(byte val) { return null; }
}
class Test
{
    public static void Main()
    {
        Conv cl = new Conv();
        cl =  1;
    }
}";
            CompileAndVerify(source).VerifyDiagnostics();
        }
 
        [Fact, WorkItem(543789, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543789")]
        public void UseImplicitConversionInBase()
        {
            string source = @"
using System;
 
class Program
{
    static void Main()
    {
        if (-(new A()))
        {
            Console.Write(""Hello"");
        }
    }
}
 
class B
{
    public static implicit operator bool(B p)
    {
        return true;
    }
}
 
class A : B
{
    public static A operator -(A p)
    {
        return null;
    }
}";
            CompileAndVerify(source, expectedOutput: "Hello");
        }
 
        [Fact, WorkItem(682456, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/682456")]
        public void GenericUDConversionVersusPredefinedConversion()
        {
            string source = @"
class InArgument<T>
{
    public static implicit operator InArgument<T>(T t) { return default(InArgument<T>); }
}
 
public struct @start
{
    static public void Main()
    {
        object bar = null;
        InArgument<object> outArgument2 = bar as InArgument<object>;
    }
}";
            CreateCompilation(source).VerifyDiagnostics();
        }
 
        [Fact, WorkItem(1063555, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1063555")]
        public void UserDefinedImplicitConversionsOnBuiltinTypes()
        {
            string source = @"
namespace System
{
   	public class Object { }
    public class ValueType { }
    public struct Void { }
 
    public struct Byte { public static implicit operator MyClass(Byte v) { return null; } }
    public struct Int16 { public static implicit operator MyClass(Int16 v) { return null; } }
    public struct Int32 { public static implicit operator MyClass(Int32 v) { return null; } }
    public struct Int64 { public static implicit operator MyClass(Int64 v) { return null; } }
    public struct Single { public static implicit operator MyClass(Single v) { return null; } }
    public struct Double { public static implicit operator MyClass(Double v) { return null; } }
    public struct Char { public static implicit operator MyClass(Char v) { return null; } }
    public struct Boolean { public static implicit operator MyClass(Boolean v) { return null; } }
    public struct SByte { public static implicit operator MyClass(SByte v) { return null; } }
    public struct UInt16 { public static implicit operator MyClass(UInt16 v) { return null; } }
    public struct UInt32 { public static implicit operator MyClass(UInt32 v) { return null; } }
    public struct UInt64 { public static implicit operator MyClass(UInt64 v) { return null; } }
    public struct IntPtr { public static implicit operator MyClass(IntPtr v) { return null; } }
    public struct UIntPtr { public static implicit operator MyClass(UIntPtr v) { return null; } }
    public struct Decimal { public static implicit operator MyClass(Decimal v) { return null; } }
    public class String { public static implicit operator MyClass(String v) { return null; } }
 
    public class MyClass
    {
        static void Test(MyClass v) { }
 
        static void Main()
        {
            Test(new Byte());
            Test(new Int16());
            Test(new Int32());
            Test(new Int64());
            Test(new Single());
            Test(new Double());
            Test(new Char());
            Test(new Boolean());
            Test(new SByte());
            Test(new UInt16());
            Test(new UInt32());
            Test(new UInt64());
            Test(new IntPtr());
            Test(new String());
            
        }
    }
}
";
            CreateEmptyCompilation(source).VerifyDiagnostics();
        }
 
        [Fact, WorkItem(34876, "https://github.com/dotnet/roslyn/pull/34876")]
        public void GenericOperatorVoidConversion()
        {
            var source = @"
class C<T>
{
    public static implicit operator C<T>(T t) => new C<T>();
 
    private static void M1() { }
    private static C<object> M2()
    {
        return M1();
    }
}
";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (9,16): error CS0029: Cannot implicitly convert type 'void' to 'C<object>'
                //         return M1();
                Diagnostic(ErrorCode.ERR_NoImplicitConv, "M1()").WithArguments("void", "C<object>").WithLocation(9, 16));
        }
 
        [Fact, WorkItem(34876, "https://github.com/dotnet/roslyn/pull/34876")]
        public void GenericOperatorVoidConversion_Cast()
        {
            var source = @"
class C<T>
{
    public static explicit operator C<T>(T t) => new C<T>();
 
    private static void M1() { }
    private static C<object> M2()
    {
        return (C<object>) M1();
    }
}
";
            var comp = CreateCompilation(source);
            comp.VerifyDiagnostics(
                // (9,16): error CS0030: Cannot convert type 'void' to 'C<object>'
                //         return (C<object>) M1();
                Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C<object>) M1()").WithArguments("void", "C<object>").WithLocation(9, 16));
        }
 
        [Fact, WorkItem(56646, "https://github.com/dotnet/roslyn/issues/56646")]
        public void LiftedConversion_InvalidTypeArgument01()
        {
            var code = @"
int? i = null;
C c = i;
 
class C
{
    public static implicit operator C(long l) => throw null;
}
 
namespace System
{
    public class Object {}
    public class String {}
    public class Exception {}
    public class ValueType : Object {}
    public struct Void {}
    public struct Int32 {}
    public struct Nullable<T> where T : struct {}
    public ref struct Int64 {}
}
";
 
            var comp = CreateEmptyCompilation(code);
            comp.VerifyDiagnostics(
                // (3,7): error CS0266: Cannot implicitly convert type 'int?' to 'C'. An explicit conversion exists (are you missing a cast?)
                // C c = i;
                Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "i").WithArguments("int?", "C").WithLocation(3, 7)
            );
        }
 
        [Fact, WorkItem(56646, "https://github.com/dotnet/roslyn/issues/56646")]
        public void LiftedConversion_InvalidTypeArgument02()
        {
            var code = @"
C c = null;
M(c);
 
void M(long? i) => throw null;
 
class C
{
    public static implicit operator int(C c) => throw null;
}
 
namespace System
{
    public class Object {}
    public class String {}
    public class Exception {}
    public class ValueType : Object {}
    public struct Void {}
    public ref struct Int32 {}
    public struct Nullable<T> where T : struct { public Nullable(T value) {} }
    public struct Int64 {}
    public struct Boolean { }
    public class Attribute { }
    public class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets t) { }
        public bool AllowMultiple { get; set; }
        public bool Inherited { get; set; }
    }
    public struct Enum { }
    public enum AttributeTargets { }
}
";
 
            var comp = CreateEmptyCompilation(code);
            var verifier = CompileAndVerify(comp, verify: Verification.Skipped);
 
            // Note that no int? is being created.
            verifier.VerifyIL("<top-level-statements-entry-point>", @"
{
  // Code size       18 (0x12)
  .maxstack  1
  IL_0000:  ldnull
  IL_0001:  call       ""int C.op_Implicit(C)""
  IL_0006:  conv.i8
  IL_0007:  newobj     ""long?..ctor(long)""
  IL_000c:  call       ""void Program.<<Main>$>g__M|0_0(long?)""
  IL_0011:  ret
}
");
        }
 
        [Fact, WorkItem(56646, "https://github.com/dotnet/roslyn/issues/56646")]
        public void LiftedConversion_InvalidTypeArgument03()
        {
            var code = @"
int? i = null;
C c = (C)i;
 
class C
{
    public static explicit operator C(long l) => throw null;
}
 
namespace System
{
    public class Object {}
    public class String {}
    public class Exception {}
    public class ValueType : Object {}
    public struct Void {}
    public struct Int32 {}
    public struct Nullable<T> where T : struct { public T Value { get => throw null; } }
    public ref struct Int64 {}
    public struct Boolean { }
    public class Attribute { }
    public class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets t) { }
        public bool AllowMultiple { get; set; }
        public bool Inherited { get; set; }
    }
    public struct Enum { }
    public enum AttributeTargets { }
}
";
 
            var comp = CreateEmptyCompilation(code);
            var verifier = CompileAndVerify(comp, verify: Verification.Skipped);
 
            verifier.VerifyIL("<top-level-statements-entry-point>", @"
{
  // Code size       23 (0x17)
  .maxstack  1
  .locals init (int? V_0) //i
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""int?""
  IL_0008:  ldloca.s   V_0
  IL_000a:  call       ""int int?.Value.get""
  IL_000f:  conv.i8
  IL_0010:  call       ""C C.op_Explicit(long)""
  IL_0015:  pop
  IL_0016:  ret
}
");
        }
 
        [Fact, WorkItem(56646, "https://github.com/dotnet/roslyn/issues/56646")]
        public void LiftedConversion_InvalidTypeArgument04()
        {
            var code = @"
C c = null;
M((long?)c);
 
void M(long? i) => throw null;
 
class C
{
    public static explicit operator int(C c) => throw null;
}
 
namespace System
{
    public class Object {}
    public class String {}
    public class Exception {}
    public class ValueType : Object {}
    public struct Void {}
    public ref struct Int32 {}
    public struct Nullable<T> where T : struct { public Nullable(T value) {} }
    public struct Int64 {}
    public struct Boolean { }
    public class Attribute { }
    public class AttributeUsageAttribute : Attribute
    {
        public AttributeUsageAttribute(AttributeTargets t) { }
        public bool AllowMultiple { get; set; }
        public bool Inherited { get; set; }
    }
    public struct Enum { }
    public enum AttributeTargets { }
}
";
 
            var comp = CreateEmptyCompilation(code);
            var verifier = CompileAndVerify(comp, verify: Verification.Skipped);
 
            // Note that no int? is being created.
            verifier.VerifyIL("<top-level-statements-entry-point>", @"
{
  // Code size       18 (0x12)
  .maxstack  1
  IL_0000:  ldnull
  IL_0001:  call       ""int C.op_Explicit(C)""
  IL_0006:  conv.i8
  IL_0007:  newobj     ""long?..ctor(long)""
  IL_000c:  call       ""void Program.<<Main>$>g__M|0_0(long?)""
  IL_0011:  ret
}
");
        }
 
        [Fact, WorkItem(56646, "https://github.com/dotnet/roslyn/issues/56646")]
        public void LiftedConversion_InvalidTypeArgument05()
        {
            var code = @"
unsafe
{
    S? s = null;
    void* f = s;
    System.Console.WriteLine((int)f);
}
 
public struct S
{
    public static unsafe implicit operator void*(S v) => throw null;
}
";
 
            var comp = CreateCompilation(code, options: TestOptions.UnsafeReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: "0", verify: Verification.Skipped);
 
            verifier.VerifyIL("<top-level-statements-entry-point>", @"
{
  // Code size       42 (0x2a)
  .maxstack  1
  .locals init (S? V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""S?""
  IL_0008:  ldloc.0
  IL_0009:  stloc.0
  IL_000a:  ldloca.s   V_0
  IL_000c:  call       ""bool S?.HasValue.get""
  IL_0011:  brtrue.s   IL_0017
  IL_0013:  ldc.i4.0
  IL_0014:  conv.u
  IL_0015:  br.s       IL_0023
  IL_0017:  ldloca.s   V_0
  IL_0019:  call       ""S S?.GetValueOrDefault()""
  IL_001e:  call       ""void* S.op_Implicit(S)""
  IL_0023:  conv.i4
  IL_0024:  call       ""void System.Console.WriteLine(int)""
  IL_0029:  ret
}
");
        }
 
        [Fact, WorkItem(56646, "https://github.com/dotnet/roslyn/issues/56646")]
        public void LiftedConversion_InvalidTypeArgument06()
        {
            var code = @"
unsafe
{
    S? s = null;
    void* f = (void*)s;
    System.Console.WriteLine((int)f);
}
 
public struct S
{
    public static unsafe explicit operator void*(S v) => throw null;
}
";
 
            var comp = CreateCompilation(code, options: TestOptions.UnsafeReleaseExe);
            var verifier = CompileAndVerify(comp, expectedOutput: "0", verify: Verification.Skipped);
 
            verifier.VerifyIL("<top-level-statements-entry-point>", @"
{
  // Code size       42 (0x2a)
  .maxstack  1
  .locals init (S? V_0)
  IL_0000:  ldloca.s   V_0
  IL_0002:  initobj    ""S?""
  IL_0008:  ldloc.0
  IL_0009:  stloc.0
  IL_000a:  ldloca.s   V_0
  IL_000c:  call       ""bool S?.HasValue.get""
  IL_0011:  brtrue.s   IL_0017
  IL_0013:  ldc.i4.0
  IL_0014:  conv.u
  IL_0015:  br.s       IL_0023
  IL_0017:  ldloca.s   V_0
  IL_0019:  call       ""S S?.GetValueOrDefault()""
  IL_001e:  call       ""void* S.op_Explicit(S)""
  IL_0023:  conv.i4
  IL_0024:  call       ""void System.Console.WriteLine(int)""
  IL_0029:  ret
}
");
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_ImplicitObjectCreation_Ambiguous()
        {
            var source = """
                C c = ("a", new());
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", new());
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", new())").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_ImplicitObjectCreation_NotAmbiguous()
        {
            var source = """
                using System;
 
                C c = ("a", new());
 
                class C
                {
                    public static implicit operator C((string, int) pair)
                    {
                        Console.WriteLine("int");
                        return new C();
                    }
                }
                """;
 
            var verifier = CompileAndVerify(source, expectedOutput: "int");
            verifier.VerifyDiagnostics();
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_DefaultLiteral_Ambiguous()
        {
            var source = """
                C c = ("a", default);
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", default);
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", default)").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_NullLiteral_NotAmbiguous()
        {
            var source = """
                using System;
 
                C c = ("a", null);
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair)
                    {
                        Console.WriteLine("string");
                        return new C();
                    }
                }
                """;
 
            var verifier = CompileAndVerify(source, expectedOutput: "string");
            verifier.VerifyDiagnostics();
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_NullLiteral_NoConversion()
        {
            var source = """
                C c = ("a", null);
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", null);
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", null)").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_SwitchExpression_Ambiguous()
        {
            var source = """
                C c = ("a", "b" switch { _ => default });
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", "b" switch { _ => default });
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", ""b"" switch { _ => default })").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_SwitchExpression_NotAmbiguous()
        {
            var source = """
                using System;
 
                C c = ("a", "b" switch { _ => null });
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair)
                    {
                        Console.WriteLine("string");
                        return new C();
                    }
                }
                """;
 
            var verifier = CompileAndVerify(source, expectedOutput: "string");
            verifier.VerifyDiagnostics();
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_SwitchExpression_NoConversion()
        {
            var source = """
                C c = ("a", "b" switch { _ => null });
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", "b" switch { _ => null });
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", ""b"" switch { _ => null })").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_CollectionExpression_Ambiguous()
        {
            var source = """
                C c = ("a", [default]);
 
                class C
                {
                    public static implicit operator C((string, int[]) pair) => new C();
                    public static implicit operator C((string, string[]) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", [default]);
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", [default])").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_CollectionExpression_NotAmbiguous()
        {
            var source = """
                using System;
 
                C c = ("a", [null]);
 
                class C
                {
                    public static implicit operator C((string, int[]) pair) => new C();
                    public static implicit operator C((string, string[]) pair)
                    {
                        Console.WriteLine("string[]");
                        return new C();
                    }
                }
                """;
 
            var verifier = CompileAndVerify(source, expectedOutput: "string[]");
            verifier.VerifyDiagnostics();
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_CollectionExpression_NoConversion()
        {
            var source = """
                C c = ("a", [null]);
 
                class C
                {
                    public static implicit operator C((string, int[]) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (1,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", [null]);
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", [null])").WithArguments("2", "C").WithLocation(1, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_ConditionalExpression_Ambiguous()
        {
            var source = """
                bool b = true;
                C c = ("a", b ? default : throw null!);
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (2,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", b ? default : throw null!);
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", b ? default : throw null!)").WithArguments("2", "C").WithLocation(2, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_ConditionalExpression_NotAmbiguous()
        {
            var source = """
                using System;
 
                bool b = true;
                C c = ("a", b ? null : throw null!);
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                    public static implicit operator C((string, string) pair)
                    {
                        Console.WriteLine("string");
                        return new C();
                    }
                }
                """;
 
            var verifier = CompileAndVerify(source, expectedOutput: "string");
            verifier.VerifyDiagnostics();
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Tuple_UserDefinedConversion_ConditionalExpression_NoConversion()
        {
            var source = """
                bool b = true;
                C c = ("a", b ? null : throw null!);
 
                class C
                {
                    public static implicit operator C((string, int) pair) => new C();
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (2,7): error CS8135: Tuple with 2 elements cannot be converted to type 'C'.
                // C c = ("a", b ? null : throw null!);
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""a"", b ? null : throw null!)").WithArguments("2", "C").WithLocation(2, 7));
        }
 
        [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2088611")]
        public void Repro_VsFeedback_2088611()
        {
            var source = """
                using System;
                using System.Collections.Generic;
                public struct SpecialStruct
                {
                    public readonly string Value;
                    public readonly bool Flag;
                    public readonly Dictionary<string, string> Map;
 
                    public SpecialStruct(string value)
                    {
                        this.Value = value;
                    }
                    public SpecialStruct(string value, bool flag)
                    {
                        this.Value = value;
                        this.Flag = flag;
                    }
                    public SpecialStruct(string value, Dictionary<string, string> map)
                    {
                        Value = value;
                        Map = map;
                    }
 
                    public static implicit operator SpecialStruct(string s) => new(s);
                    public static implicit operator SpecialStruct((string, Dictionary<string, string>) tuple) => new(tuple.Item1, tuple.Item2);
                    public static implicit operator SpecialStruct((string, bool) tuple) => new(tuple.Item1, tuple.Item2);
                }
 
                public class Class1
                {
                    Dictionary<string, SpecialStruct> specialMap = new()
                        {
                            { "key1", "value1" },
                            { "key2", ("value2", true) },
                            { "key3", ("value3", new /*specific type is omitted*/ (StringComparer.OrdinalIgnoreCase)
                                {
                                    { "subkey", "subvalue" },
                                })
                            },
                        };
                }
                """;
 
            var comp = CreateCompilation(source);
            comp.VerifyEmitDiagnostics(
                // (35,23): error CS8135: Tuple with 2 elements cannot be converted to type 'SpecialStruct'.
                //             { "key3", ("value3", new /*specific type is omitted*/ (StringComparer.OrdinalIgnoreCase)
                Diagnostic(ErrorCode.ERR_ConversionNotTupleCompatible, @"(""value3"", new /*specific type is omitted*/ (StringComparer.OrdinalIgnoreCase)
                {
                    { ""subkey"", ""subvalue"" },
                })").WithArguments("2", "SpecialStruct").WithLocation(35, 23),
                // (35,34): error CS8754: There is no target type for 'new(System.StringComparer)'
                //             { "key3", ("value3", new /*specific type is omitted*/ (StringComparer.OrdinalIgnoreCase)
                Diagnostic(ErrorCode.ERR_ImplicitObjectCreationNoTargetType, @"new /*specific type is omitted*/ (StringComparer.OrdinalIgnoreCase)
                {
                    { ""subkey"", ""subvalue"" },
                }").WithArguments("new(System.StringComparer)").WithLocation(35, 34));
        }
    }
}