diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 6f24e552a0ed7..f008274832cb4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -484,6 +484,19 @@ void checkConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNode syn CheckInlineArrayTypeIsSupported(syntax, source.Type, elementField.Type, diagnostics); } + else if (conversion.IsSpan) + { + // 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)) + { + _ = GetWellKnownTypeMember(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, diagnostics, syntax: 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); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 8a32d3821bb6f..572b1e37ed34a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1241,7 +1241,7 @@ private BoundCall BindInvocationExpressionContinued( ParameterSymbol receiverParameter = method.Parameters.First(); // we will have a different receiver if ReplaceTypeOrValueReceiver has unwrapped TypeOrValue - if ((object)receiver != receiverArgument) + if ((object)receiver != methodGroup.Receiver) { // Because the receiver didn't pass through CoerceArguments, we need to apply an appropriate conversion here. Debug.Assert(argsToParams.IsDefault || argsToParams[0] == 0); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index 96888f4a2ff91..4a298445c97db 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -244,6 +244,7 @@ private static void AssertTrivialConversion(ConversionKind kind) case ConversionKind.InterpolatedString: case ConversionKind.InterpolatedStringHandler: case ConversionKind.InlineArray: + case ConversionKind.ImplicitSpan: isTrivial = true; break; @@ -294,6 +295,7 @@ internal static Conversion GetTrivialConversion(ConversionKind kind) internal static Conversion ImplicitPointer => new Conversion(ConversionKind.ImplicitPointer); internal static Conversion FunctionType => new Conversion(ConversionKind.FunctionType); internal static Conversion InlineArray => new Conversion(ConversionKind.InlineArray); + internal static Conversion ImplicitSpan => new Conversion(ConversionKind.ImplicitSpan); // trivial conversions that could be underlying in nullable conversion // NOTE: tuple conversions can be underlying as well, but they are not trivial @@ -819,6 +821,20 @@ public bool IsReference } } + /// + /// Returns true if the conversion is a span conversion. + /// + /// + /// Span conversion is available since C# 13 as part of the "first-class Span types" feature. + /// + internal bool IsSpan // PROTOTYPE: Make part of public API + { + get + { + return Kind == ConversionKind.ImplicitSpan; + } + } + /// /// Returns true if the conversion is an implicit user-defined conversion or explicit user-defined conversion. /// diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs index 69ad0370f5ca2..b72eeca287d21 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs @@ -68,5 +68,7 @@ internal enum ConversionKind : byte InterpolatedStringHandler, // A conversion from an interpolated string literal to a type attributed with InterpolatedStringBuilderAttribute InlineArray, // A conversion from an inline array to Span/ReadOnlySpan + + ImplicitSpan, // A conversion between array, (ReadOnly)Span, string - part of the "first-class Span types" feature } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs index e0d3c4578f7b6..10a6f6c44b291 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs @@ -53,6 +53,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind) case ObjectCreation: case InlineArray: case CollectionExpression: + case ImplicitSpan: return true; case ExplicitNumeric: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 28c1c79ac1461..a3ca8288b09f1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -602,6 +602,7 @@ private static bool IsStandardImplicitConversionFromExpression(ConversionKind ki case ConversionKind.StackAllocToPointerType: case ConversionKind.StackAllocToSpanType: case ConversionKind.InlineArray: + case ConversionKind.ImplicitSpan: return true; default: return false; @@ -1014,6 +1015,11 @@ 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) @@ -1916,6 +1922,11 @@ public Conversion ClassifyImplicitExtensionMethodThisArgConversion(BoundExpressi { return Conversion.ImplicitReference; } + + if (HasImplicitSpanConversion(sourceType, destination, ref useSiteInfo)) + { + return Conversion.ImplicitSpan; + } } if (sourceExpressionOpt?.Kind == BoundKind.TupleLiteral) @@ -1977,6 +1988,7 @@ public static bool IsValidExtensionMethodThisArgConversion(Conversion conversion case ConversionKind.Identity: case ConversionKind.Boxing: case ConversionKind.ImplicitReference: + case ConversionKind.ImplicitSpan: return true; case ConversionKind.ImplicitTuple: @@ -3025,7 +3037,7 @@ internal bool HasImplicitConversionToOrImplementsVarianceCompatibleInterface(Bou // The rules for variant interface and delegate conversions are the same: // // An interface/delegate type S is convertible to an interface/delegate type T - // if and only if T is U and T is U such that for all + // if and only if S is U and T is U such that for all // parameters of U: // // * if the ith parameter of U is invariant then Si is exactly equal to Ti. @@ -3908,5 +3920,46 @@ private static bool IsIntegerTypeSupportingPointerConversions(TypeSymbol type) return 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) + { + return false; + } + + // SPEC: From any single-dimensional `array_type` with element type `Ei`... + if (source is ArrayTypeSymbol { IsSZArray: true, ElementTypeWithAnnotations: { } elementType }) + { + // SPEC: ...to `System.Span`. + if (destination.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.AllIgnoreOptions)) + { + 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)) + { + var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; + return hasCovariantConversion(elementType, spanElementType, ref useSiteInfo); + } + } + + return false; + + bool hasCovariantConversion(TypeWithAnnotations source, TypeWithAnnotations destination, ref CompoundUseSiteInfo useSiteInfo) + { + return hasIdentityConversion(source, destination) || + HasImplicitReferenceConversion(source, destination, ref useSiteInfo); + } + + bool hasIdentityConversion(TypeWithAnnotations source, TypeWithAnnotations destination) + { + return HasIdentityConversionInternal(source.Type, destination.Type) && + HasTopLevelNullabilityIdentityConversion(source, destination); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs index 06f7ae0686a9b..d92bc7d996754 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/UserDefinedImplicitConversions.cs @@ -666,6 +666,8 @@ private static bool IsEncompassingImplicitConversionKind(ConversionKind kind) case ConversionKind.ImplicitPointer: // Added for C# 12 case ConversionKind.InlineArray: + // Added for C# 13 + case ConversionKind.ImplicitSpan: return true; default: diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 4c09a80debc1a..53b2b2adac17b 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7956,4 +7956,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ foreach statement cannot operate on enumerators of type '{0}' because it is a type parameter that allows ref struct and it is not known at compile time to implement IDisposable. + + first-class Span types + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 18fa9dbb51d6e..07ec814b1a908 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -286,6 +286,8 @@ internal enum MessageID IDS_FeatureRefUnsafeInIteratorAsync = MessageBase + 12843, IDS_FeatureRefStructInterfaces = MessageBase + 12844, + + IDS_FeatureFirstClassSpan = MessageBase + 12845, } // Message IDs may refer to strings that need to be localized. @@ -472,6 +474,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureParamsCollections: case MessageID.IDS_FeatureRefUnsafeInIteratorAsync: case MessageID.IDS_FeatureRefStructInterfaces: + case MessageID.IDS_FeatureFirstClassSpan: return LanguageVersion.Preview; // C# 12.0 features. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index c139e3daf28c4..09bcd2e580f8e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8890,6 +8890,7 @@ private TypeWithState VisitConversion( break; case ConversionKind.InlineArray: + case ConversionKind.ImplicitSpan: if (checkConversion) { conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument, isChecked: conversionOpt?.Checked ?? false); diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index 1f401b2ef6eff..ca544bb53940a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -811,6 +811,8 @@ public override BoundNode VisitConversion(BoundConversion node) } break; + // PROTOTYPE: ImplicitSpan + default: if (_inExpressionLambda && node.Conversion.Method is MethodSymbol method && (method.IsAbstract || method.IsVirtual) && method.IsStatic) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 4f6b4f96c378d..68148b34f2678 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -614,6 +614,40 @@ private BoundExpression MakeConversionNodeCore( return _factory.Call(null, createSpan, rewrittenOperand, _factory.Literal(length), useStrictArgumentRefKinds: true); } + case ConversionKind.ImplicitSpan: + { + 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)) + { + throw ExceptionUtilities.Unreachable(); + } + else + { + MethodSymbol method = symbol.AsMember(spanType); + + rewrittenOperand = _factory.Convert(method.ParameterTypesWithAnnotations[0].Type, rewrittenOperand); + + if (member == WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array) + { + return new BoundReadOnlySpanFromArray(syntax, rewrittenOperand, method, spanType) { WasCompilerGenerated = true }; + } + + return _factory.Call(null, method, rewrittenOperand); + } + } + default: break; } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 482f74b8c1b7e..43efba95fb988 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2317,6 +2317,11 @@ typy souborů + + first-class Span types + first-class Span types + + generic attributes obecné atributy diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 458820646f0bd..fa820535f23e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2317,6 +2317,11 @@ Dateitypen + + first-class Span types + first-class Span types + + generic attributes Generische Attribute diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index de96942b43ca1..b00d32316581c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2317,6 +2317,11 @@ tipos de archivo + + first-class Span types + first-class Span types + + generic attributes atributos genéricos diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index cd8e3b4571564..b9dcdc4eea745 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2317,6 +2317,11 @@ types de fichier + + first-class Span types + first-class Span types + + generic attributes attributs génériques diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ee6c972ccf03d..a74a5e663019b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2317,6 +2317,11 @@ tipi di file + + first-class Span types + first-class Span types + + generic attributes attributi generici diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 049964d81ce3a..550addc69a38c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2317,6 +2317,11 @@ ファイルの種類 + + first-class Span types + first-class Span types + + generic attributes 汎用属性 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index dd8ef848b9a66..f7cb69c9bebe7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2317,6 +2317,11 @@ 파일 형식 + + first-class Span types + first-class Span types + + generic attributes 제네릭 특성 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 871593fc5cad5..52289e76246a0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2317,6 +2317,11 @@ typy plików + + first-class Span types + first-class Span types + + generic attributes atrybuty ogólne diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index a4f21ff34b491..2d0cff5f73cc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2317,6 +2317,11 @@ tipos de arquivo + + first-class Span types + first-class Span types + + generic attributes atributos genéricos diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 7bc750767181d..714d9b412b217 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2317,6 +2317,11 @@ типы файлов + + first-class Span types + first-class Span types + + generic attributes универсальные атрибуты diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 61a78dded475d..c485c0a4c9ec8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2317,6 +2317,11 @@ dosya türleri + + first-class Span types + first-class Span types + + generic attributes genel öznitelikler diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index b0175426fa302..6f78c4575cbd3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2317,6 +2317,11 @@ 文件类型 + + first-class Span types + first-class Span types + + generic attributes 通用属性 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 59932f5945760..f0998b8629821 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2317,6 +2317,11 @@ 檔案類型 + + first-class Span types + first-class Span types + + generic attributes 一般屬性 diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs index 6faa5984d11a6..56db7fd8ba725 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs @@ -38,7 +38,7 @@ public void DiagnosticAnalyzerAllInOne() missingSyntaxKinds.Add(SyntaxKind.SpreadElement); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); - var options = new AnalyzerOptions(new[] { new TestAdditionalText() }.ToImmutableArray()); + var options = new AnalyzerOptions([new TestAdditionalText()]); CreateCompilationWithMscorlib45(source).VerifyAnalyzerDiagnostics(new[] { analyzer }, options); analyzer.VerifyAllAnalyzerMembersWereCalled(); analyzer.VerifyAnalyzeSymbolCalledForAllSymbolKinds(); @@ -99,7 +99,7 @@ public class C public void AnalyzerDriverIsSafeAgainstAnalyzerExceptions() { var compilation = CreateCompilationWithMscorlib45(TestResource.AllInOneCSharpCode); - var options = new AnalyzerOptions(new[] { new TestAdditionalText() }.ToImmutableArray()); + var options = new AnalyzerOptions([new TestAdditionalText()]); ThrowingDiagnosticAnalyzer.VerifyAnalyzerEngineIsSafeAgainstExceptions(analyzer => compilation.GetAnalyzerDiagnostics(new[] { analyzer }, options)); @@ -111,7 +111,7 @@ public void AnalyzerOptionsArePassedToAllAnalyzers() var text = new StringText(string.Empty, encodingOpt: null); AnalyzerOptions options = new AnalyzerOptions ( - new[] { new TestAdditionalText("myfilepath", text) }.ToImmutableArray() + [new TestAdditionalText("myfilepath", text)] ); var compilation = CreateCompilationWithMscorlib45(TestResource.AllInOneCSharpCode); diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_Scope.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_Scope.cs index 91a7947e41648..e82d618649ff1 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_Scope.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests_Scope.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs new file mode 100644 index 0000000000000..1719aad335ee1 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -0,0 +1,2882 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public class FirstClassSpanTests : CSharpTestBase +{ + public static TheoryData LangVersions() + { + return new TheoryData() + { + LanguageVersion.CSharp12, + LanguageVersionFacts.CSharpNext, + LanguageVersion.Preview, + }; + } + + private sealed class CombinatorialLangVersions() + : CombinatorialValuesAttribute(LangVersions().Select(d => d.Single()).ToArray()); + + [Fact, WorkItem("https://github.com/dotnet/runtime/issues/101261")] + public void Example_StringValuesAmbiguity() + { + var source = """ + using System; + + Console.Write(C.M(new StringValues())); + + static class C + { + public static string M(StringValues sv) => StringExtensions.Join(",", sv); + } + + static class StringExtensions + { + public static string Join(string separator, params string[] values) => "array"; + public static string Join(string separator, params ReadOnlySpan values) => "span"; + } + + readonly struct StringValues + { + public static implicit operator string(StringValues values) => null; + public static implicit operator string[](StringValues value) => null; + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (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), + // (13,49): error CS8652: The feature 'params collections' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string Join(string separator, params ReadOnlySpan values) => "span"; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "params ReadOnlySpan values").WithArguments("params collections").WithLocation(13, 49)); + + 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 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); + } + + [Fact] + public void BreakingChange_Inheritance_UserDefinedConversion_ArrayToSpan() + { + var source = """ + using System; + + var a = new string[0]; + var d = new Derived(); + d.M(a); + + class Base + { + public void M(Span s) => Console.Write("Base"); + } + + class Derived : Base + { + public static implicit operator Derived(Span r) => new Derived(); + + public void M(Derived s) => Console.WriteLine("Derived"); + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12); + CompileAndVerify(comp, expectedOutput: "Base").VerifyDiagnostics(); + + var expectedOutput = "Derived"; + + comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void BreakingChange_ExtensionMethodLookup_ArrayToSpan() + { + var source = """ + using System; + + namespace N1 + { + using N2; + + public class C + { + public static void Main() + { + var a = new string[0]; + a.Test(); + } + } + + public static class N1Ext + { + public static void Test(this Span x) => Console.WriteLine("N1"); + } + } + + namespace N2 + { + public static class N2Ext + { + public static void Test(this string[] x) => Console.WriteLine("N2"); + } + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "N2").VerifyDiagnostics(); + + var expectedOutput = "N1"; + + var expectedDiagnostics = new[] + { + // (5,5): hidden CS8019: Unnecessary using directive. + // using N2; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N2;").WithLocation(5, 5) + }; + + comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithSpan(source, TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void BreakingChange_ExtensionMethodLookup_SpanVsIEnumerable() + { + var source = """ + using System; + using System.Collections.Generic; + + var arr = new char[] { '/' }; + arr.M('/'); + + static class E + { + public static void M(this Span s, T x) => Console.Write(1); + public static void M(this IEnumerable e, T x) => Console.Write(2); + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (5,5): error CS0121: The call is ambiguous between the following methods or properties: 'E.M(Span, T)' and 'E.M(IEnumerable, T)' + // arr.M('/'); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E.M(System.Span, T)", "E.M(System.Collections.Generic.IEnumerable, T)").WithLocation(5, 5) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void BreakingChange_ExtensionMethodLookup_SpanVsIEnumerable_Workaround(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Collections.Generic; + + var arr = new char[] { '/' }; + arr.M('/'); + + static class E + { + public static void M(this Span s, T x) => Console.Write(1); + public static void M(this IEnumerable e, T x) => Console.Write(2); + public static void M(this T[] a, T x) => Console.Write(3); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: "3").VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 19 (0x13) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr "char" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.s 47 + IL_000a: stelem.i2 + IL_000b: ldc.i4.s 47 + IL_000d: call "void E.M(char[], char)" + IL_0012: ret + } + """); + } + + [Fact] + public void BreakingChange_ExtensionMethodLookup_SpanVsIEnumerable_MethodConversion() + { + var source = """ + using System; + using System.Collections.Generic; + + var arr = new int[] { 123 }; + E.R(arr.M); + + static class E + { + public static void R(Action a) => a(-1); + public static void M(this Span s, T x) => Console.Write(1); + public static void M(this IEnumerable e, T x) => Console.Write(2); + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (5,5): error CS0121: The call is ambiguous between the following methods or properties: 'E.M(Span, T)' and 'E.M(IEnumerable, T)' + // E.R(arr.M); + Diagnostic(ErrorCode.ERR_AmbigCall, "arr.M").WithArguments("E.M(System.Span, T)", "E.M(System.Collections.Generic.IEnumerable, T)").WithLocation(5, 5) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void BreakingChange_ExtensionMethodLookup_SpanVsIEnumerable_MethodConversion_Workaround(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Collections.Generic; + + var arr = new int[] { 123 }; + E.R(arr.M); + + static class E + { + public static void R(Action a) => a(-1); + public static void M(this Span s, T x) => Console.Write(1); + public static void M(this IEnumerable e, T x) => Console.Write(2); + public static void M(this T[] a, T x) => Console.Write(3); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "3").VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit( + [CombinatorialLangVersions] LanguageVersion langVersion, + [CombinatorialValues("Span", "ReadOnlySpan")] string destination, + bool cast) + { + var source = $$""" + using System; + {{destination}} s = {{(cast ? $"({destination})" : "")}}arr(); + report(s); + static int[] arr() => new int[] { 1, 2, 3 }; + static void report({{destination}} s) { foreach (var x in s) { Console.Write(x); } } + """; + + var expectedOutput = "123"; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", $$""" + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "int[] Program.<
$>g__arr|0_0()" + IL_0005: call "System.{{destination}} System.{{destination}}.op_Implicit(int[])" + IL_000a: call "void Program.<
$>g__report|0_1(System.{{destination}})" + IL_000f: ret + } + """); + } + + [Fact] + public void Conversion_Array_Span_Implicit_MissingHelper() + { + var source = """ + using System; + Span s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + """; + + 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' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span`1", "op_Implicit").WithLocation(2, 15)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_ConstantData(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M1(new[] { 1 }); + C.M2(new[] { 2 }); + + static class C + { + public static void M1(Span s) => Console.Write(s[0]); + public static void M2(ReadOnlySpan s) => Console.Write(s[0]); + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: "12").VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 63 (0x3f) + .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: ldsfld "int[] .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE_A6" + IL_0019: dup + IL_001a: brtrue.s IL_0034 + IL_001c: pop + IL_001d: ldc.i4.1 + IL_001e: newarr "int" + IL_0023: dup + IL_0024: ldtoken "int .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE" + IL_0029: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_002e: dup + IL_002f: stsfld "int[] .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE_A6" + IL_0034: newobj "System.ReadOnlySpan..ctor(int[])" + IL_0039: call "void C.M2(System.ReadOnlySpan)" + IL_003e: ret + } + """); + } + + [Fact] + public void Conversion_Array_Span_Implicit_SpanTwice() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct Span + { + public static implicit operator Span(T[] array) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(); + + var source = """ + using System; + Span 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: "Internal"); + 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), + // (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)); + + 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_000a: pop + IL_000b: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_SemanticModel_01( + [CombinatorialValues("Span", "ReadOnlySpan")] string destination) + { + var source = $$""" + class C + { + System.{{destination}} M(int[] arg) { return arg; } + } + """; + + var comp = CreateCompilationWithSpan(source); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var arg = tree.GetRoot().DescendantNodes().OfType().Single().Expression; + Assert.Equal("arg", arg!.ToString()); + + var argType = model.GetTypeInfo(arg); + Assert.Equal("System.Int32[]", argType.Type.ToTestDisplayString()); + Assert.Equal($"System.{destination}", argType.ConvertedType.ToTestDisplayString()); + + var argConv = model.GetConversion(arg); + Assert.Equal(ConversionKind.ImplicitSpan, argConv.Kind); + Assert.True(argConv.IsSpan); + Assert.True(argConv.IsImplicit); + Assert.False(argConv.IsUserDefined); + Assert.False(argConv.IsIdentity); + } + + [Fact] + public void Conversion_Array_Span_Implicit_SemanticModel_02() + { + var source = """ + using System; + class C + where T : class + where U : class, T + { + ReadOnlySpan F1(T[] x) => (ReadOnlySpan)x; + ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + } + """; + + var comp = CreateCompilationWithSpan(source); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var casts = tree.GetRoot().DescendantNodes().OfType().ToImmutableArray(); + Assert.Equal(2, casts.Length); + + { + var cast = casts[0]; + Assert.Equal("(ReadOnlySpan)x", cast.ToString()); + + var op = (IConversionOperation)model.GetOperation(cast)!; + var conv = op.GetConversion(); + Assert.Equal(ConversionKind.ImplicitSpan, conv.Kind); + + model.VerifyOperationTree(cast, """ + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.ReadOnlySpan) (Syntax: '(ReadOnlySpan)x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x') + """); + } + + { + var cast = casts[1]; + Assert.Equal("(ReadOnlySpan)x", cast.ToString()); + + var op = (IConversionOperation)model.GetOperation(cast)!; + var conv = op.GetConversion(); + Assert.Equal(ConversionKind.ExplicitUserDefined, conv.Kind); + + model.VerifyOperationTree(cast, """ + IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(U[] array)) (OperationKind.Conversion, Type: System.ReadOnlySpan) (Syntax: '(ReadOnlySpan)x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(U[] array)) + Operand: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: U[], IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T[]) (Syntax: 'x') + """); + } + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + class C + { + System.Span M(int[] arg) => arg; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,41): error CS0029: Cannot implicitly convert type 'int[]' to 'System.Span' + // System.Span M(int[] arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("int[]", "System.Span").WithLocation(3, 41)); + } + + // PROTOTYPE: Revisit all nullable analysis tests. + [Fact] + public void Conversion_Array_Span_Implicit_NullableAnalysis() + { + var source = """ + #nullable enable + using System; + class C + { + Span M1(string[] arg) => arg; + Span M2(string?[] arg) => arg; + Span M3(string[] arg) => arg; + Span M4(string?[] arg) => arg; + + Span M5(string?[] arg) => (Span)arg; + Span M6(string?[] arg) => (Span)arg; + Span M7(string[] arg) => (Span)arg; + Span M8(string[] arg) => (Span)arg; + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,39): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // Span M2(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "string[]").WithLocation(6, 39), + // (10,39): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // Span M5(string?[] arg) => (Span)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span)arg").WithArguments("string?[]", "string[]").WithLocation(10, 39), + // (11,39): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'Span'. + // Span M6(string?[] arg) => (Span)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span)arg").WithArguments("System.Span", "System.Span").WithLocation(11, 39), + // (12,39): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'Span'. + // Span M7(string[] arg) => (Span)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span)arg").WithArguments("System.Span", "System.Span").WithLocation(12, 39)); + + var expectedDiagnostics = new[] + { + // (6,39): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'Span'. + // Span M2(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "System.Span").WithLocation(6, 39), + // (11,39): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'Span'. + // Span M6(string?[] arg) => (Span)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span)arg").WithArguments("System.Span", "System.Span").WithLocation(11, 39), + // (12,39): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'Span'. + // Span M7(string[] arg) => (Span)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span)arg").WithArguments("System.Span", "System.Span").WithLocation(12, 39) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_Array_ReadOnlySpan_Implicit_NullableAnalysis() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan M1(string[] arg) => arg; + ReadOnlySpan M2(string?[] arg) => arg; + ReadOnlySpan M3(string[] arg) => arg; + ReadOnlySpan M4(string?[] arg) => arg; + ReadOnlySpan M5(string?[] arg) => arg; + ReadOnlySpan M6(string?[] arg) => arg; + + ReadOnlySpan M7(string?[] arg) => (ReadOnlySpan)arg; + ReadOnlySpan M8(object?[] arg) => (ReadOnlySpan)arg; + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // ReadOnlySpan M2(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "string[]").WithLocation(6, 47), + // (9,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // ReadOnlySpan M5(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "string[]").WithLocation(9, 47), + // (10,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'object[]'. + // ReadOnlySpan M6(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "object[]").WithLocation(10, 47), + // (12,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'string[]'. + // ReadOnlySpan M7(string?[] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("string?[]", "string[]").WithLocation(12, 47), + // (13,47): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'string[]'. + // ReadOnlySpan M8(object?[] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[]", "string[]").WithLocation(13, 47)); + + var expectedDiagnostics = new[] + { + // (6,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M2(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "System.ReadOnlySpan").WithLocation(6, 47), + // (9,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M5(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "System.ReadOnlySpan").WithLocation(9, 47), + // (10,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M6(string?[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[]", "System.ReadOnlySpan").WithLocation(10, 47), + // (13,47): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'string[]'. + // ReadOnlySpan M8(object?[] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[]", "string[]").WithLocation(13, 47) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_Array_Span_Implicit_NullableAnalysis_NestedArrays() + { + var source = """ + #nullable enable + using System; + class C + { + Span M1(string[][] arg) => arg; + Span M2(string?[][] arg) => arg; + Span M3(string[][] arg) => arg; + Span M4(string?[][] arg) => arg; + + Span M5(string?[][] arg) => (Span)arg; + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,43): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'string[][]'. + // Span M2(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "string[][]").WithLocation(6, 43), + // (10,43): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'string[][]'. + // Span M5(string?[][] arg) => (Span)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span)arg").WithArguments("string?[][]", "string[][]").WithLocation(10, 43)); + + var expectedDiagnostics = new[] + { + // (6,43): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'Span'. + // Span M2(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "System.Span").WithLocation(6, 43) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_Array_ReadOnlySpan_Implicit_NullableAnalysis_NestedArrays() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan M1(string[][] arg) => arg; + ReadOnlySpan M2(string?[][] arg) => arg; + ReadOnlySpan M3(string[][] arg) => arg; + ReadOnlySpan M4(string?[][] arg) => arg; + ReadOnlySpan M5(string?[][] arg) => arg; + ReadOnlySpan M6(string?[][] arg) => arg; + + ReadOnlySpan M7(string?[][] arg) => (ReadOnlySpan)arg; + ReadOnlySpan M8(object?[][] arg) => (ReadOnlySpan)arg; + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'string[][]'. + // ReadOnlySpan M2(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "string[][]").WithLocation(6, 51), + // (9,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'string[][]'. + // ReadOnlySpan M5(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "string[][]").WithLocation(9, 51), + // (10,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'object[][]'. + // ReadOnlySpan M6(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "object[][]").WithLocation(10, 51), + // (12,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'string[][]'. + // ReadOnlySpan M7(string?[][] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("string?[][]", "string[][]").WithLocation(12, 51), + // (13,51): warning CS8619: Nullability of reference types in value of type 'object?[][]' doesn't match target type 'string[][]'. + // ReadOnlySpan M8(object?[][] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[][]", "string[][]").WithLocation(13, 51)); + + var expectedDiagnostics = new[] + { + // (6,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M2(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "System.ReadOnlySpan").WithLocation(6, 51), + // (9,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M5(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "System.ReadOnlySpan").WithLocation(9, 51), + // (10,51): warning CS8619: Nullability of reference types in value of type 'string?[][]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M6(string?[][] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("string?[][]", "System.ReadOnlySpan").WithLocation(10, 51), + // (13,51): warning CS8619: Nullability of reference types in value of type 'object?[][]' doesn't match target type 'string[][]'. + // ReadOnlySpan M8(object?[][] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[][]", "string[][]").WithLocation(13, 51) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_NullableAnalysis_NestedNullability(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + Span> M1(S[] arg) => arg; + Span> M2(S[] arg) => arg; + Span> M3(S[] arg) => arg; + Span> M4(S[] arg) => arg; + + Span> M5(S[] arg) => (Span>)arg; + } + struct S { } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (6,45): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // Span> M2(S[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("S[]", targetType("string")).WithLocation(6, 45), + // (7,45): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // Span> M3(S[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("S[]", targetType("string?")).WithLocation(7, 45), + // (10,45): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // Span> M5(S[] arg) => (Span>)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(Span>)arg").WithArguments("S[]", targetType("string")).WithLocation(10, 45)); + + string targetType(string inner) + => langVersion > LanguageVersion.CSharp12 ? $"System.Span>" : $"S<{inner}>[]"; + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M1(S[] arg) => arg; + ReadOnlySpan> M2(S[] arg) => arg; + ReadOnlySpan> M3(S[] arg) => arg; + ReadOnlySpan> M4(S[] arg) => arg; + ReadOnlySpan> M5(S[] arg) => arg; + ReadOnlySpan> M6(S[] arg) => arg; + + ReadOnlySpan> M7(S[] arg) => (ReadOnlySpan>)arg; + ReadOnlySpan> M8(S[] arg) => (ReadOnlySpan>)arg; + } + struct S { } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (6,53): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // ReadOnlySpan> M2(S[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("S[]", targetType("string")).WithLocation(6, 53), + // (7,53): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // ReadOnlySpan> M3(S[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("S[]", targetType("string?")).WithLocation(7, 53), + // (9,53): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // ReadOnlySpan> M5(S[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("S[]", targetType("string")).WithLocation(9, 53), + // (10,53): error CS0029: Cannot implicitly convert type 'S[]' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M6(S[] arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("S[]", "System.ReadOnlySpan>").WithLocation(10, 53), + // (12,53): warning CS8619: Nullability of reference types in value of type 'S[]' doesn't match target type 'S[]'. + // ReadOnlySpan> M7(S[] arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan>)arg").WithArguments("S[]", targetType("string")).WithLocation(12, 53), + // (13,53): error CS0030: Cannot convert type 'S[]' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M8(S[] arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan>)arg").WithArguments("S[]", "System.ReadOnlySpan>").WithLocation(13, 53)); + + string targetType(string inner) + => langVersion > LanguageVersion.CSharp12 ? $"System.ReadOnlySpan>" : $"S<{inner}>[]"; + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability_Covariant(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M(I[] arg) => arg; + } + interface I { } + """; + var targetType = langVersion > LanguageVersion.CSharp12 ? "System.ReadOnlySpan>" : "I[]"; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,52): warning CS8619: Nullability of reference types in value of type 'I[]' doesn't match target type 'I[]'. + // ReadOnlySpan> M(I[] arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("I[]", targetType).WithLocation(5, 52)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_NullableAnalysis_Outer(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + Span? M1(string[] arg) => arg; + ReadOnlySpan? M2(string[] arg) => arg; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,19): error CS9244: The type 'Span' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'Nullable' + // Span? M1(string[] arg) => arg; + Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "M1").WithArguments("System.Nullable", "T", "System.Span").WithLocation(5, 19), + // (6,27): error CS9244: The type 'ReadOnlySpan' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'Nullable' + // ReadOnlySpan? M2(string[] arg) => arg; + Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "M2").WithArguments("System.Nullable", "T", "System.ReadOnlySpan").WithLocation(6, 27)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_NullableAnalysis_ExtensionMethodReceiver() + { + var source = """ + #nullable enable + using System; + static class C + { + static void MS(string[] a, string?[] b) + { + a.M1(); b.M1(); + a.M2(); b.M2(); + a.M3(); b.M3(); + a.M4(); b.M4(); + } + static void M1(this Span arg) { } + static void M2(this Span arg) { } + static void M3(this ReadOnlySpan arg) { } + static void M4(this ReadOnlySpan arg) { } + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (7,17): warning CS8620: Argument of type 'string?[]' cannot be used for parameter 'arg' of type 'Span' in 'void C.M1(Span arg)' due to differences in the nullability of reference types. + // a.M1(); b.M1(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "b").WithArguments("string?[]", "System.Span", "arg", "void C.M1(Span arg)").WithLocation(7, 17), + // (8,9): warning CS8620: Argument of type 'string[]' cannot be used for parameter 'arg' of type 'Span' in 'void C.M2(Span arg)' due to differences in the nullability of reference types. + // a.M2(); b.M2(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a").WithArguments("string[]", "System.Span", "arg", "void C.M2(Span arg)").WithLocation(8, 9), + // (9,17): warning CS8620: Argument of type 'string?[]' cannot be used for parameter 'arg' of type 'ReadOnlySpan' in 'void C.M3(ReadOnlySpan arg)' due to differences in the nullability of reference types. + // a.M3(); b.M3(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "b").WithArguments("string?[]", "System.ReadOnlySpan", "arg", "void C.M3(ReadOnlySpan arg)").WithLocation(9, 17)); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_Ref_01( + [CombinatorialValues("ref", "ref readonly", "in")] string modifier) + { + var source = $$""" + class C + { + System.Span M1({{modifier}} string[] arg) => arg; + System.ReadOnlySpan M2({{modifier}} string[] arg) => arg; + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify).VerifyDiagnostics(); + verifier.VerifyIL("C.M1", """ + { + // Code size 8 (0x8) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: ldind.ref + IL_0002: call "System.Span System.Span.op_Implicit(string[])" + IL_0007: ret + } + """); + verifier.VerifyIL("C.M2", """ + { + // Code size 8 (0x8) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: ldind.ref + IL_0002: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(string[])" + IL_0007: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_Ref_02( + [CombinatorialLangVersions] LanguageVersion langVersion, + [CombinatorialValues("ref", "ref readonly", "in")] string modifier) + { + var source = $$""" + using System; + + class C + { + Span M1(string[] arg) => M2({{argModifier(modifier)}} + arg); // 1 + Span M2({{modifier}} Span arg) => arg; + + ReadOnlySpan M3(string[] arg) => M4({{argModifier(modifier)}} + arg); // 2 + ReadOnlySpan M4({{modifier}} ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (6,9): error CS1503: Argument 1: cannot convert from 'ref string[]' to 'ref System.Span' + // arg); // 1 + Diagnostic(ErrorCode.ERR_BadArgType, "arg").WithArguments("1", $"{argModifier(modifier)} string[]", $"{modifier} System.Span").WithLocation(6, 9), + // (10,9): error CS1503: Argument 1: cannot convert from 'ref string[]' to 'ref System.ReadOnlySpan' + // arg); // 2 + Diagnostic(ErrorCode.ERR_BadArgType, "arg").WithArguments("1", $"{argModifier(modifier)} string[]", $"{modifier} System.ReadOnlySpan").WithLocation(10, 9)); + + static string argModifier(string paramModifier) + => paramModifier == "ref readonly" ? "in" : paramModifier; + } + + [Fact] + public void Conversion_Array_Span_Implicit_Out() + { + var source = """ + class C + { + System.Span M1(out string[] arg) => arg = null; + System.ReadOnlySpan M2(out string[] arg) => arg = null; + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify).VerifyDiagnostics(); + verifier.VerifyIL("C.M1", """ + { + // Code size 12 (0xc) + .maxstack 3 + .locals init (string[] V_0) + IL_0000: ldarg.1 + IL_0001: ldnull + IL_0002: dup + IL_0003: stloc.0 + IL_0004: stind.ref + IL_0005: ldloc.0 + IL_0006: call "System.Span System.Span.op_Implicit(string[])" + IL_000b: ret + } + """); + verifier.VerifyIL("C.M2", """ + { + // Code size 12 (0xc) + .maxstack 3 + .locals init (string[] V_0) + IL_0000: ldarg.1 + IL_0001: ldnull + IL_0002: dup + IL_0003: stloc.0 + IL_0004: stind.ref + IL_0005: ldloc.0 + IL_0006: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(string[])" + IL_000b: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_RefReturn(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ref Span M1(ref string[] arg) => ref arg; + ref ReadOnlySpan M2(ref string[] arg) => ref arg; + ref ReadOnlySpan M3(ref string[] arg) => ref arg; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,50): error CS8151: The return expression must be of type 'Span' because this method returns by reference + // ref Span M1(ref string[] arg) => ref arg; + Diagnostic(ErrorCode.ERR_RefReturnMustHaveIdentityConversion, "arg").WithArguments("System.Span").WithLocation(4, 50), + // (5,58): error CS8151: The return expression must be of type 'ReadOnlySpan' because this method returns by reference + // ref ReadOnlySpan M2(ref string[] arg) => ref arg; + Diagnostic(ErrorCode.ERR_RefReturnMustHaveIdentityConversion, "arg").WithArguments("System.ReadOnlySpan").WithLocation(5, 58), + // (6,58): error CS8151: The return expression must be of type 'ReadOnlySpan' because this method returns by reference + // ref ReadOnlySpan M3(ref string[] arg) => ref arg; + Diagnostic(ErrorCode.ERR_RefReturnMustHaveIdentityConversion, "arg").WithArguments("System.ReadOnlySpan").WithLocation(6, 58)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_Value() + { + var source = """ + class C + { + void M() + { + System.Span s = A(); + System.ReadOnlySpan r = A(); + } + string[] A() => null; + } + """; + var comp = CreateCompilationWithSpan(source, TestOptions.DebugDll); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 26 (0x1a) + .maxstack 1 + .locals init (System.Span V_0, //s + System.ReadOnlySpan V_1) //r + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call "string[] C.A()" + IL_0007: call "System.Span System.Span.op_Implicit(string[])" + IL_000c: stloc.0 + IL_000d: ldarg.0 + IL_000e: call "string[] C.A()" + IL_0013: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(string[])" + IL_0018: stloc.1 + IL_0019: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Opposite_Implicit(LanguageVersion langVersion) + { + var source = """ + class C + { + int[] M1(System.Span arg) => arg; + int[] M2(System.ReadOnlySpan arg) => arg; + object[] M3(System.ReadOnlySpan arg) => arg; + string[] M4(System.ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,40): error CS0029: Cannot implicitly convert type 'System.Span' to 'int[]' + // int[] M1(System.Span arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span", "int[]").WithLocation(3, 40), + // (4,48): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'int[]' + // int[] M2(System.ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "int[]").WithLocation(4, 48), + // (5,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'object[]' + // object[] M3(System.ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "object[]").WithLocation(5, 54), + // (6,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'string[]' + // string[] M4(System.ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Opposite_Explicit(LanguageVersion langVersion) + { + var source = """ + class C + { + int[] M1(System.Span arg) => (int[])arg; + int[] M2(System.ReadOnlySpan arg) => (int[])arg; + object[] M3(System.ReadOnlySpan arg) => (object[])arg; + string[] M4(System.ReadOnlySpan arg) => (string[])arg; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,40): error CS0030: Cannot convert type 'System.Span' to 'int[]' + // int[] M1(System.Span arg) => (int[])arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int[])arg").WithArguments("System.Span", "int[]").WithLocation(3, 40), + // (4,48): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'int[]' + // int[] M2(System.ReadOnlySpan arg) => (int[])arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int[])arg").WithArguments("System.ReadOnlySpan", "int[]").WithLocation(4, 48), + // (5,54): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'object[]' + // object[] M3(System.ReadOnlySpan arg) => (object[])arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object[])arg").WithArguments("System.ReadOnlySpan", "object[]").WithLocation(5, 54), + // (6,54): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'string[]' + // string[] M4(System.ReadOnlySpan arg) => (string[])arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string[])arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Opposite_Explicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + class C + { + int[] M(System.Span arg) => (int[])arg; + } + + namespace System + { + readonly ref struct Span + { + public static explicit operator T[](Span span) => throw null; + } + } + """; + var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "int[] System.Span.op_Explicit(System.Span)" + IL_0006: ret + } + """); + } + + [Fact] + public void Conversion_Array_Span_Implicit_Params() + { + var source = """ + using System; + + class C + { + void M(string[] a) + { + M1(a); + M2(a); + M3(a); + } + void M1(params Span s) { } + void M2(params ReadOnlySpan s) { } + void M3(params string[] s) { } + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 32 (0x20) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call "System.Span System.Span.op_Implicit(string[])" + IL_0007: call "void C.M1(params System.Span)" + IL_000c: ldarg.0 + IL_000d: ldarg.1 + IL_000e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(string[])" + IL_0013: call "void C.M2(params System.ReadOnlySpan)" + IL_0018: ldarg.0 + IL_0019: ldarg.1 + IL_001a: call "void C.M3(params string[])" + IL_001f: ret + } + """); + } + + [Fact] + public void Conversion_Array_ReadOnlySpan_Implicit_Params_Covariant() + { + var source = """ + using System; + + class C + { + void M(string[] a) + { + M1(a); + M2(a); + M3(a); + } + void M1(params Span s) { } + void M2(params ReadOnlySpan s) { } + void M3(params object[] p) { } + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 38 (0x26) + .maxstack 2 + .locals init (object[] V_0) + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call "System.Span System.Span.op_Implicit(object[])" + IL_0009: call "void C.M1(params System.Span)" + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(object[])" + IL_0017: call "void C.M2(params System.ReadOnlySpan)" + IL_001c: ldarg.0 + IL_001d: ldarg.1 + IL_001e: stloc.0 + IL_001f: ldloc.0 + IL_0020: call "void C.M3(params object[])" + IL_0025: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_Multidimensional(LanguageVersion langVersion) + { + var source = """ + using System; + + class C + { + Span M1(string[,] a) => a; + Span M2(string[][] a) => a; + Span M3(string[,] a) => a; + Span M4(string[][] a) => a; + Span M5(string[,][] a) => a; + Span M6(string[][][] a) => a; + Span M7(string[][][] a) => a; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,37): error CS0029: Cannot implicitly convert type 'string[*,*]' to 'System.Span' + // Span M1(string[,] a) => a; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "a").WithArguments("string[*,*]", "System.Span").WithLocation(5, 37), + // (6,38): error CS0029: Cannot implicitly convert type 'string[][]' to 'System.Span' + // Span M2(string[][] a) => a; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "a").WithArguments("string[][]", "System.Span").WithLocation(6, 38), + // (7,39): error CS0029: Cannot implicitly convert type 'string[*,*]' to 'System.Span' + // Span M3(string[,] a) => a; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "a").WithArguments("string[*,*]", "System.Span").WithLocation(7, 39), + // (9,43): error CS0029: Cannot implicitly convert type 'string[*,*][]' to 'System.Span' + // Span M5(string[,][] a) => a; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "a").WithArguments("string[*,*][]", "System.Span").WithLocation(9, 43), + // (11,43): error CS0029: Cannot implicitly convert type 'string[][][]' to 'System.Span' + // Span M7(string[][][] a) => a; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "a").WithArguments("string[][][]", "System.Span").WithLocation(11, 43)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_MethodGroup_ReturnType(LanguageVersion langVersion) + { + var source = """ + using System; + + C.R(C.M); + C.R(() => C.M()); + + static class C + { + public static int[] M() => new int[] { 1, 2, 3 }; + public static void R(D f) => Console.Write(f()[1]); + } + + delegate Span D(); + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,5): error CS0407: 'int[] C.M()' has the wrong return type + // C.R(C.M); + Diagnostic(ErrorCode.ERR_BadRetType, "C.M").WithArguments("C.M()", "int[]").WithLocation(3, 5)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_MethodGroup_ParameterType(LanguageVersion langVersion) + { + var source = """ + using System; + + C.R(C.M); + C.R(x => C.M(x)); + + static class C + { + public static void M(Span x) => Console.Write(x[1]); + public static void R(D a) => a(new int[] { 1, 2, 3 }); + } + + delegate void D(int[] x); + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,5): error CS0123: No overload for 'C.M(Span)' matches delegate 'D' + // C.R(C.M); + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "C.M").WithArguments("C.M(System.Span)", "D").WithLocation(3, 5)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_MethodGroup_ExtensionMethodReceiver() + { + var source = """ + using System; + + var a = new[] { 1, 2, 3 }; + C.R(a.M); + C.R(x => a.M(x)); + + static class C + { + public static int M(this Span x, int y) => x[y]; + public static void R(Func f) => Console.Write(f(1)); + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,5): error CS1503: Argument 1: cannot convert from 'method group' to 'System.Func' + // C.R(a.M); + Diagnostic(ErrorCode.ERR_BadArgType, "a.M").WithArguments("1", "method group", "System.Func").WithLocation(4, 5), + // (5,10): error CS1929: 'int[]' does not contain a definition for 'M' and the best extension method overload 'C.M(Span, int)' requires a receiver of type 'System.Span' + // C.R(x => a.M(x)); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("int[]", "M", "C.M(System.Span, int)", "System.Span").WithLocation(5, 10)); + + var expectedDiagnostics = new[] + { + // (4,5): error CS1113: Extension method 'C.M(Span, int)' defined on value type 'Span' cannot be used to create delegates + // C.R(a.M); + Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "a.M").WithArguments("C.M(System.Span, int)", "System.Span").WithLocation(4, 5) + }; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpan(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_MethodGroup_FunctionPointer(LanguageVersion langVersion) + { + var source = """ + using System; + + unsafe + { + C.R(&C.M); + } + + unsafe static class C + { + public static void M(Span x) => Console.Write(x[1]); + public static void R(delegate* a) => a(new int[] { 1, 2, 3 }); + } + """; + + CreateCompilationWithSpan(source, TestOptions.UnsafeReleaseExe, TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,9): error CS8757: No overload for 'C.M(Span)' matches function pointer 'delegate*' + // C.R(&C.M); + Diagnostic(ErrorCode.ERR_MethFuncPtrMismatch, "&C.M").WithArguments("C.M(System.Span)", "delegate*").WithLocation(5, 9)); + } + + [Theory, CombinatorialData] + public void Conversion_Array_ReadOnlySpan_Covariant( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) + { + var source = $$""" + using System; + + class C + { + ReadOnlySpan M(string[] x) => {{(cast ? "(ReadOnlySpan)" : "")}}x; + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 9 (0x9) + .maxstack 1 + .locals init (object[] V_0) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(object[])" + IL_0008: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_ReadOnlySpan_Interface_Covariant( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) + { + var source = $$""" + using System; + + class C + { + ReadOnlySpan> M(I[] x) => {{(cast ? "(ReadOnlySpan>)" : "")}}x; + } + + interface I { } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 9 (0x9) + .maxstack 1 + .locals init (I[] V_0) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: call "System.ReadOnlySpan> System.ReadOnlySpan>.op_Implicit(I[])" + IL_0008: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_ReadOnlySpan_Interface_Outside( + [CombinatorialLangVersions] LanguageVersion langVersion, + [CombinatorialValues("", "in", "out")] string variance) + { + var source = $$""" + using System; + + class C + { + I> M(I x) => x; + } + + interface I<{{variance}} T> { } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,29): error CS9244: The type 'ReadOnlySpan' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'I' + // I> M(I x) => x; + Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "M").WithArguments("I", "T", "System.ReadOnlySpan").WithLocation(5, 29), + // (5,49): error CS0266: Cannot implicitly convert type 'I' to 'I>'. An explicit conversion exists (are you missing a cast?) + // I> M(I x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("I", "I>").WithLocation(5, 49)); + } + + [Theory, CombinatorialData] + public void Conversion_Array_ReadOnlySpan_Interface_Outside_AllowsRefStruct( + [CombinatorialValues("", "in", "out")] string variance) + { + var source = $$""" + using System; + + class C + { + I> M(I x) => x; + } + + interface I<{{variance}} T> where T : allows ref struct { } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (5,49): error CS0266: Cannot implicitly convert type 'I' to 'I>'. An explicit conversion exists (are you missing a cast?) + // I> M(I x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("I", "I>").WithLocation(5, 49)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Interface_Invariant(LanguageVersion langVersion) + { + var source = """ + using System; + + class C + { + ReadOnlySpan> M(I[] x) => x; + } + + interface I { } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,49): error CS0029: Cannot implicitly convert type 'I[]' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M(I[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("I[]", "System.ReadOnlySpan>").WithLocation(5, 49)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Interface_Contravariant(LanguageVersion langVersion) + { + var source = """ + using System; + + class C + { + ReadOnlySpan> M(I[] x) => x; + } + + interface I { } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,49): error CS0266: Cannot implicitly convert type 'I[]' to 'System.ReadOnlySpan>'. An explicit conversion exists (are you missing a cast?) + // ReadOnlySpan> M(I[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("I[]", "System.ReadOnlySpan>").WithLocation(5, 49)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Interface_Contravariant_Cast(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M(new[] { new C() })[0].Report(); + + class C : I + { + public void Report() => Console.Write("C"); + public static ReadOnlySpan> M(I[] x) => (ReadOnlySpan>)x; + } + + interface I + { + void Report(); + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: "C", verify: Verification.FailsILVerify).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: castclass "I[]" + IL_0006: call "System.ReadOnlySpan> System.ReadOnlySpan>.op_Implicit(I[])" + IL_000b: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Covariant_ValueType(LanguageVersion langVersion) + { + var source = """ + using System; + + class C + { + ReadOnlySpan M(int[] x) => x; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,38): error CS0029: Cannot implicitly convert type 'int[]' to 'System.ReadOnlySpan' + // ReadOnlySpan M(int[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("int[]", "System.ReadOnlySpan").WithLocation(5, 38)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Covariant_TypeParameter(LanguageVersion langVersion) + { + var source = """ + using System; + + class C + where T : class, U + { + ReadOnlySpan M(T[] x) => x; + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 9 (0x9) + .maxstack 1 + .locals init (U[] V_0) + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(U[])" + IL_0008: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_TypeParameter_NullableValueType_01(LanguageVersion langVersion) + { + var source = """ + using System; + + class C where T : struct + { + ReadOnlySpan M(T?[] x) => x; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,34): error CS0029: Cannot implicitly convert type 'T?[]' to 'System.ReadOnlySpan' + // ReadOnlySpan M(T?[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T?[]", "System.ReadOnlySpan").WithLocation(5, 34)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_TypeParameter_NullableValueType_02(LanguageVersion langVersion) + { + var source = """ + using System; + + class C where T : struct + { + ReadOnlySpan M(T[] x) => x; + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,34): error CS0029: Cannot implicitly convert type 'T[]' to 'System.ReadOnlySpan' + // ReadOnlySpan M(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(5, 34)); + } + + [Theory] + [InlineData("")] + [InlineData("where U : T")] + public void Conversion_Array_Span_Variance_01(string constraints) + { + var source = $$""" + using System; + class C {{constraints}} + { + ReadOnlySpan F1(T[] x) => x; + ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + Span F3(T[] x) => x; + Span F4(T[] x) => (Span)x; + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (4,34): error CS0029: Cannot implicitly convert type 'T[]' to 'System.ReadOnlySpan' + // ReadOnlySpan F1(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(4, 34), + // (5,34): error CS0030: Cannot convert type 'T[]' to 'System.ReadOnlySpan' + // ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(5, 34), + // (6,26): error CS0029: Cannot implicitly convert type 'T[]' to 'System.Span' + // Span F3(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.Span").WithLocation(6, 26), + // (7,26): error CS0030: Cannot convert type 'T[]' to 'System.Span' + // Span F4(T[] x) => (Span)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)x").WithArguments("T[]", "System.Span").WithLocation(7, 26)); + } + + // PROTOTYPE: User-defined conversions should not be considered? Note: this is not the only test affected. In particular, nullable analysis tests should be revisited. + [Fact] + public void Conversion_Array_Span_Variance_02() + { + var source = """ + using System; + class C + where T : class + where U : class, T + { + ReadOnlySpan F1(T[] x) => x; + ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + Span F3(T[] x) => x; + Span F4(T[] x) => (Span)x; + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (6,34): error CS0266: Cannot implicitly convert type 'T[]' to 'System.ReadOnlySpan'. An explicit conversion exists (are you missing a cast?) + // ReadOnlySpan F1(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(6, 34), + // (8,26): error CS0266: Cannot implicitly convert type 'T[]' to 'System.Span'. An explicit conversion exists (are you missing a cast?) + // Span F3(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "x").WithArguments("T[]", "System.Span").WithLocation(8, 26)); + } + + [Fact] + public void Conversion_Array_Span_Variance_03() + { + var source = """ + using System; + class C + where T : class, U + where U : class + { + ReadOnlySpan F1(T[] x) => x; + ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + Span F3(T[] x) => x; + Span F4(T[] x) => (Span)x; + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_ThroughUserImplicit( + [CombinatorialValues("Span", "ReadOnlySpan")] string destination) + { + var source = $$""" + using System; + + D.M(new C()); + + class C + { + public static implicit operator int[](C c) => new int[] { 4, 5, 6 }; + } + + static class D + { + public static void M({{destination}} xs) + { + foreach (var x in xs) + { + Console.Write(x); + } + } + } + """; + + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (3,5): error CS1503: Argument 1: cannot convert from 'C' to 'System.Span' + // D.M(new C()); + Diagnostic(ErrorCode.ERR_BadArgType, "new C()").WithArguments("1", "C", $"System.{destination}").WithLocation(3, 5)); + + var expectedOutput = "456"; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Conversion_Array_Span_ThroughUserImplicit_MissingHelper() + { + var source = """ + using System; + + D.M(new C()); + + class C + { + public static implicit operator int[](C c) => new int[] { 4, 5, 6 }; + } + + static class D + { + public static void M(Span xs) + { + foreach (var x in xs) + { + Console.Write(x); + } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS1503: Argument 1: cannot convert from 'C' to 'System.Span' + // D.M(new C()); + 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); + + expectedDiagnostics = [ + // (3,5): error CS0656: Missing compiler required member 'System.Span`1.op_Implicit' + // D.M(new C()); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.Span`1", "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(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.RegularPreview); + verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.RegularPreview, expectedDiagnostics); + + void verifyWithMissing(WellKnownMember member, 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); + } + } + } + + [Fact] + public void Conversion_Array_ReadOnlySpan_ThroughUserImplicit_MissingHelper() + { + var source = """ + using System; + + D.M(new C()); + + class C + { + public static implicit operator int[](C c) => new int[] { 4, 5, 6 }; + } + + static class D + { + public static void M(ReadOnlySpan xs) + { + foreach (var x in xs) + { + Console.Write(x); + } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS1503: Argument 1: cannot convert from 'C' to 'System.ReadOnlySpan' + // D.M(new C()); + 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); + + expectedDiagnostics = [ + // (3,5): error CS0656: Missing compiler required member 'System.ReadOnlySpan`1.op_Implicit' + // D.M(new C()); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "new C()").WithArguments("System.ReadOnlySpan`1", "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(WellKnownMember.System_ReadOnlySpan_T__op_Implicit_Array, TestOptions.RegularPreview, expectedDiagnostics); + verifyWithMissing(WellKnownMember.System_Span_T__op_Implicit_Array, TestOptions.RegularPreview); + + void verifyWithMissing(WellKnownMember member, 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); + } + } + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit() + { + var source = """ + using System; + + C.M(new int[] { 7, 8, 9 }); + + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this Span arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,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(7, 40)); + + var expectedOutput = "8"; + + var expectedIl = """ + { + // 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 comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Ref(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M(new int[] { 7, 8, 9 }); + + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this ref Span arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (7,40): error CS1929: 'int[]' does not contain a definition for 'E' and the best extension method overload 'C.E(ref Span)' requires a receiver of type 'ref System.Span' + // public static void M(int[] arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("int[]", "E", "C.E(ref System.Span)", "ref System.Span").WithLocation(7, 40)); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_RefReadOnly( + [CombinatorialValues("ref readonly", "in")] string modifier) + { + var source = $$""" + using System; + + C.M(new int[] { 7, 8, 9 }); + + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this {{modifier}} Span arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,40): error CS1929: 'int[]' does not contain a definition for 'E' and the best extension method overload 'C.E(ref Span)' requires a receiver of type 'ref System.Span' + // public static void M(int[] arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("int[]", "E", $"C.E({modifier} System.Span)", $"{modifier} System.Span").WithLocation(7, 40)); + + var expectedOutput = "8"; + + var expectedIl = $$""" + { + // Code size 15 (0xf) + .maxstack 1 + .locals init (System.Span V_0) + IL_0000: ldarg.0 + IL_0001: call "System.Span System.Span.op_Implicit(int[])" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call "void C.E({{modifier}} System.Span)" + IL_000e: ret + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_MissingHelper() + { + var source = """ + using System; + + C.M(new int[] { 7, 8, 9 }); + + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this Span arg) => Console.Write(arg[1]); + } + """; + 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' + // public static void M(int[] arg) => arg.E(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arg").WithArguments("System.Span`1", "op_Implicit").WithLocation(7, 40)); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Generic_01() + { + var source = """ + using System; + static class C + { + public static void M(int[] arg) => arg.E(); + public static void E(this Span arg) { } + } + """; + // PROTOTYPE: Needs type inference to work. + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (4,44): error CS1061: 'int[]' does not contain a definition for 'E' and no accessible extension method 'E' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // public static void M(int[] arg) => arg.E(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "E").WithArguments("int[]", "E").WithLocation(4, 44)); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Generic_02() + { + 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()); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Generic_03() + { + var source = """ + using System; + static class C + { + public static void M(int[] arg) => arg.E(42); + public static void E(this Span arg, T x) { } + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 14 (0xe) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call "System.Span System.Span.op_Implicit(int[])" + IL_0006: ldc.i4.s 42 + IL_0008: call "void C.E(System.Span, int)" + IL_000d: ret + } + """); + + var tree = comp.SyntaxTrees.Single(); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("arg.E(42)", invocation.ToString()); + + var model = comp.GetSemanticModel(tree); + var info = model.GetSymbolInfo(invocation); + Assert.Equal("void System.Span.E(System.Int32 x)", info.Symbol!.ToTestDisplayString()); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Implicit_Generic_04() + { + var source = """ + using System; + static class C + { + public static void M(int[] arg) => arg.E(42); + public static void E(this Span arg, T x) { } + } + """; + var comp = CreateCompilationWithSpan(source); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 14 (0xe) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: call "System.Span System.Span.op_Implicit(int[])" + IL_0006: ldc.i4.s 42 + IL_0008: call "void C.E(System.Span, int)" + IL_000d: ret + } + """); + + var tree = comp.SyntaxTrees.Single(); + var invocation = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("arg.E(42)", invocation.ToString()); + + var model = comp.GetSemanticModel(tree); + var info = model.GetSymbolInfo(invocation); + Assert.Equal("void System.Span.E(System.Int32 x)", info.Symbol!.ToTestDisplayString()); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_ExtensionMethodReceiver_Explicit(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M(new int[] { 7, 8, 9 }); + + static class C + { + public static void M(int[] arg) => ((Span)arg).E(); + public static void E(this Span arg) => Console.Write(arg[1]); + } + """; + + var expectedOutput = "8"; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).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 + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_ExtensionMethodReceiver_Opposite_Implicit(LanguageVersion langVersion) + { + var source = """ + static class C + { + static void M1(System.Span arg) => arg.E1(); + static void M2(System.ReadOnlySpan arg) => arg.E1(); + static void M3(System.ReadOnlySpan arg) => arg.E2(); + static void M4(System.ReadOnlySpan arg) => arg.E3(); + static void E1(this int[] arg) { } + static void E2(this object[] arg) { } + static void E3(this string[] arg) { } + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,45): error CS1929: 'Span' does not contain a definition for 'E1' and the best extension method overload 'C.E1(int[])' requires a receiver of type 'int[]' + // static void M1(System.Span arg) => arg.E1(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.Span", "E1", "C.E1(int[])", "int[]").WithLocation(3, 45), + // (4,53): error CS1929: 'ReadOnlySpan' does not contain a definition for 'E1' and the best extension method overload 'C.E1(int[])' requires a receiver of type 'int[]' + // static void M2(System.ReadOnlySpan arg) => arg.E1(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.ReadOnlySpan", "E1", "C.E1(int[])", "int[]").WithLocation(4, 53), + // (5,56): error CS1929: 'ReadOnlySpan' does not contain a definition for 'E2' and the best extension method overload 'C.E2(object[])' requires a receiver of type 'object[]' + // static void M3(System.ReadOnlySpan arg) => arg.E2(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.ReadOnlySpan", "E2", "C.E2(object[])", "object[]").WithLocation(5, 56), + // (6,56): error CS1929: 'ReadOnlySpan' does not contain a definition for 'E3' and the best extension method overload 'C.E3(string[])' requires a receiver of type 'string[]' + // static void M4(System.ReadOnlySpan arg) => arg.E3(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.ReadOnlySpan", "E3", "C.E3(string[])", "string[]").WithLocation(6, 56)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_ExtensionMethodReceiver_Opposite_Explicit(LanguageVersion langVersion) + { + var source = """ + static class C + { + static void M1(System.Span arg) => ((int[])arg).E1(); + static void M2(System.ReadOnlySpan arg) => ((int[])arg).E1(); + static void M3(System.ReadOnlySpan arg) => ((object[])arg).E2(); + static void M4(System.ReadOnlySpan arg) => ((string[])arg).E3(); + static void E1(this int[] arg) { } + static void E2(this object[] arg) { } + static void E3(this string[] arg) { } + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,46): error CS0030: Cannot convert type 'System.Span' to 'int[]' + // static void M1(System.Span arg) => ((int[])arg).E1(); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int[])arg").WithArguments("System.Span", "int[]").WithLocation(3, 46), + // (4,54): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'int[]' + // static void M2(System.ReadOnlySpan arg) => ((int[])arg).E1(); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int[])arg").WithArguments("System.ReadOnlySpan", "int[]").WithLocation(4, 54), + // (5,57): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'object[]' + // static void M3(System.ReadOnlySpan arg) => ((object[])arg).E2(); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object[])arg").WithArguments("System.ReadOnlySpan", "object[]").WithLocation(5, 57), + // (6,57): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'string[]' + // static void M4(System.ReadOnlySpan arg) => ((string[])arg).E3(); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string[])arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 57)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_ExtensionMethodReceiver_Opposite_Explicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + static class C + { + static void M(System.Span arg) => ((int[])arg).E(); + static void E(this int[] arg) { } + } + + namespace System + { + readonly ref struct Span + { + public static explicit operator T[](Span span) => throw null; + } + } + """; + var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "int[] System.Span.op_Explicit(System.Span)" + IL_0006: call "void C.E(int[])" + IL_000b: ret + } + """); + } + + [Theory] + [InlineData("")] + [InlineData("where U : T")] + [InlineData("where T : class where U : class, T")] + public void Conversion_Array_Span_ExtensionMethodReceiver_Variance_01(string constraints) + { + var source = $$""" + using System; + static class Extensions + { + public static void M1(this Span arg) { } + public static void M2(this ReadOnlySpan arg) { } + } + class C {{constraints}} + { + static void F1(T[] a) + { + a.M1(); + a.M2(); + } + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (11,9): error CS1929: 'T[]' does not contain a definition for 'M1' and the best extension method overload 'Extensions.M1(Span)' requires a receiver of type 'System.Span' + // a.M1(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("T[]", "M1", "Extensions.M1(System.Span)", "System.Span").WithLocation(11, 9), + // (12,9): error CS1929: 'T[]' does not contain a definition for 'M2' and the best extension method overload 'Extensions.M2(ReadOnlySpan)' requires a receiver of type 'System.ReadOnlySpan' + // a.M2(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("T[]", "M2", "Extensions.M2(System.ReadOnlySpan)", "System.ReadOnlySpan").WithLocation(12, 9)); + } + + [Fact] + public void Conversion_Array_Span_ExtensionMethodReceiver_Variance_02() + { + var source = """ + using System; + static class Extensions + { + public static void M1(this Span arg) { } + public static void M2(this ReadOnlySpan arg) { } + } + class C + where T : class, U + where U : class + { + static void F1(T[] a) + { + a.M1(); + a.M2(); + } + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (13,9): error CS1929: 'T[]' does not contain a definition for 'M1' and the best extension method overload 'Extensions.M1(Span)' requires a receiver of type 'System.Span' + // a.M1(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("T[]", "M1", "Extensions.M1(System.Span)", "System.Span").WithLocation(13, 9)); + } + + [Fact] + public void Conversion_Array_ReadOnlySpan_ExtensionMethodReceiver_Covariance() + { + var source = """ + using System; + + C.M(new string[] { "a", "b", "c" }); + + static class C + { + public static void M(string[] arg) => arg.E(); + public static void E(this ReadOnlySpan arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,43): error CS1929: 'string[]' does not contain a definition for 'E' and the best extension method overload 'C.E(ReadOnlySpan)' requires a receiver of type 'System.ReadOnlySpan' + // public static void M(string[] arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("string[]", "E", "C.E(System.ReadOnlySpan)", "System.ReadOnlySpan").WithLocation(7, 43)); + + var expectedOutput = "b"; + + var expectedIl = """ + { + // Code size 14 (0xe) + .maxstack 1 + .locals init (object[] V_0) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(object[])" + IL_0008: call "void C.E(System.ReadOnlySpan)" + IL_000d: ret + } + """; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_01(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new string[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + + static class C + { + public static void M(string[] x) => Console.Write(" s" + x[0]); + public static void M(ReadOnlySpan x) => Console.Write(" o" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "sa oSystem.String[] oa").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_02(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new string[] { "a" }; + C.M([..a]); + C.M(["a"]); + + static class C + { + public static void M(string[] x) { } + public static void M(ReadOnlySpan x) { } + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(string[])' and 'C.M(ReadOnlySpan)' + // C.M([..a]); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(string[])", "C.M(System.ReadOnlySpan)").WithLocation(4, 3), + // (5,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(string[])' and 'C.M(ReadOnlySpan)' + // C.M(["a"]); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(string[])", "C.M(System.ReadOnlySpan)").WithLocation(5, 3)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_03(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new string[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + C.M([..a]); + C.M(["a"]); + + static class C + { + public static void M(object[] x) => Console.Write(" a" + x[0]); + public static void M(ReadOnlySpan x) => Console.Write(" r" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "aa rSystem.String[] ra ra ra").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_04(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new object[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + C.M([..a]); + + static class C + { + public static void M(object[] x) => Console.Write(" a" + x[0]); + public static void M(ReadOnlySpan x) => Console.Write(" r" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "aa rSystem.Object[] ra ra").VerifyDiagnostics(); + } + + [Fact] + public void OverloadResolution_ReadOnlySpanVsArray_Params_01() + { + var source = """ + using System; + + var a = new string[] { "a" }; + C.M(a); + C.M(a, a); + C.M([a]); + C.M([..a, a]); + C.M("a"); + + static class C + { + public static void M(params string[] x) => Console.Write(" s" + x[0]); + public static void M(params ReadOnlySpan x) => Console.Write(" o" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: "sa oSystem.String[] oSystem.String[] oa sa").VerifyDiagnostics(); + } + + [Fact] + public void OverloadResolution_ReadOnlySpanVsArray_Params_02() + { + var source = """ + using System; + + var a = new string[] { "a" }; + C.M([..a]); + C.M(["a"]); + + static class C + { + public static void M(params string[] x) { } + public static void M(params ReadOnlySpan x) { } + } + """; + CreateCompilationWithSpan(source).VerifyDiagnostics( + // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(params string[])' and 'C.M(params ReadOnlySpan)' + // C.M([..a]); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(params string[])", "C.M(params System.ReadOnlySpan)").WithLocation(4, 3), + // (5,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(params string[])' and 'C.M(params ReadOnlySpan)' + // C.M(["a"]); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(params string[])", "C.M(params System.ReadOnlySpan)").WithLocation(5, 3)); + } + + [Fact] + public void OverloadResolution_ReadOnlySpanVsArray_Params_03() + { + var source = """ + using System; + + var a = new string[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + C.M([..a]); + C.M(["a"]); + + static class C + { + public static void M(params object[] x) => Console.Write(" a" + x[0]); + public static void M(params ReadOnlySpan x) => Console.Write(" r" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: "aa rSystem.String[] ra ra ra").VerifyDiagnostics(); + } + + [Fact] + public void OverloadResolution_ReadOnlySpanVsArray_Params_04() + { + var source = """ + using System; + + var a = new object[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + C.M([..a]); + + static class C + { + public static void M(params object[] x) => Console.Write(" a" + x[0]); + public static void M(params ReadOnlySpan x) => Console.Write(" r" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: "aa rSystem.Object[] ra ra").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_ExtensionMethodReceiver_01(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new string[] { "a" }; + a.M(); + + static class C + { + public static void M(this string[] x) => Console.Write(" s" + x[0]); + public static void M(this ReadOnlySpan x) => Console.Write(" o" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "sa").VerifyDiagnostics(); + } + + [Fact] + public void OverloadResolution_ReadOnlySpanVsArray_ExtensionMethodReceiver_02() + { + var source = """ + using System; + + var a = new string[] { "a" }; + a.M(); + + static class C + { + public static void M(this object[] x) => Console.Write(" o" + x[0]); + public static void M(this ReadOnlySpan x) => Console.Write(" s" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12); + CompileAndVerify(comp, expectedOutput: "oa").VerifyDiagnostics(); + + // PROTOTYPE: This break should go away with betterness rule. + + var expectedDiagnostics = new[] + { + // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(object[])' and 'C.M(ReadOnlySpan)' + // a.M(); + 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); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_ExtensionMethodReceiver_03(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new string[] { "a" }; + a.M(); + + static class C + { + public static void M(this object[] x) => Console.Write(" a" + x[0]); + public static void M(this ReadOnlySpan x) => Console.Write(" r" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "aa").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArray_ExtensionMethodReceiver_04(LanguageVersion langVersion) + { + var source = """ + using System; + + var a = new object[] { "a" }; + a.M(); + + static class C + { + public static void M(this object[] x) => Console.Write(" a" + x[0]); + public static void M(this ReadOnlySpan x) => Console.Write(" r" + x[0]); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "aa").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_SpanVsReadOnlySpan_01(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M(new int[0]); + C.M(default(Span)); + C.M(default(ReadOnlySpan)); + + static class C + { + public static void M(Span arg) => Console.Write(1); + public static void M(ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "112").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_SpanVsReadOnlySpan_02(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M(new object[0]); + C.M(default(Span)); + C.M(default(ReadOnlySpan)); + + C.M(new string[0]); + + static class C + { + public static void M(Span arg) => Console.Write(1); + public static void M(ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "1121").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_SpanVsReadOnlySpan_03(LanguageVersion langVersion) + { + var source = """ + using System; + + C.M(default(Span)); + C.M(default(ReadOnlySpan)); + + static class C + { + public static void M(Span arg) { } + public static void M(ReadOnlySpan arg) { } + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,5): error CS1503: Argument 1: cannot convert from 'System.Span' to 'System.Span' + // C.M(default(Span)); + Diagnostic(ErrorCode.ERR_BadArgType, "default(Span)").WithArguments("1", "System.Span", "System.Span").WithLocation(3, 5), + // (4,5): error CS1503: Argument 1: cannot convert from 'System.ReadOnlySpan' to 'System.Span' + // C.M(default(ReadOnlySpan)); + Diagnostic(ErrorCode.ERR_BadArgType, "default(ReadOnlySpan)").WithArguments("1", "System.ReadOnlySpan", "System.Span").WithLocation(4, 5)); + } + + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_01() + { + var source = """ + using System; + + (new int[0]).E(); + + static class C + { + public static void E(this Span arg) => Console.Write(1); + public static void E(this ReadOnlySpan arg) => Console.Write(2); + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (3,2): 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' + // (new int[0]).E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new int[0]").WithArguments("int[]", "E", "C.E(System.Span)", "System.Span").WithLocation(3, 2)); + + var expectedOutput = "1"; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_02(LanguageVersion langVersion) + { + var source = """ + using System; + + default(Span).E(); + default(ReadOnlySpan).E(); + + static class C + { + public static void E(this Span arg) => Console.Write(1); + public static void E(this ReadOnlySpan arg) => Console.Write(2); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "12").VerifyDiagnostics(); + } + + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_03() + { + var source = """ + using System; + + (new string[0]).E(); + (new object[0]).E(); + + static class C + { + public static void E(this Span arg) => Console.Write(1); + public static void E(this ReadOnlySpan arg) => Console.Write(2); + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (3,2): error CS1929: 'string[]' does not contain a definition for 'E' and the best extension method overload 'C.E(Span)' requires a receiver of type 'System.Span' + // (new string[0]).E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new string[0]").WithArguments("string[]", "E", "C.E(System.Span)", "System.Span").WithLocation(3, 2), + // (4,2): error CS1929: 'object[]' does not contain a definition for 'E' and the best extension method overload 'C.E(Span)' requires a receiver of type 'System.Span' + // (new string[0]).E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new object[0]").WithArguments("object[]", "E", "C.E(System.Span)", "System.Span").WithLocation(4, 2)); + + var expectedOutput = "21"; + + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_04(LanguageVersion langVersion) + { + var source = """ + using System; + + default(Span).E(); + default(ReadOnlySpan).E(); + + static class C + { + public static void E(this Span arg) { } + public static void E(this ReadOnlySpan arg) { } + } + """; + CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,1): error CS1929: 'Span' does not contain a definition for 'E' and the best extension method overload 'C.E(Span)' requires a receiver of type 'System.Span' + // default(Span).E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "default(Span)").WithArguments("System.Span", "E", "C.E(System.Span)", "System.Span").WithLocation(3, 1), + // (4,1): error CS1929: 'ReadOnlySpan' does not contain a definition for 'E' and the best extension method overload 'C.E(Span)' requires a receiver of type 'System.Span' + // default(ReadOnlySpan).E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "default(ReadOnlySpan)").WithArguments("System.ReadOnlySpan", "E", "C.E(System.Span)", "System.Span").WithLocation(4, 1)); + + // PROTOTYPE: Should work in C# 13 when ROS->ROS conversion is implemented. + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArrayVsSpan(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + + var a = new string[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + C.M([..a]); + C.M(["a"]); + + var b = new object[] { "b" }; + C.M(b); + C.M([b]); + C.M([..b, b]); + C.M([..b]); + + static class C + { + public static void M(object[] x) => Console.Write(" a" + x[0]); + public static void M(ReadOnlySpan x) => Console.Write(" r" + x[0]); + public static void M(Span x) => Console.Write(" s" + x[0]); + public static void M(IEnumerable x) => Console.Write(" e" + x.First()); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "aa rSystem.String[] ra ra ra ab rSystem.Object[] rb rb").VerifyDiagnostics(); + } + + [Fact] + public void OverloadResolution_ReadOnlySpanVsArrayVsSpan_Params() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + + var a = new string[] { "a" }; + C.M(a); + C.M([a]); + C.M([..a, a]); + C.M([..a]); + C.M(["a"]); + + var b = new object[] { "b" }; + C.M(b); + C.M([b]); + C.M([..b, b]); + C.M([..b]); + + static class C + { + public static void M(params object[] x) => Console.Write(" a" + x[0]); + public static void M(params ReadOnlySpan x) => Console.Write(" r" + x[0]); + public static void M(params Span x) => Console.Write(" s" + x[0]); + public static void M(params IEnumerable x) => Console.Write(" e" + x.First()); + } + """; + var comp = CreateCompilationWithSpan(source); + CompileAndVerify(comp, expectedOutput: "aa rSystem.String[] ra ra ra ab rSystem.Object[] rb rb").VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void OverloadResolution_ReadOnlySpanVsArrayVsSpan_ExtensionMethodReceiver(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + + var a = new string[] { "a" }; + a.M(); + + var b = new object[] { "b" }; + b.M(); + + static class C + { + public static void M(this object[] x) => Console.Write(" a" + x[0]); + public static void M(this ReadOnlySpan x) => Console.Write(" r" + x[0]); + public static void M(this Span x) => Console.Write(" s" + x[0]); + public static void M(this IEnumerable x) => Console.Write(" e" + x.First()); + } + """; + var comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "aa ab").VerifyDiagnostics(); + } +} diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConversionExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConversionExpression.cs index 7d648397969bc..0205fafada58f 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConversionExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConversionExpression.cs @@ -3404,6 +3404,54 @@ public void F(Buffer10 arg) additionalOperationTreeVerifier: new ExpectedSymbolVerifier().Verify); } + [CompilerTrait(CompilerFeature.IOperation)] + [Fact] + public void ConversionExpression_Implicit_ArrayToSpan() + { + var source = """ + class C + { + System.Span F(int[] arg) + { + System.Span /**/span = arg/**/; + return span; + } + } + """; + + var expectedOperationTree = """ + IVariableDeclaratorOperation (Symbol: System.Span span) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'span = arg') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= arg') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Span, IsImplicit) (Syntax: 'arg') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IParameterReferenceOperation: arg (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'arg') + """; + + var expectedDiagnostics = DiagnosticDescription.None; + + var comp = CreateCompilationWithSpan(source); + VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics, + additionalOperationTreeVerifier: new ExpectedSymbolVerifier().Verify); + + // In C# 12, the conversion is user-defined unlike above: + + expectedOperationTree = """ + IVariableDeclaratorOperation (Symbol: System.Span span) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'span = arg') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= arg') + IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Span System.Span.op_Implicit(System.Int32[] array)) (OperationKind.Conversion, Type: System.Span, IsImplicit) (Syntax: 'arg') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Span System.Span.op_Implicit(System.Int32[] array)) + Operand: + IParameterReferenceOperation: arg (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'arg') + """; + + comp = CreateCompilationWithSpan(source, parseOptions: TestOptions.Regular12); + VerifyOperationTreeAndDiagnosticsForTest(comp, expectedOperationTree, expectedDiagnostics, + additionalOperationTreeVerifier: new ExpectedSymbolVerifier().Verify); + } + #endregion #region Explicit Conversion diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs index 948e31354dbfb..405335091954b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs index 482521de337d1..e41eb24d079b8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/VarianceTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 42b3d2083af4e..25a04c445d097 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -957,12 +957,14 @@ 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/CodeAnalysisTest/Diagnostics/AnalysisContextInfoTests.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/AnalysisContextInfoTests.cs index d45da70ac553b..499fb1450ecd1 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/AnalysisContextInfoTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/AnalysisContextInfoTests.cs @@ -26,7 +26,7 @@ public void InitializeTest() var parseOptions = new CSharpParseOptions(kind: SourceCodeKind.Regular, documentationMode: DocumentationMode.None) .WithFeatures(new[] { new KeyValuePair("IOperation", "true") }); var compilation = CreateCompilation(code, parseOptions: parseOptions); - var options = new AnalyzerOptions(new[] { new TestAdditionalText() }.ToImmutableArray()); + var options = new AnalyzerOptions([new TestAdditionalText()]); Verify(compilation, options, nameof(AnalysisContext.RegisterCodeBlockAction)); Verify(compilation, options, nameof(AnalysisContext.RegisterCodeBlockStartAction)); diff --git a/src/Compilers/Core/Portable/InternalUtilities/ArrayExtensions.cs b/src/Compilers/Core/Portable/InternalUtilities/ArrayExtensions.cs index 9078c540e2db3..4f1b9628f71a3 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ArrayExtensions.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/ArrayExtensions.cs @@ -26,6 +26,12 @@ internal static T[] Copy(this T[] array, int start, int length) return newArray; } + public static int IndexOf(this T[] array, T value) + => Array.IndexOf(array, value); + + public static bool Contains(this T[] array, T value) + => Array.IndexOf(array, value) >= 0; + internal static T[] InsertAt(this T[] array, int position, T item) { T[] newArray = new T[array.Length + 1]; diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 1cdddef0d9097..0b45290ccf522 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -483,6 +483,7 @@ 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, @@ -491,6 +492,7 @@ 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 0fd77f299f9c2..0f76bbf06dd77 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3369,6 +3369,17 @@ 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 @@ -3431,6 +3442,17 @@ 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 @@ -4742,6 +4764,7 @@ 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 @@ -4749,6 +4772,7 @@ 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/Test/Core/TargetFrameworkUtil.cs b/src/Compilers/Test/Core/TargetFrameworkUtil.cs index 592cdb613b90f..00623f30f4e02 100644 --- a/src/Compilers/Test/Core/TargetFrameworkUtil.cs +++ b/src/Compilers/Test/Core/TargetFrameworkUtil.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.CodeGen; using System.Reflection; using System.Collections.Concurrent; +using Roslyn.Utilities; namespace Roslyn.Test.Utilities { diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 8388968457fb4..d660bee42e7fa 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -2328,6 +2328,8 @@ public ref T Current } public static implicit operator Span(T[] array) => new Span(array); + + public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(span.arr); } public readonly ref struct ReadOnlySpan diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 798d74ecf6e0e..0a8e5b8da33e9 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -707,12 +707,14 @@ 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, @@ -914,12 +916,14 @@ 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/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs index 10e0d8e5c2726..8928dcabe76d2 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Hover/ILspHoverResultCreationService.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.QuickInfo; using Roslyn.LanguageServer.Protocol; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { diff --git a/src/Tools/GenerateRulesMissingDocumentation/Program.cs b/src/Tools/GenerateRulesMissingDocumentation/Program.cs index 523cd91509a54..c8ed19cf4cde5 100644 --- a/src/Tools/GenerateRulesMissingDocumentation/Program.cs +++ b/src/Tools/GenerateRulesMissingDocumentation/Program.cs @@ -81,7 +81,7 @@ await checkHelpLinkAsync(helpLinkUri).ConfigureAwait(false)) { // We consider having "extra" entries as valid. This is to prevent CI failures due to rules being documented. // However, we consider "missing" entries as invalid. This is to force updating the file when new rules are added. - if (!actualContent.Contains(line)) + if (Array.IndexOf(actualContent, line) < 0) { await Console.Error.WriteLineAsync($"Missing entry in '{fileWithPath}'. Please add the below entry to this file to fix the build:").ConfigureAwait(false); await Console.Error.WriteLineAsync(line).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/ExportCodeRefactoringProviderAttribute.cs b/src/Workspaces/Core/Portable/CodeRefactorings/ExportCodeRefactoringProviderAttribute.cs index ccc282edc2fb3..b7dc5662aef4f 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/ExportCodeRefactoringProviderAttribute.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/ExportCodeRefactoringProviderAttribute.cs @@ -6,6 +6,7 @@ using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Mef/MefHostServices.cs b/src/Workspaces/Core/Portable/Workspace/Host/Mef/MefHostServices.cs index 9078fc1ef6412..3f0e3532e99d7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Mef/MefHostServices.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Mef/MefHostServices.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Reflection; using System.Threading; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/MSBuildTest/NewlyCreatedProjectsFromDotNetNew.cs b/src/Workspaces/MSBuildTest/NewlyCreatedProjectsFromDotNetNew.cs index f403ed97cfc89..ca70ed81a0f02 100644 --- a/src/Workspaces/MSBuildTest/NewlyCreatedProjectsFromDotNetNew.cs +++ b/src/Workspaces/MSBuildTest/NewlyCreatedProjectsFromDotNetNew.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ArrayExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ArrayExtensions.cs index ddee284eecaba..6dddc2904ceff 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ArrayExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ArrayExtensions.cs @@ -2,7 +2,6 @@ // 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.Diagnostics.CodeAnalysis; namespace Microsoft.CodeAnalysis.Shared.Extensions; @@ -11,7 +10,4 @@ internal static class ArrayExtensions { public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] this T[]? array) => array == null || array.Length == 0; - - public static bool Contains(this T[] array, T item) - => Array.IndexOf(array, item) >= 0; }