From d3f0043f65dd569d53ab8d9fd104699a8c31d773 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 20 Jun 2023 15:11:41 -0700 Subject: [PATCH 1/9] Collection literals: type inference --- .../Portable/Binder/ForEachLoopBinder.cs | 2 +- .../OverloadResolution/MethodTypeInference.cs | 73 +- .../Portable/FlowAnalysis/NullableWalker.cs | 4 +- .../Semantics/CollectionLiteralTests.cs | 799 +++++++++++++++++- 4 files changed, 828 insertions(+), 50 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index e2a33de5a1bd3..605d515b12955 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -651,7 +651,7 @@ protected BoundExpression ConvertForEachCollection( return convertedCollectionExpression; } - protected bool GetEnumeratorInfoAndInferCollectionElementType( + internal bool GetEnumeratorInfoAndInferCollectionElementType( SyntaxNode syntax, ExpressionSyntax collectionSyntax, ref ForEachEnumeratorInfo.Builder builder, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index c324ed18eaf21..baa0a98cc5d3f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -11,6 +11,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -558,7 +559,7 @@ private MethodTypeInferenceResult InferTypeArgs(Binder binder, ref CompoundUseSi // SPEC: phase. The first phase makes some initial inferences of bounds, whereas // SPEC: the second phase fixes type parameters to specific types and infers further // SPEC: bounds. The second phase may have to be repeated a number of times. - InferTypeArgsFirstPhase(ref useSiteInfo); + InferTypeArgsFirstPhase(binder, ref useSiteInfo); bool success = InferTypeArgsSecondPhase(binder, ref useSiteInfo); var inferredTypeArguments = GetResults(out bool inferredFromFunctionType); return new MethodTypeInferenceResult(success, inferredTypeArguments, inferredFromFunctionType); @@ -569,7 +570,7 @@ private MethodTypeInferenceResult InferTypeArgs(Binder binder, ref CompoundUseSi // The first phase // - private void InferTypeArgsFirstPhase(ref CompoundUseSiteInfo useSiteInfo) + private void InferTypeArgsFirstPhase(Binder binder, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert(!_formalParameterTypes.IsDefault); Debug.Assert(!_arguments.IsDefault); @@ -585,11 +586,12 @@ private void InferTypeArgsFirstPhase(ref CompoundUseSiteInfo use TypeWithAnnotations target = _formalParameterTypes[arg]; ExactOrBoundsKind kind = GetRefKind(arg).IsManagedReference() || target.Type.IsPointerType() ? ExactOrBoundsKind.Exact : ExactOrBoundsKind.LowerBound; - MakeExplicitParameterTypeInferences(argument, target, kind, ref useSiteInfo); + MakeExplicitParameterTypeInferences(binder, argument, target, kind, ref useSiteInfo); } } - private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo useSiteInfo) +#nullable enable + private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo useSiteInfo) { // SPEC: * If Ei is an anonymous function, and Ti is a delegate type or expression tree type, // SPEC: an explicit type parameter inference is made from Ei to Ti and @@ -609,8 +611,12 @@ private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeW ExplicitParameterTypeInference(argument, target, ref useSiteInfo); ExplicitReturnTypeInference(argument, target, ref useSiteInfo); } + else if (argument.Kind == BoundKind.UnconvertedCollectionLiteralExpression) + { + MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)argument, target, ref useSiteInfo); + } else if (argument.Kind != BoundKind.TupleLiteral || - !MakeExplicitParameterTypeInferences((BoundTupleLiteral)argument, target, kind, ref useSiteInfo)) + !MakeExplicitParameterTypeInferences(binder, (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 var argumentType = _extensions.GetTypeWithAnnotations(argument); @@ -626,7 +632,60 @@ private void MakeExplicitParameterTypeInferences(BoundExpression argument, TypeW } } - private bool MakeExplicitParameterTypeInferences(BoundTupleLiteral argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo useSiteInfo) + private void MakeCollectionLiteralTypeInferences( + Binder binder, + BoundUnconvertedCollectionLiteralExpression argument, + TypeWithAnnotations target, + ref CompoundUseSiteInfo useSiteInfo) + { + if (target.Type is null) + { + return; + } + + if (argument.Elements.Length == 0) + { + return; + } + + if (!tryGetCollectionIterationType(binder, (ExpressionSyntax)argument.Syntax, target.Type, out TypeWithAnnotations targetElementType)) + { + return; + } + + foreach (var element in argument.Elements) + { + if (element.Kind == BoundKind.UnconvertedCollectionLiteralExpression) + { + MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)element, targetElementType, ref useSiteInfo); + } + else + { + var elementType = _extensions.GetTypeWithAnnotations(element); + if (IsReallyAType(elementType.Type)) + { + LowerBoundInference(elementType, targetElementType, ref useSiteInfo); + } + } + } + + static bool tryGetCollectionIterationType(Binder binder, ExpressionSyntax syntax, TypeSymbol collectionType, out TypeWithAnnotations iterationType) + { + var builder = new ForEachEnumeratorInfo.Builder(); + BoundExpression collectionExpr = new BoundValuePlaceholder(syntax, collectionType); + return binder.GetEnumeratorInfoAndInferCollectionElementType( + syntax, + syntax, + ref builder, + ref collectionExpr, + isAsync: false, + BindingDiagnosticBag.Discarded, + out iterationType); + } + } +#nullable disable + + private bool MakeExplicitParameterTypeInferences(Binder binder, BoundTupleLiteral argument, TypeWithAnnotations target, ExactOrBoundsKind kind, ref CompoundUseSiteInfo useSiteInfo) { // try match up element-wise to the destination tuple (or underlying type) // Example: @@ -659,7 +718,7 @@ private bool MakeExplicitParameterTypeInferences(BoundTupleLiteral argument, Typ { var sourceArgument = sourceArguments[i]; var destType = destTypes[i]; - MakeExplicitParameterTypeInferences(sourceArgument, destType, kind, ref useSiteInfo); + MakeExplicitParameterTypeInferences(binder, sourceArgument, destType, kind, ref useSiteInfo); } return true; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 439307316b2da..b856751caf98d 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7228,9 +7228,11 @@ private static NullableAnnotation GetNullableAnnotation(BoundExpression expr) case BoundKind.UnboundLambda: case BoundKind.UnconvertedObjectCreationExpression: case BoundKind.ConvertedTupleLiteral: + case BoundKind.UnconvertedCollectionLiteralExpression: return NullableAnnotation.NotAnnotated; default: - Debug.Assert(false); // unexpected value + // PROTOTYPE: Re-enable + //Debug.Assert(false); // unexpected value return NullableAnnotation.Oblivious; } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 39c66e9a3ffac..21726beaeb47a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -777,17 +777,12 @@ static void Main() { var x = F1([1]); var y = F2([2]); + x.Report(includeType: true); + y.Report(includeType: true); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (10,17): error CS0411: The type arguments for method 'Program.F1(IEnumerable)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var x = F1([1]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.IEnumerable)").WithLocation(10, 17), - // (11,17): error CS0411: The type arguments for method 'Program.F2(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var y = F2([2]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[])").WithLocation(11, 17)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1], System.Int32[][2], "); } [Fact] @@ -855,13 +850,14 @@ class Program { static void Main() { - int[] a = new int[0]; - var b = new[] { a, [1, 2, 3] }; - b.Report(includeType: true); + var x = new[] { new int[0], [1, 2, 3] }; + x.Report(includeType: true); + var y = new[] { new[] { new int[0] }, [[1, 2, 3]] }; + y.Report(includeType: true); } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][][[], [1, 2, 3]], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][][[], [1, 2, 3]], System.Int32[][][][[[]], [[1, 2, 3]]], "); } [Fact] @@ -872,24 +868,96 @@ class Program { static void Main() { - byte[] a = new byte[0]; - var b = new[] { a, [1, 2, 3] }; - b.Report(includeType: true); + var x = new[] { new byte[0], [1, 2, 3] }; + x.Report(includeType: true); + var y = new[] { new[] { new byte[0] }, [[1, 2, 3]] }; + y.Report(includeType: true); } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Byte[][][[], [1, 2, 3]], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Byte[][][[], [1, 2, 3]], System.Byte[][][][[[]], [[1, 2, 3]]], "); } [Fact] - public void TypeInference_01() + public void BestCommonType_03() { string source = """ class Program { - static T F(T t) + static void Main(string[] args) { - return t; + var x = new[] { [ulong.MaxValue], [1, 2, 3] }; + var y = new[] { [[ulong.MaxValue]], [[1, 2, 3]] }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,17): error CS0826: No best type found for implicitly-typed array + // var x = new[] { [ulong.MaxValue], [1, 2, 3] }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { [ulong.MaxValue], [1, 2, 3] }").WithLocation(5, 17), + // (6,17): error CS0826: No best type found for implicitly-typed array + // var y = new[] { [[ulong.MaxValue]], [[1, 2, 3]] }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { [[ulong.MaxValue]], [[1, 2, 3]] }").WithLocation(6, 17)); + } + + [Fact] + public void BestCommonType_04() + { + string source = """ + class Program + { + static void Main(string[] args) + { + var x = new[] { [""], new object[0] }; + var y = new[] { [[""]], [new object[0]] }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,17): error CS0826: No best type found for implicitly-typed array + // var y = new[] { [[""]], [new object[0]] }; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, @"new[] { [[""""]], [new object[0]] }").WithLocation(6, 17)); + } + + // PROTOTYPE: Does BestTypeInferrer.InferBestTypeForConditionalOperator() + // need to updated to handle collection literals? + [Fact] + public void BestCommonType_05() + { + string source = """ + class Program + { + static void Main(string[] args) + { + bool b = args.Length > 0; + var x = b ? new int[0] : [1, 2, 3]; + x.Report(includeType: true); + var y = b ? [new int[0]] : [[1, 2, 3]]; + y.Report(includeType: true); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,11): error CS1061: 'int[]' does not contain a definition for 'Report' and no accessible extension method 'Report' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // x.Report(includeType: true); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Report").WithArguments("int[]", "Report").WithLocation(7, 11), + // (8,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection literals' and 'collection literals' + // var y = b ? [new int[0]] : [[1, 2, 3]]; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? [new int[0]] : [[1, 2, 3]]").WithArguments("collection literals", "collection literals").WithLocation(8, 17)); + } + + [Fact] + public void TypeInference_01() + { + string source = """ + static class Program + { + static T F(T a, T b) + { + return b; } static void Main() { @@ -900,16 +968,39 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (9,17): error CS0411: The type arguments for method 'Program.F(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // (9,17): error CS7036: There is no argument given that corresponds to the required parameter 'b' of 'Program.F(T, T)' // var x = F(["str"]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T)").WithLocation(9, 17), - // (10,17): error CS0411: The type arguments for method 'Program.F(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "F").WithArguments("b", "Program.F(T, T)").WithLocation(9, 17), + // (10,17): error CS7036: There is no argument given that corresponds to the required parameter 'b' of 'Program.F(T, T)' // var y = F([[], [1, 2]]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T)").WithLocation(10, 17)); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "F").WithArguments("b", "Program.F(T, T)").WithLocation(10, 17)); } [Fact] public void TypeInference_02() + { + string source = """ + static class Program + { + static T F(T a, T b) + { + return b; + } + static void Main() + { + _ = F([new int[0]], new[] { [1, 2, 3] }); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,29): error CS0826: No best type found for implicitly-typed array + // _ = F([new int[0]], new[] { [1, 2, 3] }); + Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { [1, 2, 3] }").WithLocation(9, 29)); + } + + [Fact] + public void TypeInference_03() { string source = """ class Program @@ -925,16 +1016,38 @@ static void Main() } } """; - // PROTOTYPE: Should compile and run successfully: expectedOutput: "[1, 2, 3], ") - var comp = CreateCompilation(new[] { source, s_collectionExtensions }); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void TypeInference_04() + { + string source = """ + class Program + { + static T[] AsArray(T[] args) + { + return args; + } + static void Main() + { + AsArray([]); + AsArray([1, null]); + } + } + """; + var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // 0.cs(9,17): error CS0411: The type arguments for method 'Program.AsArray(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var a = AsArray([1, 2, 3]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "AsArray").WithArguments("Program.AsArray(T[])").WithLocation(9, 17)); + // (9,9): error CS0411: The type arguments for method 'Program.AsArray(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // AsArray([]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "AsArray").WithArguments("Program.AsArray(T[])").WithLocation(9, 9), + // (10,21): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type + // AsArray([1, null]); + Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(10, 21)); } [Fact] - public void TypeInference_03() + public void TypeInference_05() { string source = """ class Program @@ -950,16 +1063,11 @@ static void Main() } } """; - // PROTOTYPE: Should compile and run successfully: expectedOutput: "[1, 2, 3], ") - var comp = CreateCompilation(new[] { source, s_collectionExtensions }); - comp.VerifyEmitDiagnostics( - // 0.cs(9,17): error CS0411: The type arguments for method 'Program.AsArray(params T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var a = AsArray([1, 2, 3]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "AsArray").WithArguments("Program.AsArray(params T[])").WithLocation(9, 17)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], "); } [Fact] - public void TypeInference_04() + public void TypeInference_06() { string source = """ class Program @@ -979,7 +1087,7 @@ static void Main() } } """; - // PROTOTYPE: Should compile and run successfully: expectedOutput: "[2], ") + // PROTOTYPE: Should compile and run successfully: expectedOutput: "[1, 2, 3], " var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 0.cs(9,29): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection literals' and 'collection literals' @@ -988,7 +1096,7 @@ static void Main() } [Fact] - public void TypeInference_05() + public void TypeInference_07() { string source = """ static class Program @@ -1004,14 +1112,623 @@ static void Main() } } """; - // PROTOTYPE: Should compile and run successfully: expectedOutput: "[1, 2, 3], " - var comp = CreateCompilation(new[] { source, s_collectionExtensions }); + var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // 0.cs(9,27): error CS0117: 'collection literals' does not contain a definition for 'AsArray' + // (9,27): error CS0117: 'collection literals' does not contain a definition for 'AsArray' // var a = [1, 2, 3].AsArray(); Diagnostic(ErrorCode.ERR_NoSuchMember, "AsArray").WithArguments("collection literals", "AsArray").WithLocation(9, 27)); } + [Fact] + public void TypeInference_08() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + struct S : IEnumerable + { + private List _list; + public void Add(T t) + { + _list ??= new List(); + _list.Add(t); + } + public IEnumerator GetEnumerator() + { + _list ??= new List(); + return _list.GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + static class Program + { + static S AsCollection(this S args) + { + return args; + } + static void Main() + { + var a = AsCollection([1, 2, 3]); + var b = [4].AsCollection(); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (27,21): error CS0117: 'collection literals' does not contain a definition for 'AsCollection' + // var b = [4].AsCollection(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "AsCollection").WithArguments("collection literals", "AsCollection").WithLocation(27, 21)); + } + + [Fact] + public void TypeInference_09() + { + string source = """ + using System.Collections; + struct S : IEnumerable + { + public void Add(T t) { } + IEnumerator IEnumerable.GetEnumerator() => null; + } + static class Program + { + static S AsCollection(this S args) + { + return args; + } + static void Main() + { + _ = AsCollection([1, 2, 3]); + _ = [4].AsCollection(); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,13): error CS0411: The type arguments for method 'Program.AsCollection(S)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // _ = AsCollection([1, 2, 3]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "AsCollection").WithArguments("Program.AsCollection(S)").WithLocation(15, 13), + // (16,17): error CS0117: 'collection literals' does not contain a definition for 'AsCollection' + // _ = [4].AsCollection(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "AsCollection").WithArguments("collection literals", "AsCollection").WithLocation(16, 17)); + } + + [Fact] + public void TypeInference_10() + { + string source = """ + using System.Collections.Generic; + class Program + { + static T[] F(T[] arg) => arg; + static List F(List arg) => arg; + static void Main() + { + _ = F([1, 2, 3]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,13): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(T[])' and 'Program.F(List)' + // _ = F([1, 2, 3]); + Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(T[])", "Program.F(System.Collections.Generic.List)").WithLocation(8, 13)); + } + + [Fact] + public void TypeInference_11() + { + string source = """ + using System.Collections; + struct S : IEnumerable + { + public void Add(T t) { } + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class Program + { + static T[] F(T[] arg) => arg; + static S F(S arg) => arg; + static void Main() + { + var x = F([1, 2, 3]); + x.Report(true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1, 2, 3], "); + } + + // PROTOTYPE: Test other variance cases. + [Fact] + public void TypeInference_12() + { + string source = """ + class Program + { + static T[] F(T[] x, T[] y) => x; + static void Main() + { + var x = F(["1"], [(object)"2"]); + x.Report(includeType: true); + var y = F([(object)"3"], ["4"]); + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Object[][1], System.Object[][3], "); + } + + // PROTOTYPE: Test other variance cases. And these are just for array inferences. + // What about constructed collection type inferences? + [Fact] + public void TypeInference_13() + { + string source = """ + class Program + { + static T[] F(T[] x, T[] y) => x; + static void Main() + { + var x = F([1], [(long)2]); + x.Report(includeType: true); + var y = F([(long)3], [4]); + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int64[][1], System.Int64[][3], "); + } + + [Fact] + public void TypeInference_14() + { + string source = """ + class Program + { + static T[] F(T[][] x) => x[0]; + static void Main() + { + var x = F([[1, 2, 3]]); + x.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1, 2, 3], "); + } + + [Fact] + public void TypeInference_15() + { + string source = """ + class Program + { + static T[] F1(T[] x, T[] y) => y; + static T[] F2(T[][] x, T[][] y) => y[0]; + static void Main() + { + var x = F1(new byte[0], [1, 2]); + var y = F2(new[] { new byte[0] }, [[3, 4]]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var x = F1(new byte[0], [1, 2]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(T[], T[])").WithLocation(7, 17), + // (8,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F2(new[] { new byte[0] }, [[3, 4]]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(8, 17)); + } + + [Fact] + public void TypeInference_16() + { + string source = """ + class Program + { + static T[] F1(T[] x, T[] y) => y; + static T[] F2(T[][] x, T[][] y) => y[0]; + static void Main() + { + var x = F1([1], [(byte)2]); + x.Report(true); + var y = F2([[3]], [[(byte)4]]); + y.Report(true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][2], System.Int32[][4], "); + } + + [Fact] + public void TypeInference_17() + { + string source = """ + class Program + { + static T[] F1(T[] x, T[] y) => y; + static T[] F2(T[][] x, T[][] y) => y[0]; + static void Main() + { + var x = F1([(long)1], [(int?)2]); + x.Report(true); + var y = F2([[(int?)3]], [[(long)4]]); + y.Report(true); + } + } + """; + var comp = CreateCompilation(new[] { source, s_collectionExtensions }); + comp.VerifyEmitDiagnostics( + // 0.cs(7,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var x = F1([(long)1], [(int?)2]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(T[], T[])").WithLocation(7, 17), + // 0.cs(9,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F2([[(int?)3]], [[(long)4]]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(9, 17)); + } + + [Fact] + public void TypeInference_18() + { + string source = """ + using System.Collections.Generic; + class Program + { + static List AsListOfArray(List arg) => arg; + static void Main() + { + var x = AsListOfArray([[4, 5], []]); + x.Report(true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Collections.Generic.List[[4, 5], []], "); + } + + [Fact] + public void TypeInference_19() + { + string source = """ + class Program + { + static T[] F(T[][] x) => x[1]; + static void Main() + { + var y = F([new byte[0], [1, 2, 3]]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,17): error CS0411: The type arguments for method 'Program.F(T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F([new byte[0], [1, 2, 3]]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T[][])").WithLocation(6, 17)); + } + + [Fact] + public void TypeInference_20() + { + string source = """ + class Program + { + static T[] F(in T[] x, T[] y) => x; + static void Main() + { + var x = F([1], [2]); + x.Report(true); + var y = F([3], [(object)4]); + y.Report(true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1], System.Object[][3], "); + } + + [Fact] + public void TypeInference_21() + { + string source = """ + class Program + { + static T[] F(in T[] x, T[] y) => x; + static void Main() + { + var y = F(in [3], [(object)4]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // var y = F(in [3], [(object)4]); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "[3]").WithLocation(6, 22)); + } + + [Fact] + public void TypeInference_22() + { + string source = """ + class Program + { + static T[] F1(T[] x, T[] y) => y; + static T[] F2(T[][] x, T[][] y) => y[0]; + static void Main() + { + var x = F1([], [default, 2]); + x.Report(true); + var y = F2([[null]], [[default, (int?)4]]); + y.Report(true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][0, 2], System.Nullable[][null, 4], "); + } + + [Fact] + public void TypeInference_23() + { + string source = """ + class Program + { + static T[] F1(T[] x, T[] y) => y; + static T[] F2(T[][] x, T[][] y) => y[0]; + static void Main() + { + var x = F1([], [default]); + var y = F2([[null]], [[default]]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var x = F1([], [default]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(T[], T[])").WithLocation(7, 17), + // (8,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F2([[null]], [[default]]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(8, 17)); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void TypeInference_24() + { + string source = """ + using System; + using System.Collections.Generic; + class Program + { + static ReadOnlySpan F1(Span x, ReadOnlySpan y) => y; + static List F2(Span x, ReadOnlySpan> y) => y[0]; + static void Main() + { + var x = F1([], [default, 2]); + x.Report(); + var y = F2([[null]], [[default, (int?)4]]); + y.Report(); + } + } + """; + CompileAndVerify( + new[] { source, s_collectionExtensionsWithSpan }, + targetFramework: TargetFramework.Net70, + verify: Verification.Skipped, + expectedOutput: "[0, 2], [null, 4], "); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void TypeInference_25() + { + string source = """ + using System; + using System.Collections.Generic; + class Program + { + static ReadOnlySpan F1(Span x, ReadOnlySpan y) => y; + static List F2(Span x, ReadOnlySpan> y) => y[0]; + static void Main() + { + var x = F1([], [default]); + var y = F2([[null]], [[default]]); + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (9,17): error CS0411: The type arguments for method 'Program.F1(Span, ReadOnlySpan)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var x = F1([], [default]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Span, System.ReadOnlySpan)").WithLocation(9, 17), + // (10,17): error CS0411: The type arguments for method 'Program.F2(Span, ReadOnlySpan>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F2([[null]], [[default]]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(System.Span, System.ReadOnlySpan>)").WithLocation(10, 17)); + } + + [Fact] + public void TypeInference_26() + { + string source = """ + class Program + { + static void F(T x) { } + static void Main() + { + F([]); + F([null, default, 0]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,9): error CS0411: The type arguments for method 'Program.F(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F([]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T)").WithLocation(6, 9), + // (7,9): error CS0411: The type arguments for method 'Program.F(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F([null, default, 0]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T)").WithLocation(7, 9)); + } + + [Fact] + public void TypeInference_27() + { + string source = """ + class Program + { + static void F(T[,] x) { } + static void Main() + { + F([]); + F([null, default, 0]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,9): error CS0411: The type arguments for method 'Program.F(T[*,*])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F([]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T[*,*])").WithLocation(6, 9), + // (7,11): error CS1503: Argument 1: cannot convert from 'collection literals' to 'int[*,*]' + // F([null, default, 0]); + Diagnostic(ErrorCode.ERR_BadArgType, "[null, default, 0]").WithArguments("1", "collection literals", "int[*,*]").WithLocation(7, 11)); + } + + [Fact] + public void TypeInference_28() + { + string source = """ + class Program + { + static void F(string x, T[] y) { } + static void Main() + { + F([], ['B']); + F([default], ['B']); + F(['A'], ['B']); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,11): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // F([], ['B']); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(6, 11), + // (7,11): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // F([default], ['B']); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(7, 11), + // (7,12): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // F([default], ['B']); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "default").WithArguments("string", "Add").WithLocation(7, 12), + // (8,11): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // F(['A'], ['B']); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['A']").WithArguments("string", "0").WithLocation(8, 11), + // (8,12): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // F(['A'], ['B']); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "'A'").WithArguments("string", "Add").WithLocation(8, 12)); + } + + [Fact] + public void TypeInference_29() + { + string source = """ + delegate void D(); + enum E { } + class Program + { + static void F1(dynamic x, T[] y) { } + static void F2(D x, T[] y) { } + static void F3(E x, T[] y) { } + static void Main() + { + F1([1], [2]); + F2([3], [4]); + F3([5], [6]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (10,12): error CS1503: Argument 1: cannot convert from 'collection literals' to 'dynamic' + // F1([1], [2]); + Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "collection literals", "dynamic").WithLocation(10, 12), + // (11,12): error CS1503: Argument 1: cannot convert from 'collection literals' to 'D' + // F2([3], [4]); + Diagnostic(ErrorCode.ERR_BadArgType, "[3]").WithArguments("1", "collection literals", "D").WithLocation(11, 12), + // (12,12): error CS1503: Argument 1: cannot convert from 'collection literals' to 'E' + // F3([5], [6]); + Diagnostic(ErrorCode.ERR_BadArgType, "[5]").WithArguments("1", "collection literals", "E").WithLocation(12, 12)); + } + + [Fact] + public void TypeInference_30() + { + string source = """ + delegate void D(); + enum E { } + class Program + { + static void F1(dynamic[] x, T[] y) { } + static void F2(D[] x, T[] y) { } + static void F3(E[] x, T[] y) { } + static void Main() + { + F1([1], [2]); + F2([null], [4]); + F3([default], [6]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void TypeInference_31() + { + string source = """ + class Program + { + static void F(T[] x) { } + static void Main() + { + F([null]); + F([Unknown]); + F([Main()]); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,9): error CS0411: The type arguments for method 'Program.F(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F([null]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T[])").WithLocation(6, 9), + // (7,12): error CS0103: The name 'Unknown' does not exist in the current context + // F([Unknown]); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Unknown").WithArguments("Unknown").WithLocation(7, 12), + // (8,9): error CS0411: The type arguments for method 'Program.F(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // F([Main()]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T[])").WithLocation(8, 9)); + } + + [Fact] + public void TypeInference_32() + { + string source = """ + delegate void D(); + class Program + { + static T[] F(T[] x) => x; + static void Main() + { + var x = F([null, Main]); + x.Report(includeType: true); + var y = F([Main, (D)Main]); + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Action[][null, System.Action], D[][D, D], "); + } + [Fact] public void MemberAccess_01() { From 183999a321eb2c202cbe69f026aef44e3f95135f Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 21 Jun 2023 20:08:25 -0700 Subject: [PATCH 2/9] Address feedback --- .../Semantics/CollectionLiteralTests.cs | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 21726beaeb47a..4f4868c6af81f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -921,8 +921,6 @@ static void Main(string[] args) Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, @"new[] { [[""""]], [new object[0]] }").WithLocation(6, 17)); } - // PROTOTYPE: Does BestTypeInferrer.InferBestTypeForConditionalOperator() - // need to updated to handle collection literals? [Fact] public void BestCommonType_05() { @@ -931,22 +929,34 @@ class Program { static void Main(string[] args) { - bool b = args.Length > 0; - var x = b ? new int[0] : [1, 2, 3]; + var x = args.Length > 0 ? new int[0] : [1, 2, 3]; x.Report(includeType: true); - var y = b ? [new int[0]] : [[1, 2, 3]]; + var y = args.Length == 0 ? [[4, 5]] : new[] { new byte[0] }; y.Report(includeType: true); } } """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1, 2, 3], System.Byte[][][[4, 5]], "); + } + + [Fact] + public void BestCommonType_06() + { + string source = """ + class Program + { + static void Main(string[] args) + { + bool b = args.Length > 0; + var y = b ? [new int[0]] : [[1, 2, 3]]; + } + } + """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,11): error CS1061: 'int[]' does not contain a definition for 'Report' and no accessible extension method 'Report' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) - // x.Report(includeType: true); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Report").WithArguments("int[]", "Report").WithLocation(7, 11), - // (8,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection literals' and 'collection literals' + // (6,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection literals' and 'collection literals' // var y = b ? [new int[0]] : [[1, 2, 3]]; - Diagnostic(ErrorCode.ERR_InvalidQM, "b ? [new int[0]] : [[1, 2, 3]]").WithArguments("collection literals", "collection literals").WithLocation(8, 17)); + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? [new int[0]] : [[1, 2, 3]]").WithArguments("collection literals", "collection literals").WithLocation(6, 17)); } [Fact] @@ -1353,20 +1363,18 @@ class Program static void Main() { var x = F1([(long)1], [(int?)2]); - x.Report(true); var y = F2([[(int?)3]], [[(long)4]]); - y.Report(true); } } """; - var comp = CreateCompilation(new[] { source, s_collectionExtensions }); + var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // 0.cs(7,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // (7,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F1([(long)1], [(int?)2]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(T[], T[])").WithLocation(7, 17), - // 0.cs(9,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // (8,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F2([[(int?)3]], [[(long)4]]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(9, 17)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(8, 17)); } [Fact] From c4e3e1afc2444c9263f79cec3b1a7a02fd90064f Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 21 Jun 2023 21:55:28 -0700 Subject: [PATCH 3/9] More tests --- .../Semantics/CollectionLiteralTests.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 4f4868c6af81f..a600ea5a9873d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1737,6 +1737,116 @@ static void Main() CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Action[][null, System.Action], D[][D, D], "); } + [Fact] + public void TypeInference_33() + { + string source = """ + delegate byte D(); + class Program + { + static T[] F(T[] x) => x; + static void Main() + { + var x = F([null, () => 1]); + x.Report(includeType: true); + var y = F([() => 2, (D)(() => 3)]); + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Func[][null, System.Func`1[System.Int32]], D[][D, D], "); + } + + [Fact] + public void TypeInference_34() + { + string source = """ + using System; + using System.Collections.Generic; + class Program + { + static List> F1(List> x) => x; + static string F2() => null; + static void Main() + { + var x = F1([F2]); + var y = F1([null, () => 1]); + var z = F1([() => default, F2]); + } + } + """; + var comp = CreateCompilation(source); + // PROTOTYPE: Should inference succeed? + comp.VerifyEmitDiagnostics( + // (9,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var x = F1([F2]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(9, 17), + // (10,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F1([null, () => 1]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(10, 17), + // (11,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var z = F1([() => default, F2]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(11, 17)); + } + + [Fact] + public void TypeInference_35() + { + string source = """ + class Program + { + static (T, U)[] F((T, U)[] x) => x; + static void Main() + { + var x = F([(1, "2")]); + x.Report(includeType: true); + var y = F([default, (3, (byte)4)]); + y.Report(includeType: true); + } + } + """; + CompileAndVerify( + new[] { source, s_collectionExtensions }, + expectedOutput: "System.ValueTuple[][(1, 2)], System.ValueTuple[][(0, 0), (3, 4)], "); + } + + [Fact] + public void TypeInference_36() + { + string source = """ + using System; + class Program + { + static void F(Action[] a) { } + static void Main() + { + F([Main, () => { }]); + } + } + """; + var comp = CreateCompilation(source); + // PROTOTYPE: Unexpected syntax errors. + comp.VerifyEmitDiagnostics( + // (7,12): error CS0246: The type or namespace name 'MainAttribute' could not be found (are you missing a using directive or an assembly reference?) + // F([Main, () => { }]); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Main").WithArguments("MainAttribute").WithLocation(7, 12), + // (7,12): error CS0246: The type or namespace name 'Main' could not be found (are you missing a using directive or an assembly reference?) + // F([Main, () => { }]); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Main").WithArguments("Main").WithLocation(7, 12), + // (7,18): error CS1003: Syntax error, ']' expected + // F([Main, () => { }]); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("]").WithLocation(7, 18), + // (7,27): error CS1026: ) expected + // F([Main, () => { }]); + Diagnostic(ErrorCode.ERR_CloseParenExpected, "]").WithLocation(7, 27), + // (7,28): error CS1002: ; expected + // F([Main, () => { }]); + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(7, 28), + // (7,28): error CS1513: } expected + // F([Main, () => { }]); + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(7, 28)); + } + [Fact] public void MemberAccess_01() { From dc92b4f52c99abdaa623659c1d7241bc3472f6c4 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Thu, 22 Jun 2023 17:06:44 -0700 Subject: [PATCH 4/9] Update tests --- .../Semantics/CollectionLiteralTests.cs | 41 +------------------ 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 5d9c5ac91b8f1..4f443d6e6d408 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1890,7 +1890,7 @@ static void Main() { var x = F1([F2]); var y = F1([null, () => 1]); - var z = F1([() => default, F2]); + var z = F1([F2, () => default]); } } """; @@ -1904,7 +1904,7 @@ static void Main() // var y = F1([null, () => 1]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(10, 17), // (11,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var z = F1([() => default, F2]); + // var z = F1([F2, () => default]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(11, 17)); } @@ -1929,43 +1929,6 @@ static void Main() expectedOutput: "System.ValueTuple[][(1, 2)], System.ValueTuple[][(0, 0), (3, 4)], "); } - [Fact] - public void TypeInference_36() - { - string source = """ - using System; - class Program - { - static void F(Action[] a) { } - static void Main() - { - F([Main, () => { }]); - } - } - """; - var comp = CreateCompilation(source); - // PROTOTYPE: Unexpected syntax errors. - comp.VerifyEmitDiagnostics( - // (7,12): error CS0246: The type or namespace name 'MainAttribute' could not be found (are you missing a using directive or an assembly reference?) - // F([Main, () => { }]); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Main").WithArguments("MainAttribute").WithLocation(7, 12), - // (7,12): error CS0246: The type or namespace name 'Main' could not be found (are you missing a using directive or an assembly reference?) - // F([Main, () => { }]); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Main").WithArguments("Main").WithLocation(7, 12), - // (7,18): error CS1003: Syntax error, ']' expected - // F([Main, () => { }]); - Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("]").WithLocation(7, 18), - // (7,27): error CS1026: ) expected - // F([Main, () => { }]); - Diagnostic(ErrorCode.ERR_CloseParenExpected, "]").WithLocation(7, 27), - // (7,28): error CS1002: ; expected - // F([Main, () => { }]); - Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(7, 28), - // (7,28): error CS1513: } expected - // F([Main, () => { }]); - Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(7, 28)); - } - [Fact] public void MemberAccess_01() { From 2b012faa8f77932480b54a394338c473a2d45fc3 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 21 Jun 2023 23:16:40 -0700 Subject: [PATCH 5/9] Recurse through elements for parameter and output type inferences --- .../OverloadResolution/MethodTypeInference.cs | 66 +++++++++++-------- .../Semantics/CollectionLiteralTests.cs | 59 +++++++++++++++-- 2 files changed, 91 insertions(+), 34 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index baa0a98cc5d3f..f0a02acebfd25 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -613,7 +613,7 @@ private void MakeExplicitParameterTypeInferences(Binder binder, BoundExpression } else if (argument.Kind == BoundKind.UnconvertedCollectionLiteralExpression) { - MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)argument, target, ref useSiteInfo); + MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)argument, target, kind, ref useSiteInfo); } else if (argument.Kind != BoundKind.TupleLiteral || !MakeExplicitParameterTypeInferences(binder, (BoundTupleLiteral)argument, target, kind, ref useSiteInfo)) @@ -636,6 +636,7 @@ private void MakeCollectionLiteralTypeInferences( Binder binder, BoundUnconvertedCollectionLiteralExpression argument, TypeWithAnnotations target, + ExactOrBoundsKind kind, ref CompoundUseSiteInfo useSiteInfo) { if (target.Type is null) @@ -648,40 +649,29 @@ private void MakeCollectionLiteralTypeInferences( return; } - if (!tryGetCollectionIterationType(binder, (ExpressionSyntax)argument.Syntax, target.Type, out TypeWithAnnotations targetElementType)) + if (!TryGetCollectionIterationType(binder, (ExpressionSyntax)argument.Syntax, target.Type, out TypeWithAnnotations targetElementType)) { return; } foreach (var element in argument.Elements) { - if (element.Kind == BoundKind.UnconvertedCollectionLiteralExpression) - { - MakeCollectionLiteralTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)element, targetElementType, ref useSiteInfo); - } - else - { - var elementType = _extensions.GetTypeWithAnnotations(element); - if (IsReallyAType(elementType.Type)) - { - LowerBoundInference(elementType, targetElementType, ref useSiteInfo); - } - } + MakeExplicitParameterTypeInferences(binder, element, targetElementType, kind, ref useSiteInfo); } + } - static bool tryGetCollectionIterationType(Binder binder, ExpressionSyntax syntax, TypeSymbol collectionType, out TypeWithAnnotations iterationType) - { - var builder = new ForEachEnumeratorInfo.Builder(); - BoundExpression collectionExpr = new BoundValuePlaceholder(syntax, collectionType); - return binder.GetEnumeratorInfoAndInferCollectionElementType( - syntax, - syntax, - ref builder, - ref collectionExpr, - isAsync: false, - BindingDiagnosticBag.Discarded, - out iterationType); - } + private static bool TryGetCollectionIterationType(Binder binder, ExpressionSyntax syntax, TypeSymbol collectionType, out TypeWithAnnotations iterationType) + { + var builder = new ForEachEnumeratorInfo.Builder(); + BoundExpression collectionExpr = new BoundValuePlaceholder(syntax, collectionType); + return binder.GetEnumeratorInfoAndInferCollectionElementType( + syntax, + syntax, + ref builder, + ref collectionExpr, + isAsync: false, + BindingDiagnosticBag.Discarded, + out iterationType); } #nullable disable @@ -847,6 +837,10 @@ private void MakeOutputTypeInferences(Binder binder, BoundExpression argument, T { MakeOutputTypeInferences(binder, (BoundTupleLiteral)argument, formalType, ref useSiteInfo); } + else if (argument.Kind == BoundKind.UnconvertedCollectionLiteralExpression) + { + MakeOutputTypeInferences(binder, (BoundUnconvertedCollectionLiteralExpression)argument, formalType, ref useSiteInfo); + } else { if (HasUnfixedParamInOutputType(argument, formalType.Type) && !HasUnfixedParamInInputType(argument, formalType.Type)) @@ -860,6 +854,24 @@ private void MakeOutputTypeInferences(Binder binder, BoundExpression argument, T } } + private void MakeOutputTypeInferences(Binder binder, BoundUnconvertedCollectionLiteralExpression argument, TypeWithAnnotations formalType, ref CompoundUseSiteInfo useSiteInfo) + { + if (argument.Elements.Length == 0) + { + return; + } + + if (!TryGetCollectionIterationType(binder, (ExpressionSyntax)argument.Syntax, formalType.Type, out TypeWithAnnotations targetElementType)) + { + return; + } + + foreach (var element in argument.Elements) + { + MakeOutputTypeInferences(binder, element, targetElementType, ref useSiteInfo); + } + } + private void MakeOutputTypeInferences(Binder binder, BoundTupleLiteral argument, TypeWithAnnotations formalType, ref CompoundUseSiteInfo useSiteInfo) { if (formalType.Type.Kind != SymbolKind.NamedType) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 4f443d6e6d408..a1eda8561a763 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1889,27 +1889,72 @@ class Program static void Main() { var x = F1([F2]); + x.Report(); var y = F1([null, () => 1]); + y.Report(); var z = F1([F2, () => default]); + z.Report(); + } + } + """; + CompileAndVerify( + new[] { source, s_collectionExtensions }, + expectedOutput: "[System.Func`1[System.String]], [null, System.Func`1[System.Int32]], [System.Func`1[System.String], System.Func`1[System.String]], "); + } + + [Fact] + public void TypeInference_35() + { + string source = """ + using System; + using System.Collections.Generic; + class Program + { + static List> F1(List> x) => x; + static void F2(string s) { } + static void Main() + { + var x = F1([F2, (string s) => { }]); + x.Report(); + var y = F1([null, (int a) => { }]); + y.Report(); + } + } + """; + CompileAndVerify( + new[] { source, s_collectionExtensions }, + expectedOutput: "[System.Action`1[System.String], System.Action`1[System.String]], [null, System.Action`1[System.Int32]], "); + } + + [Fact] + public void TypeInference_36() + { + string source = """ + using System; + using System.Collections.Generic; + class Program + { + static List> F1(List> x) => x; + static string F2() => null; + static void Main() + { + var x = F1([() => default]); + var y = F1([() => 2, F2]); } } """; var comp = CreateCompilation(source); - // PROTOTYPE: Should inference succeed? comp.VerifyEmitDiagnostics( // (9,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var x = F1([F2]); + // var x = F1([() => default]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(9, 17), // (10,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F1([null, () => 1]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(10, 17), - // (11,17): error CS0411: The type arguments for method 'Program.F1(List>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var z = F1([F2, () => default]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(11, 17)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(System.Collections.Generic.List>)").WithLocation(10, 17)); } [Fact] - public void TypeInference_35() + public void TypeInference_37() { string source = """ class Program From f8673ba391d2e15a060df8e5d9dba8fd1aa0fe5d Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 23 Jun 2023 06:37:55 -0700 Subject: [PATCH 6/9] Address feedback --- .../Semantics/CollectionLiteralTests.cs | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index a1eda8561a763..990eb9e8727e1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -999,29 +999,6 @@ static void Main() [Fact] public void BestCommonType_03() - { - string source = """ - class Program - { - static void Main(string[] args) - { - var x = new[] { [ulong.MaxValue], [1, 2, 3] }; - var y = new[] { [[ulong.MaxValue]], [[1, 2, 3]] }; - } - } - """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (5,17): error CS0826: No best type found for implicitly-typed array - // var x = new[] { [ulong.MaxValue], [1, 2, 3] }; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { [ulong.MaxValue], [1, 2, 3] }").WithLocation(5, 17), - // (6,17): error CS0826: No best type found for implicitly-typed array - // var y = new[] { [[ulong.MaxValue]], [[1, 2, 3]] }; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { [[ulong.MaxValue]], [[1, 2, 3]] }").WithLocation(6, 17)); - } - - [Fact] - public void BestCommonType_04() { string source = """ class Program @@ -1041,7 +1018,7 @@ static void Main(string[] args) } [Fact] - public void BestCommonType_05() + public void BestCommonType_04() { string source = """ class Program @@ -1059,7 +1036,7 @@ static void Main(string[] args) } [Fact] - public void BestCommonType_06() + public void BestCommonType_05() { string source = """ class Program From 5f1e143c522462e3d46eb89828914fef16bbc25b Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 23 Jun 2023 06:48:46 -0700 Subject: [PATCH 7/9] Add parens around type name --- .../Semantics/CollectionLiteralTests.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 990eb9e8727e1..41f21ae0c0f9e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -49,7 +49,7 @@ internal static void Report(this object o, bool includeType = false) { var builder = new StringBuilder(); Append(builder, isFirst: true, o); - if (includeType) Console.Write(GetTypeName(o.GetType())); + if (includeType) Console.Write("({0}) ", GetTypeName(o.GetType())); Console.Write(builder.ToString()); Console.Write(", "); } @@ -477,7 +477,7 @@ static void Main() """; CompileAndVerify( new[] { source, s_collectionExtensions }, - expectedOutput: "System.Collections.Generic.List[], System.Collections.Generic.List[], System.Collections.Generic.List[], System.Collections.Generic.List[], System.Collections.Generic.List[], "); + expectedOutput: "(System.Collections.Generic.List) [], (System.Collections.Generic.List) [], (System.Collections.Generic.List) [], (System.Collections.Generic.List) [], (System.Collections.Generic.List) [], "); } [Fact] @@ -504,7 +504,7 @@ static void Main() """; CompileAndVerify( new[] { source, s_collectionExtensions }, - expectedOutput: "System.Collections.Generic.List[1], System.Collections.Generic.List[2], System.Collections.Generic.List[3], System.Collections.Generic.List[4], System.Collections.Generic.List[5], "); + expectedOutput: "(System.Collections.Generic.List) [1], (System.Collections.Generic.List) [2], (System.Collections.Generic.List) [3], (System.Collections.Generic.List) [4], (System.Collections.Generic.List) [5], "); } [Fact] @@ -714,7 +714,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][], System.Int32[][1, 2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [], (System.Int32[]) [1, 2], "); } // Overload resolution should choose collection initializer type over interface. @@ -738,7 +738,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Collections.Generic.List[], System.Collections.Generic.List[1, 2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Collections.Generic.List) [], (System.Collections.Generic.List) [1, 2], "); } [Fact] @@ -901,7 +901,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1], System.Int32[][2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1], (System.Int32[]) [2], "); } [Fact] @@ -976,7 +976,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][][[], [1, 2, 3]], System.Int32[][][][[[]], [[1, 2, 3]]], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[][]) [[], [1, 2, 3]], (System.Int32[][][]) [[[]], [[1, 2, 3]]], "); } [Fact] @@ -994,7 +994,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Byte[][][[], [1, 2, 3]], System.Byte[][][][[[]], [[1, 2, 3]]], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Byte[][]) [[], [1, 2, 3]], (System.Byte[][][]) [[[]], [[1, 2, 3]]], "); } [Fact] @@ -1032,7 +1032,7 @@ static void Main(string[] args) } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1, 2, 3], System.Byte[][][[4, 5]], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2, 3], (System.Byte[][]) [[4, 5]], "); } [Fact] @@ -1342,7 +1342,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1, 2, 3], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2, 3], "); } // PROTOTYPE: Test other variance cases. @@ -1362,7 +1362,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Object[][1], System.Object[][3], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Object[]) [1], (System.Object[]) [3], "); } // PROTOTYPE: Test other variance cases. And these are just for array inferences. @@ -1383,7 +1383,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int64[][1], System.Int64[][3], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int64[]) [1], (System.Int64[]) [3], "); } [Fact] @@ -1400,7 +1400,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1, 2, 3], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2, 3], "); } [Fact] @@ -1445,7 +1445,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][2], System.Int32[][4], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [2], (System.Int32[]) [4], "); } [Fact] @@ -1488,7 +1488,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Collections.Generic.List[[4, 5], []], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Collections.Generic.List) [[4, 5], []], "); } [Fact] @@ -1527,7 +1527,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][1], System.Object[][3], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1], (System.Object[]) [3], "); } [Fact] @@ -1567,7 +1567,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Int32[][0, 2], System.Nullable[][null, 4], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [0, 2], (System.Nullable[]) [null, 4], "); } [Fact] @@ -1830,7 +1830,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Action[][null, System.Action], D[][D, D], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Action[]) [null, System.Action], (D[]) [D, D], "); } [Fact] @@ -1850,7 +1850,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Func[][null, System.Func`1[System.Int32]], D[][D, D], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Func[]) [null, System.Func`1[System.Int32]], (D[]) [D, D], "); } [Fact] @@ -1948,7 +1948,7 @@ static void Main() """; CompileAndVerify( new[] { source, s_collectionExtensions }, - expectedOutput: "System.ValueTuple[][(1, 2)], System.ValueTuple[][(0, 0), (3, 4)], "); + expectedOutput: "(System.ValueTuple[]) [(1, 2)], (System.ValueTuple[]) [(0, 0), (3, 4)], "); } [Fact] From 72c79461159e5a22a4473b38938b1b0fac9c87cf Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 23 Jun 2023 07:02:40 -0700 Subject: [PATCH 8/9] Merge tests --- .../Semantics/CollectionLiteralTests.cs | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 41f21ae0c0f9e..70afeeab0271a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1111,18 +1111,18 @@ public void TypeInference_03() string source = """ class Program { - static T[] AsArray(T[] args) - { - return args; - } + static T[] AsArray1(T[] args) => args; + static T[] AsArray2(params T[] args) => args; static void Main() { - var a = AsArray([1, 2, 3]); + var a = AsArray1([1, 2, 3]); a.Report(); + var b = AsArray2(["4", null]); + b.Report(); } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [4, null], "); } [Fact] @@ -1152,33 +1152,13 @@ static void Main() Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(10, 21)); } - [Fact] - public void TypeInference_05() - { - string source = """ - class Program - { - static T[] AsArray(params T[] args) - { - return args; - } - static void Main() - { - var a = AsArray([1, 2, 3]); - a.Report(); - } - } - """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], "); - } - [Fact] public void TypeInference_06() { string source = """ class Program { - static T[] AsArray(params T[] args) + static T[] AsArray(T[] args) { return args; } From 69f6b5a8b028bdadfa188731164cfb030b9e07e7 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 23 Jun 2023 07:05:41 -0700 Subject: [PATCH 9/9] Add existing scenario to test --- .../Semantics/CollectionLiteralTests.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 70afeeab0271a..2669eeea2f2ab 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1389,23 +1389,28 @@ public void TypeInference_15() string source = """ class Program { + static T F0(T[] x, T y) => y; static T[] F1(T[] x, T[] y) => y; static T[] F2(T[][] x, T[][] y) => y[0]; static void Main() { - var x = F1(new byte[0], [1, 2]); - var y = F2(new[] { new byte[0] }, [[3, 4]]); + var x = F0(new byte[0], 1); + var y = F1(new byte[0], [1, 2]); + var z = F2(new[] { new byte[0] }, [[3, 4]]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var x = F1(new byte[0], [1, 2]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(T[], T[])").WithLocation(7, 17), - // (8,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var y = F2(new[] { new byte[0] }, [[3, 4]]); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(8, 17)); + // (8,17): error CS0411: The type arguments for method 'Program.F0(T[], T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var x = F0(new byte[0], 1); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F0").WithArguments("Program.F0(T[], T)").WithLocation(8, 17), + // (9,17): error CS0411: The type arguments for method 'Program.F1(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var y = F1(new byte[0], [1, 2]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1(T[], T[])").WithLocation(9, 17), + // (10,17): error CS0411: The type arguments for method 'Program.F2(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var z = F2(new[] { new byte[0] }, [[3, 4]]); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T[][], T[][])").WithLocation(10, 17)); } [Fact]