From df95d51a64d3a60a585c58776c66baa987186888 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 15 Sep 2021 08:24:59 -0700 Subject: [PATCH 1/6] Handle user-defined conversions from lambda expressions or method groups to Delegate, Expression, and base types --- .../UserDefinedImplicitConversions.cs | 3 + .../Semantic/Semantics/DelegateTypeTests.cs | 660 ++++++++++++++++++ 2 files changed, 663 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index 4bc7e128466c8..346c42d85c673 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -642,6 +642,9 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) // Added for C# 7.1 case ConversionKind.DefaultLiteral: + + // Added for C# 10. + case ConversionKind.FunctionType: return true; default: diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 7ce5d30c70ade..bdf2862542151 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -6478,6 +6478,666 @@ private static void VerifyExpressionType(SemanticModel model, ExpressionSyntax v Assert.Equal(expectedType, type.ToTestDisplayString()); } + [WorkItem(56407, "https://github.com/dotnet/roslyn/issues/56407")] + [Fact] + public void UserDefinedConversions_01() + { + var source = +@"using System.Linq.Expressions; + +public class Program +{ + public static void Main() + { + SomeMethod((Employee e) => e.Name); + } + + public static void SomeMethod(Field field) { } + + public class Employee + { + public string Name { get; set; } + } + + public class Field + { + public static implicit operator Field(Expression expression) => null; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (7,20): error CS1660: Cannot convert lambda expression to type 'Program.Field' because it is not a delegate type + // SomeMethod((Employee e) => e.Name); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(Employee e) => e.Name").WithArguments("lambda expression", "Program.Field").WithLocation(7, 20)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void UserDefinedConversions_Implicit_01() + { + var source = +@"using System; +using System.Linq.Expressions; +class C1 +{ + public static implicit operator C1(Func f) { Console.WriteLine(""operator C1(Func f)""); return new C1(); } +} +class C2 +{ + public static implicit operator C2(Expression> e) { Console.WriteLine(""operator C2(Expression> e)""); return new C2(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + C1 c1 = () => 1; + C2 c2 = () => 2; + c1 = F; + _ = (C1)(() => 1); + _ = (C2)(() => 2); + _ = (C1)F; + } +}"; + + + var expectedDiagnostics = new[] + { + // (16,17): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // C1 c1 = () => 1; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(16, 17), + // (17,17): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // C2 c2 = () => 2; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(17, 17), + // (18,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C1'. Did you intend to invoke the method? + // c1 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C1").WithLocation(18, 14) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UserDefinedConversions_Implicit_02() + { + var source = +@"using System; +class C1 +{ + public static implicit operator C1(object o) { Console.WriteLine(""operator C1(object o)""); return new C1(); } +} +class C2 +{ + public static implicit operator C2(ICloneable c) { Console.WriteLine(""operator C2(ICloneable c)""); return new C2(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + C1 c1 = () => 1; + C2 c2 = () => 2; + c1 = F; + c2 = F; + _ = (C1)(() => 1); + _ = (C2)(() => 2); + _ = (C1)F; + _ = (C2)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (4,37): error CS0553: 'C1.implicit operator C1(object)': user-defined conversions to or from a base type are not allowed + // public static implicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } + Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.implicit operator C1(object)").WithLocation(4, 37), + // (8,37): error CS0552: 'C2.implicit operator C2(ICloneable)': user-defined conversions to or from an interface are not allowed + // public static implicit operator C2(ICloneable c) { Console.WriteLine("operator C2(ICloneable c)"); return new C2(); } + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C2").WithArguments("C2.implicit operator C2(System.ICloneable)").WithLocation(8, 37), + // (15,17): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // C1 c1 = () => 1; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(15, 17), + // (16,17): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // C2 c2 = () => 2; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(16, 17), + // (17,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C1'. Did you intend to invoke the method? + // c1 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C1").WithLocation(17, 14), + // (18,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C2'. Did you intend to invoke the method? + // c2 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C2").WithLocation(18, 14), + // (19,18): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // _ = (C1)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(19, 18), + // (20,18): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // _ = (C2)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(20, 18), + // (21,13): error CS0030: Cannot convert type 'method' to 'C1' + // _ = (C1)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(21, 13), + // (22,13): error CS0030: Cannot convert type 'method' to 'C2' + // _ = (C2)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(22, 13)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,37): error CS0553: 'C1.implicit operator C1(object)': user-defined conversions to or from a base type are not allowed + // public static implicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } + Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.implicit operator C1(object)").WithLocation(4, 37), + // (8,37): error CS0552: 'C2.implicit operator C2(ICloneable)': user-defined conversions to or from an interface are not allowed + // public static implicit operator C2(ICloneable c) { Console.WriteLine("operator C2(ICloneable c)"); return new C2(); } + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C2").WithArguments("C2.implicit operator C2(System.ICloneable)").WithLocation(8, 37)); + } + + [Fact] + public void UserDefinedConversions_Implicit_03() + { + var source = +@"using System; +using System.Linq.Expressions; +class C1 +{ + public static implicit operator C1(Delegate d) { Console.WriteLine(""operator C1(Delegate d)""); return new C1(); } +} +class C2 +{ + public static implicit operator C2(MulticastDelegate d) { Console.WriteLine(""operator C2(MulticastDelegate d)""); return new C2(); } +} +class C3 +{ + public static implicit operator C3(Expression e) { Console.WriteLine(""operator C3(Expression e)""); return new C3(); } +} +class C4 +{ + public static implicit operator C4(LambdaExpression e) { Console.WriteLine(""operator C4(LambdaExpression e)""); return new C4(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + C1 c1 = () => 1; + C2 c2 = () => 2; + C3 c3 = () => 3; + C4 c4 = () => 4; + c1 = F; + c2 = F; + _ = (C1)(() => 1); + _ = (C2)(() => 2); + _ = (C3)(() => 3); + _ = (C4)(() => 4); + _ = (C1)F; + _ = (C2)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (24,17): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // C1 c1 = () => 1; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(24, 17), + // (25,17): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // C2 c2 = () => 2; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(25, 17), + // (26,17): error CS1660: Cannot convert lambda expression to type 'C3' because it is not a delegate type + // C3 c3 = () => 3; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 3").WithArguments("lambda expression", "C3").WithLocation(26, 17), + // (27,17): error CS1660: Cannot convert lambda expression to type 'C4' because it is not a delegate type + // C4 c4 = () => 4; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 4").WithArguments("lambda expression", "C4").WithLocation(27, 17), + // (28,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C1'. Did you intend to invoke the method? + // c1 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C1").WithLocation(28, 14), + // (29,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C2'. Did you intend to invoke the method? + // c2 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C2").WithLocation(29, 14), + // (30,18): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // _ = (C1)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(30, 18), + // (31,18): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // _ = (C2)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(31, 18), + // (32,18): error CS1660: Cannot convert lambda expression to type 'C3' because it is not a delegate type + // _ = (C3)(() => 3); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 3").WithArguments("lambda expression", "C3").WithLocation(32, 18), + // (33,18): error CS1660: Cannot convert lambda expression to type 'C4' because it is not a delegate type + // _ = (C4)(() => 4); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 4").WithArguments("lambda expression", "C4").WithLocation(33, 18), + // (34,13): error CS0030: Cannot convert type 'method' to 'C1' + // _ = (C1)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(34, 13), + // (35,13): error CS0030: Cannot convert type 'method' to 'C2' + // _ = (C2)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(35, 13)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"operator C1(Delegate d) +operator C2(MulticastDelegate d) +operator C3(Expression e) +operator C4(LambdaExpression e) +operator C1(Delegate d) +operator C2(MulticastDelegate d) +operator C1(Delegate d) +operator C2(MulticastDelegate d) +operator C3(Expression e) +operator C4(LambdaExpression e) +operator C1(Delegate d) +operator C2(MulticastDelegate d) +"); + } + + [Fact] + public void UserDefinedConversions_Implicit_04() + { + var source = +@"using System; +class C +{ + public static implicit operator C(T t) { Console.WriteLine(""operator C<{0}>({0} t)"", typeof(T).FullName); return new C(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + C c1 = () => 1; + C c2 = () => 2; + c1 = F; + c2 = F; + _ = (C)(() => 1); + _ = (C)(() => 2); + _ = (C)F; + _ = (C)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (11,24): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // C c1 = () => 1; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(11, 24), + // (12,28): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // C c2 = () => 2; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C").WithLocation(12, 28), + // (13,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C'. Did you intend to invoke the method? + // c1 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C").WithLocation(13, 14), + // (14,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C'. Did you intend to invoke the method? + // c2 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C").WithLocation(14, 14), + // (15,25): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(15, 25), + // (16,29): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C").WithLocation(16, 29), + // (17,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(17, 13), + // (18,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(18, 13)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"operator C(System.Object t) +operator C(System.ICloneable t) +operator C(System.Object t) +operator C(System.ICloneable t) +operator C(System.Object t) +operator C(System.ICloneable t) +operator C(System.Object t) +operator C(System.ICloneable t) +"); + } + + [Fact] + public void UserDefinedConversions_Implicit_05() + { + var source = +@"using System; +using System.Linq.Expressions; +class C +{ + public static implicit operator C(T t) { Console.WriteLine(""operator C<{0}>({0} t)"", typeof(T).FullName); return new C(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + C c1 = () => 1; + C c2 = () => 2; + C c3 = () => 3; + C c4 = () => 4; + c1 = F; + c2 = F; + _ = (C)(() => 1); + _ = (C)(() => 2); + _ = (C)(() => 3); + _ = (C)(() => 4); + _ = (C)F; + _ = (C)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (12,26): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // C c1 = () => 1; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(12, 26), + // (13,35): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // C c2 = () => 2; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C").WithLocation(13, 35), + // (14,28): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // C c3 = () => 3; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 3").WithArguments("lambda expression", "C").WithLocation(14, 28), + // (15,34): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // C c4 = () => 4; + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 4").WithArguments("lambda expression", "C").WithLocation(15, 34), + // (16,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C'. Did you intend to invoke the method? + // c1 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C").WithLocation(16, 14), + // (17,14): error CS0428: Cannot convert method group 'F' to non-delegate type 'C'. Did you intend to invoke the method? + // c2 = F; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "F").WithArguments("F", "C").WithLocation(17, 14), + // (18,27): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(18, 27), + // (19,36): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C").WithLocation(19, 36), + // (20,29): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 3); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 3").WithArguments("lambda expression", "C").WithLocation(20, 29), + // (21,35): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 4); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 4").WithArguments("lambda expression", "C").WithLocation(21, 35), + // (22,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(22, 13), + // (23,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(23, 13)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"operator C(System.Delegate t) +operator C(System.MulticastDelegate t) +operator C(System.Linq.Expressions.Expression t) +operator C(System.Linq.Expressions.LambdaExpression t) +operator C(System.Delegate t) +operator C(System.MulticastDelegate t) +operator C(System.Delegate t) +operator C(System.MulticastDelegate t) +operator C(System.Linq.Expressions.Expression t) +operator C(System.Linq.Expressions.LambdaExpression t) +operator C(System.Delegate t) +operator C(System.MulticastDelegate t) +"); + } + + [Fact] + public void UserDefinedConversions_Explicit_01() + { + var source = +@"using System; +using System.Linq.Expressions; +class C1 +{ + public static explicit operator C1(Func f) { Console.WriteLine(""operator C1(Func f)""); return new C1(); } +} +class C2 +{ + public static explicit operator C2(Expression> e) { Console.WriteLine(""operator C2(Expression> e)""); return new C2(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + _ = (C1)(() => 1); + _ = (C2)(() => 2); + _ = (C1)F; + } +}"; + + string expectedOutput = +@"operator C1(Func f) +operator C2(Expression> e) +operator C1(Func f) +"; + CompileAndVerify(source, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput); + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput); + } + + [Fact] + public void UserDefinedConversions_Explicit_02() + { + var source = +@"using System; +class C1 +{ + public static explicit operator C1(object o) { Console.WriteLine(""operator C1(object o)""); return new C1(); } +} +class C2 +{ + public static explicit operator C2(ICloneable c) { Console.WriteLine(""operator C2(ICloneable c)""); return new C2(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + _ = (C1)(() => 1); + _ = (C2)(() => 2); + _ = (C1)F; + _ = (C2)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (4,37): error CS0553: 'C1.explicit operator C1(object)': user-defined conversions to or from a base type are not allowed + // public static explicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } + Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.explicit operator C1(object)").WithLocation(4, 37), + // (8,37): error CS0552: 'C2.explicit operator C2(ICloneable)': user-defined conversions to or from an interface are not allowed + // public static explicit operator C2(ICloneable c) { Console.WriteLine("operator C2(ICloneable c)"); return new C2(); } + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C2").WithArguments("C2.explicit operator C2(System.ICloneable)").WithLocation(8, 37), + // (15,18): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // _ = (C1)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(15, 18), + // (16,18): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // _ = (C2)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(16, 18), + // (17,13): error CS0030: Cannot convert type 'method' to 'C1' + // _ = (C1)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(17, 13), + // (18,13): error CS0030: Cannot convert type 'method' to 'C2' + // _ = (C2)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(18, 13)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,37): error CS0553: 'C1.explicit operator C1(object)': user-defined conversions to or from a base type are not allowed + // public static explicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } + Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.explicit operator C1(object)").WithLocation(4, 37), + // (8,37): error CS0552: 'C2.explicit operator C2(ICloneable)': user-defined conversions to or from an interface are not allowed + // public static explicit operator C2(ICloneable c) { Console.WriteLine("operator C2(ICloneable c)"); return new C2(); } + Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C2").WithArguments("C2.explicit operator C2(System.ICloneable)").WithLocation(8, 37)); + } + + [Fact] + public void UserDefinedConversions_Explicit_03() + { + var source = +@"using System; +using System.Linq.Expressions; +class C1 +{ + public static explicit operator C1(Delegate d) { Console.WriteLine(""operator C1(Delegate d)""); return new C1(); } +} +class C2 +{ + public static explicit operator C2(MulticastDelegate d) { Console.WriteLine(""operator C2(MulticastDelegate d)""); return new C2(); } +} +class C3 +{ + public static explicit operator C3(Expression e) { Console.WriteLine(""operator C3(Expression e)""); return new C3(); } +} +class C4 +{ + public static explicit operator C4(LambdaExpression e) { Console.WriteLine(""operator C4(LambdaExpression e)""); return new C4(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + _ = (C1)(() => 1); + _ = (C2)(() => 2); + _ = (C3)(() => 3); + _ = (C4)(() => 4); + _ = (C1)F; + _ = (C2)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (24,18): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type + // _ = (C1)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(24, 18), + // (25,18): error CS1660: Cannot convert lambda expression to type 'C2' because it is not a delegate type + // _ = (C2)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C2").WithLocation(25, 18), + // (26,18): error CS1660: Cannot convert lambda expression to type 'C3' because it is not a delegate type + // _ = (C3)(() => 3); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 3").WithArguments("lambda expression", "C3").WithLocation(26, 18), + // (27,18): error CS1660: Cannot convert lambda expression to type 'C4' because it is not a delegate type + // _ = (C4)(() => 4); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 4").WithArguments("lambda expression", "C4").WithLocation(27, 18), + // (28,13): error CS0030: Cannot convert type 'method' to 'C1' + // _ = (C1)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(28, 13), + // (29,13): error CS0030: Cannot convert type 'method' to 'C2' + // _ = (C2)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(29, 13)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"operator C1(Delegate d) +operator C2(MulticastDelegate d) +operator C3(Expression e) +operator C4(LambdaExpression e) +operator C1(Delegate d) +operator C2(MulticastDelegate d) +"); + } + + [Fact] + public void UserDefinedConversions_Explicit_04() + { + var source = +@"using System; +class C +{ + public static explicit operator C(T t) { Console.WriteLine(""operator C<{0}>({0} t)"", typeof(T).FullName); return new C(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + _ = (C)(() => 1); + _ = (C)(() => 2); + _ = (C)F; + _ = (C)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (11,25): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(11, 25), + // (12,29): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C").WithLocation(12, 29), + // (13,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(13, 13), + // (14,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(14, 13)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"operator C(System.Object t) +operator C(System.ICloneable t) +operator C(System.Object t) +operator C(System.ICloneable t) +"); + } + + [Fact] + public void UserDefinedConversions_Explicit_05() + { + var source = +@"using System; +using System.Linq.Expressions; +class C +{ + public static explicit operator C(T t) { Console.WriteLine(""operator C<{0}>({0} t)"", typeof(T).FullName); return new C(); } +} +class Program +{ + static int F() => 0; + static void Main() + { + _ = (C)(() => 1); + _ = (C)(() => 2); + _ = (C)(() => 3); + _ = (C)(() => 4); + _ = (C)F; + _ = (C)F; + } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (12,27): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 1); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(12, 27), + // (13,36): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 2); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 2").WithArguments("lambda expression", "C").WithLocation(13, 36), + // (14,29): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 3); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 3").WithArguments("lambda expression", "C").WithLocation(14, 29), + // (15,35): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type + // _ = (C)(() => 4); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 4").WithArguments("lambda expression", "C").WithLocation(15, 35), + // (16,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(16, 13), + // (17,13): error CS0030: Cannot convert type 'method' to 'C' + // _ = (C)F; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(17, 13)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"operator C(System.Delegate t) +operator C(System.MulticastDelegate t) +operator C(System.Linq.Expressions.Expression t) +operator C(System.Linq.Expressions.LambdaExpression t) +operator C(System.Delegate t) +operator C(System.MulticastDelegate t) +"); + } + [Fact] public void TaskRunArgument() { From 6539d9a7d7f6b35b4920d21e0572be0e73ee6360 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Fri, 17 Sep 2021 14:58:30 -0700 Subject: [PATCH 2/6] Remove function type conversions from standard conversions --- .../Binder/Semantics/BestTypeInferrer.cs | 4 +- .../Semantics/Conversions/ConversionsBase.cs | 134 +++++++++--- .../UserDefinedImplicitConversions.cs | 4 +- .../OverloadResolution/MethodTypeInference.cs | 2 +- .../Portable/Binder/UsingStatementBinder.cs | 4 +- .../Portable/FlowAnalysis/NullableWalker.cs | 4 +- .../Semantic/Semantics/DelegateTypeTests.cs | 196 ++++++++---------- 7 files changed, 201 insertions(+), 147 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs index d3059e2cd3647..4f214f5cc4d3c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs @@ -256,8 +256,8 @@ public static NullableFlowState GetNullableState(ArrayBuilder typ } var conversionsWithoutNullability = conversions.WithNullability(false); - var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type1, type2, ref useSiteInfo).Exists; - var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromType(type2, type1, ref useSiteInfo).Exists; + var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(type1, type2, ref useSiteInfo).Exists; + var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(type2, type1, ref useSiteInfo).Exists; if (t1tot2 && t2tot1) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 2fcb3c077b57d..0f4ecccc46332 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -75,6 +75,7 @@ internal ConversionsBase WithNullability(bool includeNullability) internal AssemblySymbol CorLibrary { get { return corLibrary; } } +#nullable enable /// /// Determines if the source expression is convertible to the destination type via /// any built-in or user-defined implicit conversion. @@ -84,10 +85,10 @@ public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourc Debug.Assert(sourceExpression != null); Debug.Assert((object)destination != null); - var sourceType = sourceExpression.GetTypeOrFunctionType(); + var sourceType = sourceExpression.Type; //PERF: identity conversion is by far the most common implicit conversion, check for that first - if ((object)sourceType != null && HasIdentityConversionInternal(sourceType, destination)) + if (sourceType is { } && HasIdentityConversionInternal(sourceType, destination)) { return Conversion.Identity; } @@ -98,7 +99,7 @@ public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourc return conversion; } - if ((object)sourceType != null) + if (sourceType is { }) { // Try using the short-circuit "fast-conversion" path. Conversion fastConversion = FastClassifyConversion(sourceType, destination); @@ -118,6 +119,13 @@ public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourc } } } + else if (sourceExpression.GetFunctionType() is { }) + { + if (IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo)) + { + return Conversion.FunctionType; + } + } conversion = GetImplicitUserDefinedConversion(sourceExpression, sourceType, destination, ref useSiteInfo); if (conversion.Exists) @@ -171,6 +179,34 @@ public Conversion ClassifyImplicitConversionFromType(TypeSymbol source, TypeSymb return GetImplicitUserDefinedConversion(null, source, destination, ref useSiteInfo); } + /// + /// Helper method that calls or + /// depending on whether the + /// types are instances. + /// Used by method type inference and best common type only. + /// + public Conversion ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) + { + var sourceFunctionType = source as FunctionTypeSymbol; + var destinationFunctionType = destination as FunctionTypeSymbol; + + if (sourceFunctionType is null && destinationFunctionType is null) + { + return ClassifyImplicitConversionFromType(source, destination, ref useSiteInfo); + } + + if (sourceFunctionType is { } && destinationFunctionType is { }) + { + return HasImplicitFunctionTypeConversion(sourceFunctionType, destinationFunctionType, ref useSiteInfo) ? + Conversion.FunctionType : + Conversion.NoConversion; + } + + Debug.Assert(false); + return Conversion.NoConversion; + } +#nullable disable + /// /// Determines if the source expression of given type is convertible to the destination type via /// any built-in or user-defined conversion. @@ -513,10 +549,57 @@ public Conversion ClassifyStandardConversion(BoundExpression sourceExpression, T return Conversion.NoConversion; } + private static bool IsStandardImplicitConversionFromExpression(ConversionKind kind) + { + if (IsStandardImplicitConversionFromType(kind)) + { + return true; + } + + // See comment in ClassifyStandardImplicitConversion(BoundExpression, ...) + // where the set of standard implicit conversions is extended from the spec + // to include conversions from expression. + switch (kind) + { + case ConversionKind.AnonymousFunction: + case ConversionKind.MethodGroup: + case ConversionKind.ImplicitEnumeration: + case ConversionKind.ImplicitDynamic: + case ConversionKind.ImplicitNullToPointer: + case ConversionKind.ImplicitTupleLiteral: + case ConversionKind.StackAllocToPointerType: + case ConversionKind.StackAllocToSpanType: + return true; + default: + return false; + } + } + + // See https://github.com/dotnet/csharplang/blob/main/spec/conversions.md#standard-conversions: + // "The standard conversions are those pre-defined conversions that can occur as part of a user-defined conversion." + private static bool IsStandardImplicitConversionFromType(ConversionKind kind) + { + switch (kind) + { + case ConversionKind.Identity: + case ConversionKind.ImplicitNumeric: + case ConversionKind.ImplicitNullable: + case ConversionKind.ImplicitReference: + case ConversionKind.Boxing: + case ConversionKind.ImplicitConstant: + case ConversionKind.ImplicitPointer: + case ConversionKind.ImplicitPointerToVoid: + case ConversionKind.ImplicitTuple: + return true; + default: + return false; + } + } + private Conversion ClassifyStandardImplicitConversion(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert(sourceExpression != null || (object)source != null); - Debug.Assert(sourceExpression == null || (object)sourceExpression.GetTypeOrFunctionType() == (object)source); + Debug.Assert(sourceExpression == null || (object)sourceExpression.Type == (object)source); Debug.Assert((object)destination != null); // SPEC: The following implicit conversions are classified as standard implicit conversions: @@ -550,6 +633,7 @@ private Conversion ClassifyStandardImplicitConversion(BoundExpression sourceExpr Conversion conversion = ClassifyImplicitBuiltInConversionFromExpression(sourceExpression, source, destination, ref useSiteInfo); if (conversion.Exists) { + Debug.Assert(IsStandardImplicitConversionFromExpression(conversion.Kind)); return conversion; } @@ -562,9 +646,17 @@ private Conversion ClassifyStandardImplicitConversion(BoundExpression sourceExpr } private Conversion ClassifyStandardImplicitConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) + { + var conversion = ClassifyStandardImplicitConversionInternal(source, destination, ref useSiteInfo); + Debug.Assert(conversion.Kind == ConversionKind.NoConversion || IsStandardImplicitConversionFromType(conversion.Kind)); + return conversion; + } + + private Conversion ClassifyStandardImplicitConversionInternal(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert((object)source != null); Debug.Assert((object)destination != null); + Debug.Assert(source is not FunctionTypeSymbol); if (HasIdentityConversionInternal(source, destination)) { @@ -582,13 +674,6 @@ private Conversion ClassifyStandardImplicitConversion(TypeSymbol source, TypeSym return nullableConversion; } - if (source is FunctionTypeSymbol functionType) - { - return HasImplicitFunctionTypeConversion(functionType, destination, ref useSiteInfo) ? - Conversion.FunctionType : - Conversion.NoConversion; - } - if (HasImplicitReferenceConversion(source, destination, ref useSiteInfo)) { return Conversion.ImplicitReference; @@ -867,7 +952,7 @@ private static bool ExplicitConversionMayDifferFromImplicit(Conversion implicitC private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpression sourceExpression, TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert(sourceExpression != null || (object)source != null); - Debug.Assert(sourceExpression == null || (object)sourceExpression.GetTypeOrFunctionType() == (object)source); + Debug.Assert(sourceExpression == null || (object)sourceExpression.Type == (object)source); Debug.Assert((object)destination != null); if (HasImplicitDynamicConversionFromExpression(source, destination)) @@ -1188,6 +1273,7 @@ internal static bool HasImplicitConstantExpressionConversion(BoundExpression sou return false; } +#nullable enable private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression sourceExpression, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo, bool forCast) { Debug.Assert(sourceExpression != null); @@ -1205,8 +1291,8 @@ private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression } } - var sourceType = sourceExpression.GetTypeOrFunctionType(); - if ((object)sourceType != null) + var sourceType = sourceExpression.Type; + if (sourceType is { }) { // Try using the short-circuit "fast-conversion" path. Conversion fastConversion = FastClassifyConversion(sourceType, destination); @@ -1223,11 +1309,17 @@ private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression } } } + else if (sourceExpression.GetFunctionType() is { }) + { + if (IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo)) + { + return Conversion.FunctionType; + } + } return GetExplicitUserDefinedConversion(sourceExpression, sourceType, destination, ref useSiteInfo); } -#nullable enable private static bool HasImplicitEnumerationConversion(BoundExpression source, TypeSymbol destination) { Debug.Assert((object)source != null); @@ -2572,16 +2664,6 @@ private bool HasImplicitConversionFromDelegate(TypeSymbol source, TypeSymbol des return false; } - private bool HasImplicitFunctionTypeConversion(FunctionTypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) - { - if (destination is FunctionTypeSymbol destinationFunctionType) - { - return HasImplicitSignatureConversion(source, destinationFunctionType, ref useSiteInfo); - } - - return IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo); - } - internal bool IsValidFunctionTypeConversionTarget(TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { if (destination.SpecialType == SpecialType.System_MulticastDelegate) @@ -2604,7 +2686,7 @@ internal bool IsValidFunctionTypeConversionTarget(TypeSymbol destination, ref Co return false; } - private bool HasImplicitSignatureConversion(FunctionTypeSymbol sourceType, FunctionTypeSymbol destinationType, ref CompoundUseSiteInfo useSiteInfo) + private bool HasImplicitFunctionTypeConversion(FunctionTypeSymbol sourceType, FunctionTypeSymbol destinationType, ref CompoundUseSiteInfo useSiteInfo) { var sourceDelegate = sourceType.GetInternalDelegateType(); var destinationDelegate = destinationType.GetInternalDelegateType(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index 346c42d85c673..03b3cffe5c1b0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -606,6 +606,7 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) // Not "standard". case ConversionKind.ImplicitUserDefined: case ConversionKind.ExplicitUserDefined: + case ConversionKind.FunctionType: // Not implicit. case ConversionKind.ExplicitNumeric: @@ -642,9 +643,6 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) // Added for C# 7.1 case ConversionKind.DefaultLiteral: - - // Added for C# 10. - case ConversionKind.FunctionType: return true; default: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 13bf3163b29de..79abcec3c5fbd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -2793,7 +2793,7 @@ private static bool ImplicitConversionExists(TypeWithAnnotations sourceWithAnnot return false; } - return conversions.ClassifyImplicitConversionFromType(source, destination, ref useSiteInfo).Exists; + return conversions.ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(source, destination, ref useSiteInfo).Exists; } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 00c41b9db84b3..b8b2771e86477 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -258,8 +258,8 @@ bool populateDisposableConversionOrDisposeMethod(bool fromExpression, out Conver Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref CompoundUseSiteInfo useSiteInfo) { return fromExpression ? - originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref useSiteInfo) : - originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref useSiteInfo); + originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt!, targetInterface, ref useSiteInfo) : + originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt!, targetInterface, ref useSiteInfo); } TypeSymbol getDisposableInterface(bool isAsync) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 5c6333648ff28..008fac3713e87 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6327,10 +6327,10 @@ private static Conversion GenerateConversion(Conversions conversions, BoundExpre return useExpression ? (fromExplicitCast ? conversions.ClassifyConversionFromExpression(sourceExpression, destinationType, ref discardedUseSiteInfo, forCast: true) : - conversions.ClassifyImplicitConversionFromExpression(sourceExpression, destinationType, ref discardedUseSiteInfo)) : + conversions.ClassifyImplicitConversionFromExpression(sourceExpression!, destinationType, ref discardedUseSiteInfo)) : (fromExplicitCast ? conversions.ClassifyConversionFromType(sourceType, destinationType, ref discardedUseSiteInfo, forCast: true) : - conversions.ClassifyImplicitConversionFromType(sourceType, destinationType, ref discardedUseSiteInfo)); + conversions.ClassifyImplicitConversionFromType(sourceType!, destinationType, ref discardedUseSiteInfo)); } /// diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index bdf2862542151..11bd12380934e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -6427,18 +6427,17 @@ public void SynthesizedDelegateTypes_21() delegate void D4(out object x, ref object y); class Program { + static void M(Delegate d) { Report(d); } + static void M(D2 d) { Report(d); } + static void M(D4 d) { Report(d); } static void F1(ref object x, object y) { } static void F2(object x, ref object y) { } static void Main() { - var d1 = F1; - D2 d2 = F2; - var d3 = (ref object x, out object y) => { y = null; }; - D4 d4 = (out object x, ref object y) => { x = null; }; - Report(d1); - Report(d2); - Report(d3); - Report(d4); + M(F1); + M(F2); + M((ref object x, out object y) => { y = null; }); + M((out object x, ref object y) => { x = null; }); } static void Report(Delegate d) => Console.WriteLine(d.GetType()); }"; @@ -6505,14 +6504,18 @@ public class Field } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (7,20): error CS1660: Cannot convert lambda expression to type 'Program.Field' because it is not a delegate type // SomeMethod((Employee e) => e.Name); - Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(Employee e) => e.Name").WithArguments("lambda expression", "Program.Field").WithLocation(7, 20)); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "(Employee e) => e.Name").WithArguments("lambda expression", "Program.Field").WithLocation(7, 20) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(source); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -6543,7 +6546,6 @@ static void Main() } }"; - var expectedDiagnostics = new[] { // (16,17): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type @@ -6593,8 +6595,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (4,37): error CS0553: 'C1.implicit operator C1(object)': user-defined conversions to or from a base type are not allowed // public static implicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.implicit operator C1(object)").WithLocation(4, 37), @@ -6624,16 +6626,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(21, 13), // (22,13): error CS0030: Cannot convert type 'method' to 'C2' // _ = (C2)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(22, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(22, 13) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(source); - comp.VerifyDiagnostics( - // (4,37): error CS0553: 'C1.implicit operator C1(object)': user-defined conversions to or from a base type are not allowed - // public static implicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } - Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.implicit operator C1(object)").WithLocation(4, 37), - // (8,37): error CS0552: 'C2.implicit operator C2(ICloneable)': user-defined conversions to or from an interface are not allowed - // public static implicit operator C2(ICloneable c) { Console.WriteLine("operator C2(ICloneable c)"); return new C2(); } - Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C2").WithArguments("C2.implicit operator C2(System.ICloneable)").WithLocation(8, 37)); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -6678,8 +6678,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (24,17): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type // C1 c1 = () => 1; Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(24, 17), @@ -6715,22 +6715,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(34, 13), // (35,13): error CS0030: Cannot convert type 'method' to 'C2' // _ = (C2)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(35, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(35, 13) + }; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"operator C1(Delegate d) -operator C2(MulticastDelegate d) -operator C3(Expression e) -operator C4(LambdaExpression e) -operator C1(Delegate d) -operator C2(MulticastDelegate d) -operator C1(Delegate d) -operator C2(MulticastDelegate d) -operator C3(Expression e) -operator C4(LambdaExpression e) -operator C1(Delegate d) -operator C2(MulticastDelegate d) -"); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -6758,8 +6750,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (11,24): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type // C c1 = () => 1; Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(11, 24), @@ -6783,18 +6775,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(17, 13), // (18,13): error CS0030: Cannot convert type 'method' to 'C' // _ = (C)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(18, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(18, 13) + }; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"operator C(System.Object t) -operator C(System.ICloneable t) -operator C(System.Object t) -operator C(System.ICloneable t) -operator C(System.Object t) -operator C(System.ICloneable t) -operator C(System.Object t) -operator C(System.ICloneable t) -"); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -6827,8 +6815,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (12,26): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type // C c1 = () => 1; Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(12, 26), @@ -6864,22 +6852,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(22, 13), // (23,13): error CS0030: Cannot convert type 'method' to 'C' // _ = (C)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(23, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(23, 13) + }; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"operator C(System.Delegate t) -operator C(System.MulticastDelegate t) -operator C(System.Linq.Expressions.Expression t) -operator C(System.Linq.Expressions.LambdaExpression t) -operator C(System.Delegate t) -operator C(System.MulticastDelegate t) -operator C(System.Delegate t) -operator C(System.MulticastDelegate t) -operator C(System.Linq.Expressions.Expression t) -operator C(System.Linq.Expressions.LambdaExpression t) -operator C(System.Delegate t) -operator C(System.MulticastDelegate t) -"); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -6941,8 +6921,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (4,37): error CS0553: 'C1.explicit operator C1(object)': user-defined conversions to or from a base type are not allowed // public static explicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.explicit operator C1(object)").WithLocation(4, 37), @@ -6960,16 +6940,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(17, 13), // (18,13): error CS0030: Cannot convert type 'method' to 'C2' // _ = (C2)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(18, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(18, 13) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(source); - comp.VerifyDiagnostics( - // (4,37): error CS0553: 'C1.explicit operator C1(object)': user-defined conversions to or from a base type are not allowed - // public static explicit operator C1(object o) { Console.WriteLine("operator C1(object o)"); return new C1(); } - Diagnostic(ErrorCode.ERR_ConversionWithBase, "C1").WithArguments("C1.explicit operator C1(object)").WithLocation(4, 37), - // (8,37): error CS0552: 'C2.explicit operator C2(ICloneable)': user-defined conversions to or from an interface are not allowed - // public static explicit operator C2(ICloneable c) { Console.WriteLine("operator C2(ICloneable c)"); return new C2(); } - Diagnostic(ErrorCode.ERR_ConversionWithInterface, "C2").WithArguments("C2.explicit operator C2(System.ICloneable)").WithLocation(8, 37)); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -7008,8 +6986,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (24,18): error CS1660: Cannot convert lambda expression to type 'C1' because it is not a delegate type // _ = (C1)(() => 1); Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C1").WithLocation(24, 18), @@ -7027,16 +7005,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C1)F").WithArguments("method", "C1").WithLocation(28, 13), // (29,13): error CS0030: Cannot convert type 'method' to 'C2' // _ = (C2)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(29, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C2)F").WithArguments("method", "C2").WithLocation(29, 13) + }; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"operator C1(Delegate d) -operator C2(MulticastDelegate d) -operator C3(Expression e) -operator C4(LambdaExpression e) -operator C1(Delegate d) -operator C2(MulticastDelegate d) -"); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -7060,8 +7036,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (11,25): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type // _ = (C)(() => 1); Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(11, 25), @@ -7073,14 +7049,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(13, 13), // (14,13): error CS0030: Cannot convert type 'method' to 'C' // _ = (C)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(14, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(14, 13) + }; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"operator C(System.Object t) -operator C(System.ICloneable t) -operator C(System.Object t) -operator C(System.ICloneable t) -"); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -7107,8 +7083,8 @@ static void Main() } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( + var expectedDiagnostics = new[] + { // (12,27): error CS1660: Cannot convert lambda expression to type 'C' because it is not a delegate type // _ = (C)(() => 1); Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "C").WithLocation(12, 27), @@ -7126,16 +7102,14 @@ static void Main() Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(16, 13), // (17,13): error CS0030: Cannot convert type 'method' to 'C' // _ = (C)F; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(17, 13)); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(C)F").WithArguments("method", "C").WithLocation(17, 13) + }; - CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"operator C(System.Delegate t) -operator C(System.MulticastDelegate t) -operator C(System.Linq.Expressions.Expression t) -operator C(System.Linq.Expressions.LambdaExpression t) -operator C(System.Delegate t) -operator C(System.MulticastDelegate t) -"); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] From 0b5884c953b54281221aad5063eae121e59be319 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Sat, 18 Sep 2021 23:00:16 -0700 Subject: [PATCH 3/6] Extract local --- .../Semantics/OverloadResolution/MethodTypeInference.cs | 8 ++++---- .../CSharp/Portable/FlowAnalysis/NullableWalker.cs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 79abcec3c5fbd..22a3404b90874 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -606,15 +606,15 @@ private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeW !MakeExplicitParameterTypeInferences((BoundTupleLiteral)argument, target, kind, ref useSiteInfo)) { // Either the argument is not a tuple literal, or we were unable to do the inference from its elements, let's try to infer from argument type - if (IsReallyAType(argument.GetTypeOrFunctionType())) + var argumentType = _extensions.GetTypeWithAnnotations(argument); + if (IsReallyAType(argumentType.Type)) { - ExactOrBoundsInference(kind, _extensions.GetTypeWithAnnotations(argument), target, ref useSiteInfo); + ExactOrBoundsInference(kind, argumentType, target, ref useSiteInfo); } else if (IsUnfixedTypeParameter(target) && kind is ExactOrBoundsKind.LowerBound) { var ordinal = ((TypeParameterSymbol)target.Type).Ordinal; - var typeWithAnnotations = _extensions.GetTypeWithAnnotations(argument); - _nullableAnnotationLowerBounds[ordinal] = _nullableAnnotationLowerBounds[ordinal].Join(typeWithAnnotations.NullableAnnotation); + _nullableAnnotationLowerBounds[ordinal] = _nullableAnnotationLowerBounds[ordinal].Join(argumentType.NullableAnnotation); } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 008fac3713e87..f394b0dd863f1 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6156,6 +6156,7 @@ private static NullableAnnotation GetNullableAnnotation(BoundExpression expr) case BoundKind.MethodGroup: case BoundKind.UnboundLambda: case BoundKind.UnconvertedObjectCreationExpression: + case BoundKind.ConvertedTupleLiteral: return NullableAnnotation.NotAnnotated; default: Debug.Assert(false); // unexpected value From 64c63cfa62629f0230cd67faabced1f1672617f9 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 22 Sep 2021 14:20:35 -0700 Subject: [PATCH 4/6] PR feedback --- .../Semantics/Conversions/ConversionsBase.cs | 89 ++++++++++--------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 0f4ecccc46332..c80f8ea7a0fe6 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -647,60 +647,65 @@ private Conversion ClassifyStandardImplicitConversion(BoundExpression sourceExpr private Conversion ClassifyStandardImplicitConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { - var conversion = ClassifyStandardImplicitConversionInternal(source, destination, ref useSiteInfo); + var conversion = classifyConversion(source, destination, ref useSiteInfo); Debug.Assert(conversion.Kind == ConversionKind.NoConversion || IsStandardImplicitConversionFromType(conversion.Kind)); return conversion; - } - - private Conversion ClassifyStandardImplicitConversionInternal(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) - { - Debug.Assert((object)source != null); - Debug.Assert((object)destination != null); - Debug.Assert(source is not FunctionTypeSymbol); - if (HasIdentityConversionInternal(source, destination)) + Conversion classifyConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { - return Conversion.Identity; - } + Debug.Assert((object)source != null); + Debug.Assert((object)destination != null); - if (HasImplicitNumericConversion(source, destination)) - { - return Conversion.ImplicitNumeric; - } + if (HasIdentityConversionInternal(source, destination)) + { + return Conversion.Identity; + } - var nullableConversion = ClassifyImplicitNullableConversion(source, destination, ref useSiteInfo); - if (nullableConversion.Exists) - { - return nullableConversion; - } + if (HasImplicitNumericConversion(source, destination)) + { + return Conversion.ImplicitNumeric; + } - if (HasImplicitReferenceConversion(source, destination, ref useSiteInfo)) - { - return Conversion.ImplicitReference; - } + var nullableConversion = ClassifyImplicitNullableConversion(source, destination, ref useSiteInfo); + if (nullableConversion.Exists) + { + return nullableConversion; + } - if (HasBoxingConversion(source, destination, ref useSiteInfo)) - { - return Conversion.Boxing; - } + if (source is FunctionTypeSymbol) + { + Debug.Assert(false); + return Conversion.NoConversion; + } - if (HasImplicitPointerToVoidConversion(source, destination)) - { - return Conversion.PointerToVoid; - } + if (HasImplicitReferenceConversion(source, destination, ref useSiteInfo)) + { + return Conversion.ImplicitReference; + } - if (HasImplicitPointerConversion(source, destination, ref useSiteInfo)) - { - return Conversion.ImplicitPointer; - } + if (HasBoxingConversion(source, destination, ref useSiteInfo)) + { + return Conversion.Boxing; + } - var tupleConversion = ClassifyImplicitTupleConversion(source, destination, ref useSiteInfo); - if (tupleConversion.Exists) - { - return tupleConversion; - } + if (HasImplicitPointerToVoidConversion(source, destination)) + { + return Conversion.PointerToVoid; + } - return Conversion.NoConversion; + if (HasImplicitPointerConversion(source, destination, ref useSiteInfo)) + { + return Conversion.ImplicitPointer; + } + + var tupleConversion = ClassifyImplicitTupleConversion(source, destination, ref useSiteInfo); + if (tupleConversion.Exists) + { + return tupleConversion; + } + + return Conversion.NoConversion; + } } private Conversion ClassifyImplicitBuiltInConversionSlow(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) From a37e5f9a25b57531bc5279927f1c671c0230375f Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 22 Sep 2021 15:03:19 -0700 Subject: [PATCH 5/6] PR feedback --- .../Binder/Semantics/BestTypeInferrer.cs | 4 +- .../Semantics/Conversions/ConversionsBase.cs | 26 ++++++--- .../OverloadResolution/MethodTypeInference.cs | 2 +- .../Portable/Binder/UsingStatementBinder.cs | 14 ++++- .../Semantic/Semantics/DelegateTypeTests.cs | 57 +++++++++++++++++++ 5 files changed, 89 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs index 4f214f5cc4d3c..412fe53b7a8fd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs @@ -256,8 +256,8 @@ public static NullableFlowState GetNullableState(ArrayBuilder typ } var conversionsWithoutNullability = conversions.WithNullability(false); - var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(type1, type2, ref useSiteInfo).Exists; - var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(type2, type1, ref useSiteInfo).Exists; + var t1tot2 = conversionsWithoutNullability.ClassifyImplicitConversionFromTypeWhenNeitherOrBothFunctionTypes(type1, type2, ref useSiteInfo).Exists; + var t2tot1 = conversionsWithoutNullability.ClassifyImplicitConversionFromTypeWhenNeitherOrBothFunctionTypes(type2, type1, ref useSiteInfo).Exists; if (t1tot2 && t2tot1) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index c80f8ea7a0fe6..3baeb90ca170b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -119,9 +119,9 @@ public Conversion ClassifyImplicitConversionFromExpression(BoundExpression sourc } } } - else if (sourceExpression.GetFunctionType() is { }) + else if (sourceExpression.GetFunctionType() is { } sourceFunctionType) { - if (IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo)) + if (HasImplicitFunctionTypeConversion(sourceFunctionType, destination, ref useSiteInfo)) { return Conversion.FunctionType; } @@ -181,11 +181,11 @@ public Conversion ClassifyImplicitConversionFromType(TypeSymbol source, TypeSymb /// /// Helper method that calls or - /// depending on whether the + /// depending on whether the /// types are instances. /// Used by method type inference and best common type only. /// - public Conversion ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) + public Conversion ClassifyImplicitConversionFromTypeWhenNeitherOrBothFunctionTypes(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { var sourceFunctionType = source as FunctionTypeSymbol; var destinationFunctionType = destination as FunctionTypeSymbol; @@ -197,7 +197,7 @@ public Conversion ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConver if (sourceFunctionType is { } && destinationFunctionType is { }) { - return HasImplicitFunctionTypeConversion(sourceFunctionType, destinationFunctionType, ref useSiteInfo) ? + return HasImplicitFunctionTypeToFunctionTypeConversion(sourceFunctionType, destinationFunctionType, ref useSiteInfo) ? Conversion.FunctionType : Conversion.NoConversion; } @@ -1314,9 +1314,9 @@ private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression } } } - else if (sourceExpression.GetFunctionType() is { }) + else if (sourceExpression.GetFunctionType() is { } sourceFunctionType) { - if (IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo)) + if (HasImplicitFunctionTypeConversion(sourceFunctionType, destination, ref useSiteInfo)) { return Conversion.FunctionType; } @@ -2669,6 +2669,16 @@ private bool HasImplicitConversionFromDelegate(TypeSymbol source, TypeSymbol des return false; } + private bool HasImplicitFunctionTypeConversion(FunctionTypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) + { + if (destination is FunctionTypeSymbol destinationFunctionType) + { + return HasImplicitFunctionTypeToFunctionTypeConversion(source, destinationFunctionType, ref useSiteInfo); + } + + return IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo); + } + internal bool IsValidFunctionTypeConversionTarget(TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { if (destination.SpecialType == SpecialType.System_MulticastDelegate) @@ -2691,7 +2701,7 @@ internal bool IsValidFunctionTypeConversionTarget(TypeSymbol destination, ref Co return false; } - private bool HasImplicitFunctionTypeConversion(FunctionTypeSymbol sourceType, FunctionTypeSymbol destinationType, ref CompoundUseSiteInfo useSiteInfo) + private bool HasImplicitFunctionTypeToFunctionTypeConversion(FunctionTypeSymbol sourceType, FunctionTypeSymbol destinationType, ref CompoundUseSiteInfo useSiteInfo) { var sourceDelegate = sourceType.GetInternalDelegateType(); var destinationDelegate = destinationType.GetInternalDelegateType(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 22a3404b90874..71765b500ac50 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -2793,7 +2793,7 @@ private static bool ImplicitConversionExists(TypeWithAnnotations sourceWithAnnot return false; } - return conversions.ClassifyImplicitConversionFromTypeOrImplicitFunctionTypeConversion(source, destination, ref useSiteInfo).Exists; + return conversions.ClassifyImplicitConversionFromTypeWhenNeitherOrBothFunctionTypes(source, destination, ref useSiteInfo).Exists; } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index b8b2771e86477..48d24a282d272 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -257,9 +257,17 @@ bool populateDisposableConversionOrDisposeMethod(bool fromExpression, out Conver Conversion classifyConversion(bool fromExpression, TypeSymbol targetInterface, ref CompoundUseSiteInfo useSiteInfo) { - return fromExpression ? - originalBinder.Conversions.ClassifyImplicitConversionFromExpression(expressionOpt!, targetInterface, ref useSiteInfo) : - originalBinder.Conversions.ClassifyImplicitConversionFromType(declarationTypeOpt!, targetInterface, ref useSiteInfo); + var conversions = originalBinder.Conversions; + if (fromExpression) + { + Debug.Assert(expressionOpt is { }); + return conversions.ClassifyImplicitConversionFromExpression(expressionOpt, targetInterface, ref useSiteInfo); + } + else + { + Debug.Assert(declarationTypeOpt is { }); + return conversions.ClassifyImplicitConversionFromType(declarationTypeOpt, targetInterface, ref useSiteInfo); + } } TypeSymbol getDisposableInterface(bool isAsync) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 11bd12380934e..4fc61eb43555c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -6477,6 +6477,63 @@ private static void VerifyExpressionType(SemanticModel model, ExpressionSyntax v Assert.Equal(expectedType, type.ToTestDisplayString()); } + [Fact] + public void ClassifyConversionFromExpressionToFunctionType() + { + var source = +@"class Program +{ + static void Main() + { + object o = () => 1; + } +}"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var funcOfT = comp.GetWellKnownType(WellKnownType.System_Func_T); + var tree = comp.SyntaxTrees[0]; + var expr = tree.GetRoot().DescendantNodes().OfType().Single(); + var model = comp.GetSemanticModel(tree); + model = ((CSharpSemanticModel)model).GetMemberModel(expr); + + verifyConversions(model, expr, comp.GetSpecialType(SpecialType.System_MulticastDelegate).GetPublicSymbol(), ConversionKind.FunctionType, ConversionKind.FunctionType); + verifyConversions(model, expr, comp.GetWellKnownType(WellKnownType.System_Linq_Expressions_Expression).GetPublicSymbol(), ConversionKind.FunctionType, ConversionKind.FunctionType); + verifyConversions(model, expr, getFunctionType(funcOfT.Construct(comp.GetSpecialType(SpecialType.System_Int32))), ConversionKind.FunctionType, ConversionKind.FunctionType); + verifyConversions(model, expr, getFunctionType(funcOfT.Construct(comp.GetSpecialType(SpecialType.System_Object))), ConversionKind.NoConversion, ConversionKind.NoConversion); + + static ITypeSymbol getFunctionType(NamedTypeSymbol delegateType) + { + return new FunctionTypeSymbol_PublicModel(new FunctionTypeSymbol(delegateType)); + } + + static void verifyConversions(SemanticModel model, ExpressionSyntax expr, ITypeSymbol destination, ConversionKind expectedImplicitKind, ConversionKind expectedExplicitKind) + { + Assert.Equal(expectedImplicitKind, model.ClassifyConversion(expr, destination, isExplicitInSource: false).Kind); + Assert.Equal(expectedExplicitKind, model.ClassifyConversion(expr, destination, isExplicitInSource: true).Kind); + } + } + + private sealed class FunctionTypeSymbol_PublicModel : Symbols.PublicModel.TypeSymbol + { + private readonly FunctionTypeSymbol _underlying; + + internal FunctionTypeSymbol_PublicModel(FunctionTypeSymbol underlying) : + base(nullableAnnotation: default) + { + _underlying = underlying; + } + + internal override TypeSymbol UnderlyingTypeSymbol => _underlying; + internal override NamespaceOrTypeSymbol UnderlyingNamespaceOrTypeSymbol => _underlying; + internal override Symbol UnderlyingSymbol => _underlying; + + protected override void Accept(SymbolVisitor visitor) => throw new NotImplementedException(); + protected override TResult Accept(SymbolVisitor visitor) => throw new NotImplementedException(); + protected override ITypeSymbol WithNullableAnnotation(CodeAnalysis.NullableAnnotation nullableAnnotation) => this; + } + [WorkItem(56407, "https://github.com/dotnet/roslyn/issues/56407")] [Fact] public void UserDefinedConversions_01() From 7b745c13649121374498ab8b34b195b1fdca0983 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 22 Sep 2021 16:37:41 -0700 Subject: [PATCH 6/6] Remove implicit conversion case --- .../Binder/Semantics/Conversions/ConversionsBase.cs | 7 ------- .../CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 3baeb90ca170b..8ece6a379b6f7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -1314,13 +1314,6 @@ private Conversion ClassifyExplicitOnlyConversionFromExpression(BoundExpression } } } - else if (sourceExpression.GetFunctionType() is { } sourceFunctionType) - { - if (HasImplicitFunctionTypeConversion(sourceFunctionType, destination, ref useSiteInfo)) - { - return Conversion.FunctionType; - } - } return GetExplicitUserDefinedConversion(sourceExpression, sourceType, destination, ref useSiteInfo); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 4fc61eb43555c..57afe51034f20 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -6478,7 +6478,7 @@ private static void VerifyExpressionType(SemanticModel model, ExpressionSyntax v } [Fact] - public void ClassifyConversionFromExpressionToFunctionType() + public void ClassifyConversionFromExpression() { var source = @"class Program