diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 48be9963f215e..9310fe215b6ef 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -486,18 +487,63 @@ void checkConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNode syn } else if (conversion.IsSpan) { + Debug.Assert(destination.OriginalDefinition.IsSpan() || destination.OriginalDefinition.IsReadOnlySpan()); + + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureFirstClassSpan, diagnostics); + // PROTOTYPE: Check runtime APIs used for other span conversions once they are implemented. - if (destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)) + // NOTE: We cannot use well-known members because per the spec + // the Span types involved in the Span conversions can be any that match the type name. + if (TryFindImplicitOperatorFromArray(destination.OriginalDefinition) is { } method) { - _ = GetWellKnownTypeMember(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, diagnostics, syntax: syntax); + diagnostics.ReportUseSite(method, syntax); } else { - Debug.Assert(destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions)); - _ = GetWellKnownTypeMember(WellKnownMember.System_Span_T__op_Implicit_Array, diagnostics, syntax: syntax); + Error(diagnostics, + ErrorCode.ERR_MissingPredefinedMember, + syntax, + destination.OriginalDefinition, + WellKnownMemberNames.ImplicitConversionName); + } + } + } + } + + internal static MethodSymbol? TryFindImplicitOperatorFromArray(TypeSymbol type) + { + Debug.Assert(type.IsSpan() || type.IsReadOnlySpan()); + + return TryFindSingleMember(type, WellKnownMemberNames.ImplicitConversionName, + static (method) => method is + { + ParameterCount: 1, + Arity: 0, + IsStatic: true, + DeclaredAccessibility: Accessibility.Public, + Parameters: [{ Type: ArrayTypeSymbol { IsSZArray: true, ElementType: TypeParameterSymbol } }] + }); + } + + private static MethodSymbol? TryFindSingleMember(TypeSymbol type, string name, Func predicate) + { + var members = type.GetMembers(name); + MethodSymbol? result = null; + foreach (var member in members) + { + if (member is MethodSymbol method && predicate(method)) + { + if (result is not null) + { + // Ambiguous member found. + return null; } + + result = method; } } + + return result; } private static void CheckInlineArrayTypeIsSupported(SyntaxNode syntax, TypeSymbol inlineArrayType, TypeSymbol elementType, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 5a895b42a467c..1b30e949ff574 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -602,7 +602,6 @@ private static bool IsStandardImplicitConversionFromExpression(ConversionKind ki case ConversionKind.StackAllocToPointerType: case ConversionKind.StackAllocToSpanType: case ConversionKind.InlineArray: - case ConversionKind.ImplicitSpan: return true; default: return false; @@ -624,6 +623,7 @@ private static bool IsStandardImplicitConversionFromType(ConversionKind kind) case ConversionKind.ImplicitPointer: case ConversionKind.ImplicitPointerToVoid: case ConversionKind.ImplicitTuple: + case ConversionKind.ImplicitSpan: return true; default: return false; @@ -743,6 +743,11 @@ Conversion classifyConversion(TypeSymbol source, TypeSymbol destination, ref Com return tupleConversion; } + if (HasImplicitSpanConversion(source, destination, ref useSiteInfo)) + { + return Conversion.ImplicitSpan; + } + return Conversion.NoConversion; } } @@ -912,6 +917,10 @@ private Conversion DeriveStandardExplicitFromOppositeStandardImplicitConversion( break; + case ConversionKind.ImplicitSpan: + impliedExplicitConversion = Conversion.NoConversion; + break; + default: throw ExceptionUtilities.UnexpectedValue(oppositeConversion.Kind); } @@ -1021,11 +1030,6 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi return Conversion.ImplicitDynamic; } - if (HasImplicitSpanConversion(source, destination, ref useSiteInfo)) - { - return Conversion.ImplicitSpan; - } - // The following conversions only exist for certain form of expressions, // if we have no expression none if them is applicable. if (sourceExpression == null) @@ -1909,8 +1913,7 @@ public Conversion ConvertExtensionMethodThisArg(TypeSymbol parameterType, TypeSy public Conversion ClassifyImplicitExtensionMethodThisArgConversion(BoundExpression sourceExpressionOpt, TypeSymbol sourceType, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { Debug.Assert(sourceExpressionOpt is null || Compilation is not null); - // PROTOTYPE: Revert `TypeSymbol.Equals(...)` to `(object)sourceExpressionOpt.Type == sourceType` when implicit span is a "conversion from type". - Debug.Assert(sourceExpressionOpt == null || TypeSymbol.Equals(sourceExpressionOpt.Type, sourceType, TypeCompareKind.ConsiderEverything)); + Debug.Assert(sourceExpressionOpt == null || (object)sourceExpressionOpt.Type == sourceType); Debug.Assert((object)destination != null); if ((object)sourceType != null) @@ -3929,10 +3932,18 @@ private static bool IsIntegerTypeSupportingPointerConversions(TypeSymbol type) } #nullable enable + private bool IsFeatureFirstClassSpanEnabled + { + get + { + // Note: when Compilation is null, we assume latest LangVersion. + return Compilation?.IsFeatureEnabled(MessageID.IDS_FeatureFirstClassSpan) != false; + } + } + private bool HasImplicitSpanConversion(TypeSymbol? source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { - // PROTOTYPE: Is it fine that this conversion does not exists when Compilation is null? - if (Compilation?.IsFeatureEnabled(MessageID.IDS_FeatureFirstClassSpan) != true) + if (!IsFeatureFirstClassSpanEnabled) { return false; } @@ -3941,14 +3952,14 @@ private bool HasImplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio if (source is ArrayTypeSymbol { IsSZArray: true, ElementTypeWithAnnotations: { } elementType }) { // SPEC: ...to `System.Span`. - if (destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions)) + if (destination.OriginalDefinition.IsSpan()) { var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; return hasIdentityConversion(elementType, spanElementType); } // SPEC: ...to `System.ReadOnlySpan`, provided that `Ei` is covariance-convertible to `Ui`. - if (destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)) + if (destination.OriginalDefinition.IsReadOnlySpan()) { var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; return hasCovariantConversion(elementType, spanElementType, ref useSiteInfo); @@ -3975,8 +3986,7 @@ bool hasIdentityConversion(TypeWithAnnotations source, TypeWithAnnotations desti /// private bool HasExplicitSpanConversion(TypeSymbol? source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { - // PROTOTYPE: Is it fine that this conversion does not exists when Compilation is null? - if (Compilation?.IsFeatureEnabled(MessageID.IDS_FeatureFirstClassSpan) != true) + if (!IsFeatureFirstClassSpanEnabled) { return false; } @@ -3985,8 +3995,7 @@ private bool HasExplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio // to `System.Span` or `System.ReadOnlySpan` // provided an explicit reference conversion exists from `Ti` to `Ui`. if (source is ArrayTypeSymbol { IsSZArray: true, ElementTypeWithAnnotations: { } elementType } && - (destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions) || - destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions))) + (destination.OriginalDefinition.IsSpan() || destination.OriginalDefinition.IsReadOnlySpan())) { var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; return HasIdentityOrReferenceConversion(elementType.Type, spanElementType.Type, ref useSiteInfo) && @@ -3998,23 +4007,17 @@ private bool HasExplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio private bool IgnoreUserDefinedSpanConversions(TypeSymbol? source, TypeSymbol? target) { - if (source is null || target is null) - { - return false; - } - - // PROTOTYPE: Is it fine that this check is not performed when Compilation is null? - return Compilation?.IsFeatureEnabled(MessageID.IDS_FeatureFirstClassSpan) == true && - (ignoreUserDefinedSpanConversionsInOneDirection(Compilation, source, target) || - ignoreUserDefinedSpanConversionsInOneDirection(Compilation, target, source)); + return source is not null && target is not null && + IsFeatureFirstClassSpanEnabled && + (ignoreUserDefinedSpanConversionsInOneDirection(source, target) || + ignoreUserDefinedSpanConversionsInOneDirection(target, source)); - static bool ignoreUserDefinedSpanConversionsInOneDirection(CSharpCompilation compilation, TypeSymbol a, TypeSymbol b) + static bool ignoreUserDefinedSpanConversionsInOneDirection(TypeSymbol a, TypeSymbol b) { // SPEC: User-defined conversions are not considered when converting between // SPEC: - any single-dimensional `array_type` and `System.Span`/`System.ReadOnlySpan` if (a is ArrayTypeSymbol { IsSZArray: true } && - (b.OriginalDefinition.Equals(compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions) || - b.OriginalDefinition.Equals(compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions))) + (b.OriginalDefinition.IsSpan() || b.OriginalDefinition.IsReadOnlySpan())) { return true; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 1f24819989622..3be4f5f03c861 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7909,10 +7909,10 @@ private Conversion GenerateConversionForConditionalOperator(BoundExpression sour return conversion; } - private Conversion GenerateConversion(Conversions conversions, BoundExpression? sourceExpression, TypeSymbol? sourceType, TypeSymbol destinationType, bool fromExplicitCast, bool extensionMethodThisArgument, bool isChecked, bool forceFromExpression = false) + private Conversion GenerateConversion(Conversions conversions, BoundExpression? sourceExpression, TypeSymbol? sourceType, TypeSymbol destinationType, bool fromExplicitCast, bool extensionMethodThisArgument, bool isChecked) { var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; - bool useExpression = forceFromExpression || sourceType is null || UseExpressionForConversion(sourceExpression); + bool useExpression = sourceType is null || UseExpressionForConversion(sourceExpression); if (extensionMethodThisArgument) { return conversions.ClassifyImplicitExtensionMethodThisArgConversion( @@ -8903,10 +8903,7 @@ private TypeWithState VisitConversion( if (checkConversion) { var previousKind = conversion.Kind; - conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument, isChecked: conversionOpt?.Checked ?? false, - // Span conversion is "from expression". - // PROTOTYPE: Should it be "from type" instead? - forceFromExpression: true); + conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument, isChecked: conversionOpt?.Checked ?? false); Debug.Assert(!conversion.Exists || conversion.Kind == previousKind); canConvertNestedNullability = conversion.Exists; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 19bc054cbcc5f..90bd13ffa13c1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -619,28 +619,17 @@ private BoundExpression MakeConversionNodeCore( { var spanType = (NamedTypeSymbol)rewrittenType; - WellKnownMember member; - if (spanType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T), TypeCompareKind.AllIgnoreOptions)) - { - member = WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array; - } - else - { - Debug.Assert(spanType.OriginalDefinition.Equals(_compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions)); - member = WellKnownMember.System_Span_T__op_Implicit_Array; - } - - if (!TryGetWellKnownTypeMember(rewrittenOperand.Syntax, member, out MethodSymbol? symbol)) + if (Binder.TryFindImplicitOperatorFromArray(spanType.OriginalDefinition) is not { } methodDefinition) { throw ExceptionUtilities.Unreachable(); } else { - MethodSymbol method = symbol.AsMember(spanType); + MethodSymbol method = methodDefinition.AsMember(spanType); rewrittenOperand = _factory.Convert(method.ParameterTypesWithAnnotations[0].Type, rewrittenOperand); - if (member == WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array) + if (_compilation.IsReadOnlySpanType(spanType)) { return new BoundReadOnlySpanFromArray(syntax, rewrittenOperand, method, spanType) { WasCompilerGenerated = true }; } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 3e95df90575a1..2dd13ef77b904 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1338,6 +1338,28 @@ internal static bool IsValidV6SwitchGoverningType(this TypeSymbol type, bool isT return false; } + internal static bool IsSpan(this TypeSymbol type) + { + return type is NamedTypeSymbol + { + Name: "Span", + Arity: 1, + ContainingType: null, + ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true }, + }; + } + + internal static bool IsReadOnlySpan(this TypeSymbol type) + { + return type is NamedTypeSymbol + { + Name: "ReadOnlySpan", + Arity: 1, + ContainingType: null, + ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true }, + }; + } + internal static bool IsSpanChar(this TypeSymbol type) { return type is NamedTypeSymbol diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index 9c93b0061bc40..a8f84ee322fbf 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -63,39 +63,27 @@ readonly struct StringValues // public static string Join(string separator, params ReadOnlySpan values) => "span"; Diagnostic(ErrorCode.ERR_FeatureInPreview, "params ReadOnlySpan values").WithArguments("params collections").WithLocation(13, 49)); - var expectedDiagnostics = new[] - { - // (7,65): error CS0121: The call is ambiguous between the following methods or properties: 'StringExtensions.Join(string, params string[])' and 'StringExtensions.Join(string, params ReadOnlySpan)' - // public static string M(StringValues sv) => StringExtensions.Join(",", sv); - Diagnostic(ErrorCode.ERR_AmbigCall, "Join").WithArguments("StringExtensions.Join(string, params string[])", "StringExtensions.Join(string, params System.ReadOnlySpan)").WithLocation(7, 65) - }; - - CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); - CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); - - // PROTOTYPE: Need to consider "implicit span conversion" in "better conversion target" for this to work. + var expectedOutput = "array"; - //var expectedOutput = "array"; - - //var expectedIl = """ - // { - // // Code size 17 (0x11) - // .maxstack 2 - // IL_0000: ldstr "," - // IL_0005: ldarg.0 - // IL_0006: call "string[] StringValues.op_Implicit(StringValues)" - // IL_000b: call "string StringExtensions.Join(string, params string[])" - // IL_0010: ret - // } - // """; + var expectedIl = """ + { + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldstr "," + IL_0005: ldarg.0 + IL_0006: call "string[] StringValues.op_Implicit(StringValues)" + IL_000b: call "string StringExtensions.Join(string, params string[])" + IL_0010: ret + } + """; - //var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); - //var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); - //verifier.VerifyIL("C.M", expectedIl); + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); - //comp = CreateCompilationWithSpan(source); - //verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); - //verifier.VerifyIL("C.M", expectedIl); + comp = CreateCompilationWithSpan(source); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); } [Fact] @@ -345,14 +333,186 @@ public void Conversion_Array_Span_Implicit_MissingHelper() using System; Span s = arr(); static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + } + } """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } - var comp = CreateCompilationWithSpan(source); - comp.MakeMemberMissing(WellKnownMember.System_Span_T__op_Implicit_Array); - comp.VerifyDiagnostics( - // (2,15): error CS0656: Missing compiler required member 'System.Span`1.op_Implicit' + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_DifferentOperator( + [CombinatorialValues("int[]", "T[][]")] string parameterType) + { + var source = $$""" + using System; + Span s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static implicit operator Span({{parameterType}} array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' // Span s = arr(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span`1", "op_Implicit").WithLocation(2, 15)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_ExplicitOperator() + { + var source = """ + using System; + Span s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static explicit operator Span(T[] array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Explicit_ExplicitOperator() + { + var source = """ + using System; + Span s = (Span)arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static explicit operator Span(T[] array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'Span.op_Implicit' + // Span s = (Span)arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "(Span)arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_UnrecognizedModreq() + { + /* + public struct Span + { + public static implicit operator modreq(A) Span(T[] array) => throw null; + } + public class A { } + public static class C + { + public static void M(Span s) { } + } + */ + var ilSource = """ + .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType + { + .pack 0 + .size 1 + .method public hidebysig specialname static valuetype System.Span`1 modreq(A) op_Implicit(!T[] 'array') cil managed + { + .maxstack 1 + ret + } + } + .class public auto ansi sealed beforefieldinit A extends System.Object + { + } + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + { + .maxstack 1 + ret + } + } + """; + var source = """ + C.M(new int[] { 1, 2, 3 }); + """; + CreateCompilationWithIL(source, ilSource).VerifyDiagnostics( + // (1,5): error CS0570: 'Span.implicit operator Span(T[])' is not supported by the language + // C.M(new int[] { 1, 2, 3 }); + Diagnostic(ErrorCode.ERR_BindToBogus, "new int[] { 1, 2, 3 }").WithArguments("System.Span.implicit operator System.Span(T[])").WithLocation(1, 5)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_UnrecognizedModopt() + { + /* + public struct Span + { + public static implicit operator modopt(A) Span(T[] array) => throw null; + } + public class A { } + public static class C + { + public static void M(Span s) { } + } + */ + var ilSource = """ + .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType + { + .pack 0 + .size 1 + .method public hidebysig specialname static valuetype System.Span`1 modopt(A) op_Implicit(!T[] 'array') cil managed + { + .maxstack 1 + ret + } + } + .class public auto ansi sealed beforefieldinit A extends System.Object + { + } + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + { + .maxstack 1 + ret + } + } + """; + var source = """ + C.M(arr()); + static int[] arr() => new int[] { 1, 2, 3 }; + """; + var comp = CreateCompilationWithIL(source, ilSource); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "int[] Program.<
$>g__arr|0_0()" + IL_0005: call "System.Span System.Span.op_Implicit(int[])" + IL_000a: call "void C.M(System.Span)" + IL_000f: ret + } + """); } [Theory, MemberData(nameof(LangVersions))] @@ -403,15 +563,65 @@ .maxstack 4 """); } - [Fact] - public void Conversion_Array_Span_Implicit_SpanTwice() + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_ConstantData_NotWellKnownSpan(LanguageVersion langVersion) { - static string getSpanSource(string output) => $$""" + var source = """ + extern alias span; + C.M1(new[] { 1 }); + C.M2(new[] { 2 }); + static class C + { + public static void M1(span::System.Span s) => System.Console.Write(s[0]); + public static void M2(span::System.ReadOnlySpan s) => System.Console.Write(s[0]); + } + """; + var spanDll = CreateCompilation(SpanSource, options: TestOptions.UnsafeReleaseDll) + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span"]); + var verifier = CompileAndVerify([source, SpanSource], + references: [spanDll], + expectedOutput: "12", + verify: Verification.Fails, + // warning CS0436: Type conflicts with imported type + options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("CS0436", ReportDiagnostic.Suppress), + parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 41 (0x29) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: call "System.Span System.Span.op_Implicit(int[])" + IL_000f: call "void C.M1(System.Span)" + IL_0014: ldc.i4.1 + IL_0015: newarr "int" + IL_001a: dup + IL_001b: ldc.i4.0 + IL_001c: ldc.i4.2 + IL_001d: stelem.i4 + IL_001e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_0023: call "void C.M2(System.ReadOnlySpan)" + IL_0028: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_MultipleSpans_01( + [CombinatorialValues("Span", "ReadOnlySpan")] string type) + { + string getSpanSource(string output) => $$""" namespace System { - public readonly ref struct Span + public readonly ref struct {{type}} { - public static implicit operator Span(T[] array) + public static implicit operator {{type}}(T[] array) { Console.Write("{{output}}"); return default; @@ -424,10 +634,12 @@ public static implicit operator Span(T[] array) .VerifyDiagnostics() .EmitToImageReference(); - var source = """ + var source = $$""" using System; - Span s = arr(); + {{type}} s = arr(); + use(s); static int[] arr() => new int[] { 1, 2, 3 }; + static void use({{type}} s) { } """; var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); @@ -435,17 +647,64 @@ public static implicit operator Span(T[] array) verifier.VerifyDiagnostics( // (2,1): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. // Span s = arr(); - Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Span").WithArguments("", "System.Span", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "System.Span").WithLocation(2, 1), + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(2, 1), + // (5,17): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. + // static void use(Span s) { } + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(5, 17), // (5,41): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. // public static implicit operator Span(T[] array) - Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Span").WithArguments("", "System.Span", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "System.Span").WithLocation(5, 41)); + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(5, 41)); - verifier.VerifyIL("", """ + verifier.VerifyIL("", $$""" + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "int[] Program.<
$>g__arr|0_0()" + IL_0005: call "System.{{type}} System.{{type}}.op_Implicit(int[])" + IL_000a: call "void Program.<
$>g__use|0_1(System.{{type}})" + IL_000f: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_MultipleSpans_02( + [CombinatorialValues("Span", "ReadOnlySpan")] string type) + { + string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct {{type}} + { + public static implicit operator {{type}}(T[] array) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["lib"]); + + var source = $$""" + extern alias lib; + lib::System.{{type}} s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "External"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", $$""" { // Code size 12 (0xc) .maxstack 1 IL_0000: call "int[] Program.<
$>g__arr|0_0()" - IL_0005: call "System.Span System.Span.op_Implicit(int[])" + IL_0005: call "System.{{type}} System.{{type}}.op_Implicit(int[])" IL_000a: pop IL_000b: ret } @@ -2087,12 +2346,32 @@ class C static class D { - public static void M(Span xs) + public static void M(Span xs) { } + } + """; + + var missingRosHelper = """ + namespace System + { + public readonly ref struct Span { - foreach (var x in xs) - { - Console.Write(x); - } + public static implicit operator Span(T[] array) => throw null; + } + public readonly ref struct ReadOnlySpan + { + } + } + """; + + var missingSpanHelper = """ + namespace System + { + public readonly ref struct Span + { + } + public readonly ref struct ReadOnlySpan + { + public static implicit operator ReadOnlySpan(T[] array) => throw null; } } """; @@ -2104,33 +2383,24 @@ public static void M(Span xs) Diagnostic(ErrorCode.ERR_BadArgType, "new C()").WithArguments("1", "C", "System.Span").WithLocation(3, 5) }; - verifyWithMissing(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.Regular12, expectedDiagnostics); - verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.Regular12, expectedDiagnostics); + verifyWithMissing(missingRosHelper, TestOptions.Regular12, expectedDiagnostics); + verifyWithMissing(missingSpanHelper, TestOptions.Regular12, expectedDiagnostics); expectedDiagnostics = [ - // (3,5): error CS0656: Missing compiler required member 'System.Span`1.op_Implicit' + // (3,5): error CS0656: Missing compiler required member 'System.Span.op_Implicit' // D.M(new C()); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.Span`1", "op_Implicit").WithLocation(3, 5) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.Span", "op_Implicit").WithLocation(3, 5) ]; - verifyWithMissing(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.RegularNext); - verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.RegularNext, expectedDiagnostics); + verifyWithMissing(missingRosHelper, TestOptions.RegularNext); + verifyWithMissing(missingSpanHelper, TestOptions.RegularNext, expectedDiagnostics); - verifyWithMissing(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.RegularPreview); - verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.RegularPreview, expectedDiagnostics); + verifyWithMissing(missingRosHelper, TestOptions.RegularPreview); + verifyWithMissing(missingSpanHelper, TestOptions.RegularPreview, expectedDiagnostics); - void verifyWithMissing(WellKnownMember member, CSharpParseOptions parseOptions, params DiagnosticDescription[] expected) + void verifyWithMissing(string source2, CSharpParseOptions parseOptions, params DiagnosticDescription[] expected) { - var comp = CreateCompilationWithSpan(source, parseOptions: parseOptions); - comp.MakeMemberMissing(member); - if (expected.Length == 0) - { - CompileAndVerify(comp, expectedOutput: "456").VerifyDiagnostics(); - } - else - { - comp.VerifyDiagnostics(expected); - } + CreateCompilation([source, source2], parseOptions: parseOptions).VerifyDiagnostics(expected); } } @@ -2149,12 +2419,32 @@ class C static class D { - public static void M(ReadOnlySpan xs) + public static void M(ReadOnlySpan xs) { } + } + """; + + var missingRosHelper = """ + namespace System + { + public readonly ref struct Span + { + public static implicit operator Span(T[] array) => throw null; + } + public readonly ref struct ReadOnlySpan { - foreach (var x in xs) - { - Console.Write(x); - } + } + } + """; + + var missingSpanHelper = """ + namespace System + { + public readonly ref struct Span + { + } + public readonly ref struct ReadOnlySpan + { + public static implicit operator ReadOnlySpan(T[] array) => throw null; } } """; @@ -2166,33 +2456,24 @@ public static void M(ReadOnlySpan xs) Diagnostic(ErrorCode.ERR_BadArgType, "new C()").WithArguments("1", "C", "System.ReadOnlySpan").WithLocation(3, 5) }; - verifyWithMissing(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.Regular12, expectedDiagnostics); - verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.Regular12, expectedDiagnostics); + verifyWithMissing(missingRosHelper, TestOptions.Regular12, expectedDiagnostics); + verifyWithMissing(missingSpanHelper, TestOptions.Regular12, expectedDiagnostics); expectedDiagnostics = [ - // (3,5): error CS0656: Missing compiler required member 'System.ReadOnlySpan`1.op_Implicit' + // (3,5): error CS0656: Missing compiler required member 'System.ReadOnlySpan.op_Implicit' // D.M(new C()); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.ReadOnlySpan`1", "op_Implicit").WithLocation(3, 5) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.ReadOnlySpan", "op_Implicit").WithLocation(3, 5) ]; - verifyWithMissing(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.RegularNext, expectedDiagnostics); - verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.RegularNext); + verifyWithMissing(missingRosHelper, TestOptions.RegularNext, expectedDiagnostics); + verifyWithMissing(missingSpanHelper, TestOptions.RegularNext); - verifyWithMissing(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.RegularPreview, expectedDiagnostics); - verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.RegularPreview); + verifyWithMissing(missingRosHelper, TestOptions.RegularPreview, expectedDiagnostics); + verifyWithMissing(missingSpanHelper, TestOptions.RegularPreview); - void verifyWithMissing(WellKnownMember member, CSharpParseOptions parseOptions, params DiagnosticDescription[] expected) + void verifyWithMissing(string source2, CSharpParseOptions parseOptions, params DiagnosticDescription[] expected) { - var comp = CreateCompilationWithSpan(source, parseOptions: parseOptions); - comp.MakeMemberMissing(member); - if (expected.Length == 0) - { - CompileAndVerify(comp, expectedOutput: "456").VerifyDiagnostics(); - } - else - { - comp.VerifyDiagnostics(expected); - } + CreateCompilation([source, source2], parseOptions: parseOptions).VerifyDiagnostics(expected); } } @@ -2313,15 +2594,20 @@ public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_MissingHelper static class C { public static void M(int[] arg) => arg.E(); - public static void E(this Span arg) => Console.Write(arg[1]); + public static void E(this Span arg) { } + } + + namespace System + { + public readonly ref struct Span + { + } } """; - var comp = CreateCompilationWithSpan(source); - comp.MakeMemberMissing(WellKnownMember.System_Span_T__op_Implicit_Array); - comp.VerifyDiagnostics( - // (7,40): error CS0656: Missing compiler required member 'System.Span`1.op_Implicit' + CreateCompilation(source).VerifyDiagnostics( + // (7,40): error CS0656: Missing compiler required member 'System.Span.op_Implicit' // public static void M(int[] arg) => arg.E(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arg").WithArguments("System.Span`1", "op_Implicit").WithLocation(7, 40)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arg").WithArguments("System.Span", "op_Implicit").WithLocation(7, 40)); } [Fact] @@ -2424,6 +2710,23 @@ .maxstack 2 var model = comp.GetSemanticModel(tree); var info = model.GetSymbolInfo(invocation); Assert.Equal("void System.Span.E(System.Int32 x)", info.Symbol!.ToTestDisplayString()); + + var methodSymbol = (IMethodSymbol)info.Symbol!; + var spanType = methodSymbol.ReceiverType!; + Assert.Equal("System.Span", spanType.ToTestDisplayString()); + + // Reduce the extension method with Span receiver. + var unreducedSymbol = methodSymbol.ReducedFrom!; + var reduced = unreducedSymbol.ReduceExtensionMethod(spanType); + Assert.Equal(methodSymbol, reduced); + + var arrayType = comp.GetMember("C.M").GetPublicSymbol().Parameters.Single().Type; + Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); + + // Reduce the extension method with array receiver. + // PROTOTYPE: This needs type inference to work. + reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); + Assert.Null(reduced); } [Fact] @@ -2458,6 +2761,161 @@ .maxstack 2 var model = comp.GetSemanticModel(tree); var info = model.GetSymbolInfo(invocation); Assert.Equal("void System.Span.E(System.Int32 x)", info.Symbol!.ToTestDisplayString()); + + var methodSymbol = (IMethodSymbol)info.Symbol!; + var spanType = methodSymbol.ReceiverType!; + Assert.Equal("System.Span", spanType.ToTestDisplayString()); + + // Reduce the extension method with Span receiver. + var unreducedSymbol = methodSymbol.ReducedFrom!; + var reduced = unreducedSymbol.ReduceExtensionMethod(spanType); + Assert.Equal(methodSymbol, reduced); + + var arrayType = comp.GetMember("C.M").GetPublicSymbol().Parameters.Single().Type; + Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); + + // Reduce the extension method with array receiver. + // PROTOTYPE: This needs type inference to work. + reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); + Assert.Null(reduced); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Generic_05() + { + var source = """ + using System; + static class C + { + public static void M(int[] arg) => arg.E("abc"); + public static void E(this Span arg, T x) { } + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call "System.Span System.Span.op_Implicit(int[])" + IL_0006: ldstr "abc" + IL_000b: call "void C.E(System.Span, string)" + IL_0010: ret + } + """); + + var tree = comp.SyntaxTrees.Single(); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("""arg.E("abc")""", invocation.ToString()); + + var model = comp.GetSemanticModel(tree); + var info = model.GetSymbolInfo(invocation); + Assert.Equal("void System.Span.E(System.String x)", info.Symbol!.ToTestDisplayString()); + + var methodSymbol = (IMethodSymbol)info.Symbol!; + var spanType = methodSymbol.ReceiverType!; + Assert.Equal("System.Span", spanType.ToTestDisplayString()); + + // Reduce the extension method with Span receiver. + var unreducedSymbol = methodSymbol.ReducedFrom!; + var reduced = unreducedSymbol.ReduceExtensionMethod(spanType); + Assert.Equal("void System.Span.E(T x)", reduced.ToTestDisplayString()); + + var arrayType = comp.GetMember("C.M").GetPublicSymbol().Parameters.Single().Type; + Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); + + // Reduce the extension method with array receiver. + reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); + Assert.Equal("void System.Span.E(T x)", reduced.ToTestDisplayString()); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Reduced_01() + { + var source = """ + using System; + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this Span arg) { } + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "System.Span System.Span.op_Implicit(int[])" + IL_0006: call "void C.E(System.Span)" + IL_000b: ret + } + """); + + var tree = comp.SyntaxTrees.Single(); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("arg.E()", invocation.ToString()); + + var model = comp.GetSemanticModel(tree); + var info = model.GetSymbolInfo(invocation); + Assert.Equal("void System.Span.E()", info.Symbol!.ToTestDisplayString()); + + var methodSymbol = (IMethodSymbol)info.Symbol!; + var spanType = methodSymbol.ReceiverType!; + Assert.Equal("System.Span", spanType.ToTestDisplayString()); + + // Reduce the extension method with Span receiver. + var unreducedSymbol = methodSymbol.ReducedFrom!; + var reduced = unreducedSymbol.ReduceExtensionMethod(spanType); + Assert.Equal("void System.Span.E()", reduced.ToTestDisplayString()); + + var arrayType = comp.GetMember("C.M").GetPublicSymbol().Parameters.Single().Type; + Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); + + // Reduce the extension method with array receiver. + reduced = unreducedSymbol.ReduceExtensionMethod(arrayType); + Assert.Equal("void System.Span.E()", reduced.ToTestDisplayString()); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Reduced_01_CSharp12() + { + var source = """ + using System; + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this Span arg) { } + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,40): error CS1929: 'int[]' does not contain a definition for 'E' and the best extension method overload 'C.E(Span)' requires a receiver of type 'System.Span' + // public static void M(int[] arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("int[]", "E", "C.E(System.Span)", "System.Span").WithLocation(4, 40)); + + var tree = comp.SyntaxTrees.Single(); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("arg.E()", invocation.ToString()); + + var model = comp.GetSemanticModel(tree); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + + var methodSymbol = comp.GetMember("C.E").GetPublicSymbol(); + var spanType = methodSymbol.Parameters.Single().Type; + Assert.Equal("System.Span", spanType.ToTestDisplayString()); + + // Reduce the extension method with Span receiver. + var reduced = methodSymbol.ReduceExtensionMethod(spanType); + Assert.Equal("void System.Span.E()", reduced.ToTestDisplayString()); + + var arrayType = comp.GetMember("C.M").GetPublicSymbol().Parameters.Single().Type; + Assert.Equal("System.Int32[]", arrayType.ToTestDisplayString()); + + // Reduce the extension method with array receiver. + reduced = methodSymbol.ReduceExtensionMethod(arrayType); + Assert.Equal("void System.Span.E()", reduced.ToTestDisplayString()); } [Theory, MemberData(nameof(LangVersions))] @@ -2875,8 +3333,8 @@ static class C CompileAndVerify(comp, expectedOutput: "aa rSystem.Object[] ra ra").VerifyDiagnostics(); } - [Fact] - public void OverloadResolution_ReadOnlySpanVsArray_05() + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_05(LanguageVersion langVersion) { var source = """ using System; @@ -2890,23 +3348,8 @@ static class C public static void M(ReadOnlySpan x) => Console.Write(2); } """; - var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12); + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); CompileAndVerify(comp, expectedOutput: "11").VerifyDiagnostics(); - - // PROTOTYPE: Need to consider "implicit span conversion" in "better conversion target" for this to work. - - var expectedDiagnostics = new[] - { - // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(object[])' and 'C.M(ReadOnlySpan)' - // C.M(null); - Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(object[])", "C.M(System.ReadOnlySpan)").WithLocation(3, 3), - // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(object[])' and 'C.M(ReadOnlySpan)' - // C.M(default); - Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(object[])", "C.M(System.ReadOnlySpan)").WithLocation(4, 3) - }; - - CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); - CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -3017,16 +3460,8 @@ static class C public static void M(params ReadOnlySpan x) => Console.Write(2); } """; - - // PROTOTYPE: Need to consider "implicit span conversion" in "better conversion target" for this to work. - - CreateCompilationWithSpan(source).VerifyDiagnostics( - // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(params object[])' and 'C.M(params ReadOnlySpan)' - // C.M(null); - Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(params object[])", "C.M(params System.ReadOnlySpan)").WithLocation(3, 3), - // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(params object[])' and 'C.M(params ReadOnlySpan)' - // C.M(default); - Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(params object[])", "C.M(params System.ReadOnlySpan)").WithLocation(4, 3)); + var comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: "11").VerifyDiagnostics(); } [Theory, MemberData(nameof(LangVersions))] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 25a04c445d097..42b3d2083af4e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -957,14 +957,12 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_Span_T__get_Item: case WellKnownMember.System_Span_T__get_Length: case WellKnownMember.System_Span_T__Slice_Int_Int: - case WellKnownMember.System_Span_T__op_Implicit_Array: case WellKnownMember.System_ReadOnlySpan_T__ctor_Pointer: case WellKnownMember.System_ReadOnlySpan_T__ctor_Array: case WellKnownMember.System_ReadOnlySpan_T__ctor_Array_Start_Length: case WellKnownMember.System_ReadOnlySpan_T__get_Item: case WellKnownMember.System_ReadOnlySpan_T__get_Length: case WellKnownMember.System_ReadOnlySpan_T__Slice_Int_Int: - case WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array: case WellKnownMember.System_Index__ctor: case WellKnownMember.System_Index__GetOffset: case WellKnownMember.System_Range__ctor: diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 0b45290ccf522..1cdddef0d9097 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -483,7 +483,6 @@ internal enum WellKnownMember System_Span_T__get_Item, System_Span_T__get_Length, System_Span_T__Slice_Int_Int, - System_Span_T__op_Implicit_Array, System_ReadOnlySpan_T__ctor_Pointer, System_ReadOnlySpan_T__ctor_Array, @@ -492,7 +491,6 @@ internal enum WellKnownMember System_ReadOnlySpan_T__get_Item, System_ReadOnlySpan_T__get_Length, System_ReadOnlySpan_T__Slice_Int_Int, - System_ReadOnlySpan_T__op_Implicit_Array, System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 0f76bbf06dd77..0fd77f299f9c2 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3369,17 +3369,6 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - // System_Span_T__op_Implicit_Array - (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Span_T - WellKnownType.ExtSentinel), // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, - (byte)WellKnownType.ExtSentinel, (WellKnownType.System_Span_T - WellKnownType.ExtSentinel), - 1, - (byte)SignatureTypeCode.GenericTypeParameter, (byte)0, // Return Type - (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericTypeParameter, 0, - // System_ReadOnlySpan_T__ctor_Pointer (byte)(MemberFlags.Constructor), // Flags (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), // DeclaringTypeId @@ -3442,17 +3431,6 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - // System_ReadOnlySpan_T__op_Implicit_Array - (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, - (byte)WellKnownType.ExtSentinel, (WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), - 1, - (byte)SignatureTypeCode.GenericTypeParameter, (byte)0, // Return Type - (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericTypeParameter, 0, - // System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor (byte)(MemberFlags.Constructor), // Flags (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_IsUnmanagedAttribute - WellKnownType.ExtSentinel), // DeclaringTypeId @@ -4764,7 +4742,6 @@ static WellKnownMembers() "get_Item", // System_Span_T__get_Item "get_Length", // System_Span_T__get_Length "Slice", // System_Span_T__Slice_Int_Int - "op_Implicit", // System_Span_T__op_Implicit_Array ".ctor", // System_ReadOnlySpan_T__ctor_Pointer ".ctor", // System_ReadOnlySpan_T__ctor_Array ".ctor", // System_ReadOnlySpan_T__ctor_Array_Start_Length @@ -4772,7 +4749,6 @@ static WellKnownMembers() "get_Item", // System_ReadOnlySpan_T__get_Item "get_Length", // System_ReadOnlySpan_T__get_Length "Slice", // System_ReadOnlySpan_T__Slice_Int_Int - "op_Implicit", // System_ReadOnlySpan_T__op_Implicit_Array ".ctor", // System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor "Fix", // Microsoft_VisualBasic_Conversion__FixSingle diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 0a8e5b8da33e9..798d74ecf6e0e 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -707,14 +707,12 @@ End Namespace WellKnownMember.System_Span_T__get_Item, WellKnownMember.System_Span_T__get_Length, WellKnownMember.System_Span_T__Slice_Int_Int, - WellKnownMember.System_Span_T__op_Implicit_Array, WellKnownMember.System_ReadOnlySpan_T__ctor_Pointer, WellKnownMember.System_ReadOnlySpan_T__ctor_Array, WellKnownMember.System_ReadOnlySpan_T__ctor_Array_Start_Length, WellKnownMember.System_ReadOnlySpan_T__get_Item, WellKnownMember.System_ReadOnlySpan_T__get_Length, WellKnownMember.System_ReadOnlySpan_T__Slice_Int_Int, - WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, WellKnownMember.System_Runtime_CompilerServices_AsyncIteratorStateMachineAttribute__ctor, WellKnownMember.System_IAsyncDisposable__DisposeAsync, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, @@ -916,14 +914,12 @@ End Namespace WellKnownMember.System_Span_T__get_Item, WellKnownMember.System_Span_T__get_Length, WellKnownMember.System_Span_T__Slice_Int_Int, - WellKnownMember.System_Span_T__op_Implicit_Array, WellKnownMember.System_ReadOnlySpan_T__ctor_Pointer, WellKnownMember.System_ReadOnlySpan_T__ctor_Array, WellKnownMember.System_ReadOnlySpan_T__ctor_Array_Start_Length, WellKnownMember.System_ReadOnlySpan_T__get_Item, WellKnownMember.System_ReadOnlySpan_T__get_Length, WellKnownMember.System_ReadOnlySpan_T__Slice_Int_Int, - WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, WellKnownMember.System_Runtime_CompilerServices_AsyncIteratorStateMachineAttribute__ctor, WellKnownMember.System_IAsyncDisposable__DisposeAsync, WellKnownMember.System_Collections_Generic_IAsyncEnumerable_T__GetAsyncEnumerator, diff --git a/src/Workspaces/CoreTest/XxHash128Tests.cs b/src/Workspaces/CoreTest/XxHash128Tests.cs index df6040457600c..755b2a9650e36 100644 --- a/src/Workspaces/CoreTest/XxHash128Tests.cs +++ b/src/Workspaces/CoreTest/XxHash128Tests.cs @@ -20,8 +20,7 @@ public class XxHash128Tests public void Hash_InvalidInputs_Throws() { Assert.Throws("source", () => XxHash128.Hash(null)); - // PROTOTYPE: Should make this work with `null` instead of `default(byte[])`. - Assert.Throws("source", () => XxHash128.Hash(default(byte[]), 42)); + Assert.Throws("source", () => XxHash128.Hash(null, 42)); Assert.Throws("destination", () => XxHash128.Hash(new byte[] { 1, 2, 3 }, new byte[7])); }