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
+
+
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
+
+
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
+
+
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
+
+
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
+
+
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
+
+
汎用属性
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
+
+
제네릭 특성
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
+
+
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
+
+
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
+
+
универсальные атрибуты
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
+
+
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
+
+
通用属性
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
+
+
一般屬性
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