diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 9310fe215b6ef..677e658dbb601 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -487,51 +487,214 @@ void checkConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNode syn } else if (conversion.IsSpan) { - Debug.Assert(destination.OriginalDefinition.IsSpan() || destination.OriginalDefinition.IsReadOnlySpan()); + Debug.Assert(source.Type is not null); + Debug.Assert(destination.IsSpan() || destination.IsReadOnlySpan()); CheckFeatureAvailability(syntax, MessageID.IDS_FeatureFirstClassSpan, diagnostics); - // PROTOTYPE: Check runtime APIs used for other span conversions once they are implemented. // NOTE: We cannot use well-known members because per the spec // the Span types involved in the Span conversions can be any that match the type name. - if (TryFindImplicitOperatorFromArray(destination.OriginalDefinition) is { } method) + + // Span.op_Implicit(T[]) or ReadOnlySpan.op_Implicit(T[]) + if (source.Type is ArrayTypeSymbol) { - diagnostics.ReportUseSite(method, syntax); + reportUseSiteOrMissing( + TryFindImplicitOperatorFromArray(destination.OriginalDefinition), + destination.OriginalDefinition, + WellKnownMemberNames.ImplicitConversionName, + syntax, + diagnostics); } - else + + // ReadOnlySpan Span.op_Implicit(Span) + if (source.Type.IsSpan()) { - Error(diagnostics, - ErrorCode.ERR_MissingPredefinedMember, + Debug.Assert(destination.IsReadOnlySpan()); + reportUseSiteOrMissing( + TryFindImplicitOperatorFromSpan(source.Type.OriginalDefinition, destination.OriginalDefinition), + source.Type.OriginalDefinition, + WellKnownMemberNames.ImplicitConversionName, syntax, - destination.OriginalDefinition, - WellKnownMemberNames.ImplicitConversionName); + diagnostics); + } + + // ReadOnlySpan ReadOnlySpan.CastUp(ReadOnlySpan) + if (source.Type.IsSpan() || source.Type.IsReadOnlySpan()) + { + Debug.Assert(destination.IsReadOnlySpan()); + if (NeedsSpanCastUp(source.Type, destination)) + { + // If converting Span -> ROS -> ROS, + // the source of the CastUp is the return type of the op_Implicit (i.e., the ROS) + // which has the same original definition as the destination ROS. + TypeSymbol sourceForCastUp = source.Type.IsSpan() + ? destination.OriginalDefinition + : source.Type.OriginalDefinition; + + MethodSymbol? castUpMethod = TryFindCastUpMethod(sourceForCastUp, destination.OriginalDefinition); + reportUseSiteOrMissing( + castUpMethod, + destination.OriginalDefinition, + WellKnownMemberNames.CastUpMethodName, + syntax, + diagnostics); + castUpMethod? + .AsMember((NamedTypeSymbol)destination) + .Construct([((NamedTypeSymbol)source.Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]]) + .CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(Compilation, Conversions, includeNullability: false, syntax.Location, diagnostics)); + } + } + + // ReadOnlySpan MemoryExtensions.AsSpan(string) + if (source.Type.IsStringType()) + { + reportUseSiteOrMissing( + TryFindAsSpanCharMethod(Compilation, destination), + WellKnownMemberNames.MemoryExtensionsTypeFullName, + WellKnownMemberNames.AsSpanMethodName, + syntax, + diagnostics); } } } + + static void reportUseSiteOrMissing(MethodSymbol? method, object containingType, string methodName, SyntaxNode syntax, BindingDiagnosticBag diagnostics) + { + if (method is not null) + { + diagnostics.ReportUseSite(method, syntax); + } + else + { + Error(diagnostics, + ErrorCode.ERR_MissingPredefinedMember, + syntax, + containingType, + methodName); + } + } } + // {type}.op_Implicit(T[]) internal static MethodSymbol? TryFindImplicitOperatorFromArray(TypeSymbol type) { Debug.Assert(type.IsSpan() || type.IsReadOnlySpan()); + Debug.Assert(type.IsDefinition); + + return TryFindImplicitOperator(type, 0, static (_, method) => + method.Parameters[0].Type is ArrayTypeSymbol { IsSZArray: true, ElementType: TypeParameterSymbol }); + } + + // ReadOnlySpan Span.op_Implicit(Span) + internal static MethodSymbol? TryFindImplicitOperatorFromSpan(TypeSymbol spanType, TypeSymbol readonlySpanType) + { + Debug.Assert(spanType.IsSpan() && readonlySpanType.IsReadOnlySpan()); + Debug.Assert(spanType.IsDefinition && readonlySpanType.IsDefinition); + + return TryFindImplicitOperator(spanType, readonlySpanType, + static (readonlySpanType, method) => method.Parameters[0].Type.IsSpan() && + readonlySpanType.Equals(method.ReturnType.OriginalDefinition, TypeCompareKind.ConsiderEverything)); + } - return TryFindSingleMember(type, WellKnownMemberNames.ImplicitConversionName, - static (method) => method is + private static MethodSymbol? TryFindImplicitOperator(TypeSymbol type, TArg arg, + Func predicate) + { + return TryFindSingleMethod(type, WellKnownMemberNames.ImplicitConversionName, (predicate, arg), + static (arg, method) => method is { ParameterCount: 1, Arity: 0, IsStatic: true, DeclaredAccessibility: Accessibility.Public, - Parameters: [{ Type: ArrayTypeSymbol { IsSZArray: true, ElementType: TypeParameterSymbol } }] - }); + } && arg.predicate(arg.arg, method)); + } + + internal static bool NeedsSpanCastUp(TypeSymbol source, TypeSymbol destination) + { + Debug.Assert(source.IsSpan() || source.IsReadOnlySpan()); + Debug.Assert(destination.IsReadOnlySpan()); + Debug.Assert(!source.IsDefinition && !destination.IsDefinition); + + var sourceElementType = ((NamedTypeSymbol)source).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type; + var destinationElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type; + + var sameElementTypes = sourceElementType.Equals(destinationElementType, TypeCompareKind.AllIgnoreOptions); + + Debug.Assert(!source.IsReadOnlySpan() || !sameElementTypes); + + return !sameElementTypes; + } + + // ReadOnlySpan ReadOnlySpan.CastUp(ReadOnlySpan) where TDerived : class + internal static MethodSymbol? TryFindCastUpMethod(TypeSymbol source, TypeSymbol destination) + { + Debug.Assert(source.IsReadOnlySpan() && destination.IsReadOnlySpan()); + Debug.Assert(source.IsDefinition && destination.IsDefinition); + + return TryFindSingleMethod(destination, WellKnownMemberNames.CastUpMethodName, (source, destination), + static (arg, method) => method is + { + ParameterCount: 1, + Arity: 1, + IsStatic: true, + DeclaredAccessibility: Accessibility.Public, + Parameters: [{ } parameter], + TypeArgumentsWithAnnotations: [{ } typeArgument], + } && + // parameter type is the source ReadOnlySpan<> + arg.source.Equals(parameter.Type.OriginalDefinition, TypeCompareKind.ConsiderEverything) && + // return type is the destination ReadOnlySpan<> + arg.destination.Equals(method.ReturnType.OriginalDefinition, TypeCompareKind.ConsiderEverything) && + // TDerived : class + typeArgument.Type.IsReferenceType && + // parameter type argument is TDerived + ((NamedTypeSymbol)parameter.Type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type.Equals(typeArgument.Type, TypeCompareKind.ConsiderEverything) && + // return type argument is T + ((NamedTypeSymbol)method.ReturnType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type.Equals(((NamedTypeSymbol)arg.destination).TypeParameters[0], TypeCompareKind.ConsiderEverything)); + } + + // ReadOnlySpan MemoryExtensions.AsSpan(string) + internal static MethodSymbol? TryFindAsSpanCharMethod(CSharpCompilation compilation, TypeSymbol readOnlySpanType) + { + Debug.Assert(readOnlySpanType.IsReadOnlySpan()); + Debug.Assert(!readOnlySpanType.IsDefinition); + Debug.Assert(((NamedTypeSymbol)readOnlySpanType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].SpecialType is SpecialType.System_Char); + + MethodSymbol? result = null; + foreach (var memoryExtensionsType in compilation.GetTypesByMetadataName(WellKnownMemberNames.MemoryExtensionsTypeFullName)) + { + if (memoryExtensionsType.DeclaredAccessibility == Accessibility.Public && + TryFindSingleMethod(memoryExtensionsType.GetSymbol(), WellKnownMemberNames.AsSpanMethodName, 0, + static (_, method) => method is + { + ParameterCount: 1, + Arity: 0, + IsStatic: true, + DeclaredAccessibility: Accessibility.Public, + Parameters: [{ Type.SpecialType: SpecialType.System_String }] + }) is { } method && + method.ReturnType.Equals(readOnlySpanType, TypeCompareKind.ConsiderEverything)) + { + if (result is not null) + { + // Ambiguous member found. + return null; + } + + result = method; + } + } + + return result; } - private static MethodSymbol? TryFindSingleMember(TypeSymbol type, string name, Func predicate) + private static MethodSymbol? TryFindSingleMethod(TypeSymbol type, string name, TArg arg, Func predicate) { var members = type.GetMembers(name); MethodSymbol? result = null; foreach (var member in members) { - if (member is MethodSymbol method && predicate(method)) + if (member is MethodSymbol method && predicate(arg, method)) { if (result is not null) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 51a9c2b7b699b..bec89470a71cb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -3953,7 +3953,7 @@ private bool IsFeatureFirstClassSpanEnabled private bool HasImplicitSpanConversion(TypeSymbol? source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { - if (!IsFeatureFirstClassSpanEnabled) + if (source is null || !IsFeatureFirstClassSpanEnabled) { return false; } @@ -3962,19 +3962,39 @@ private bool HasImplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio if (source is ArrayTypeSymbol { IsSZArray: true, ElementTypeWithAnnotations: { } elementType }) { // SPEC: ...to `System.Span`. - if (destination.OriginalDefinition.IsSpan()) + if (destination.IsSpan()) { var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; return hasIdentityConversion(elementType, spanElementType); } // SPEC: ...to `System.ReadOnlySpan`, provided that `Ei` is covariance-convertible to `Ui`. - if (destination.OriginalDefinition.IsReadOnlySpan()) + if (destination.IsReadOnlySpan()) { var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; return hasCovariantConversion(elementType, spanElementType, ref useSiteInfo); } } + // SPEC: From `System.Span` to `System.ReadOnlySpan`, provided that `Ti` is covariance-convertible to `Ui`. + // SPEC: From `System.ReadOnlySpan` to `System.ReadOnlySpan`, provided that `Ti` is covariance-convertible to `Ui`. + else if (source.IsSpan() || source.IsReadOnlySpan()) + { + if (destination.IsReadOnlySpan()) + { + var sourceElementType = ((NamedTypeSymbol)source).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; + var destinationElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; + return hasCovariantConversion(sourceElementType, destinationElementType, ref useSiteInfo); + } + } + // SPEC: From `string` to `System.ReadOnlySpan`. + else if (source.IsStringType()) + { + if (destination.IsReadOnlySpan()) + { + var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; + return spanElementType.SpecialType is SpecialType.System_Char; + } + } return false; @@ -4005,7 +4025,7 @@ private bool HasExplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio // to `System.Span` or `System.ReadOnlySpan` // provided an explicit reference conversion exists from `Ti` to `Ui`. if (source is ArrayTypeSymbol { IsSZArray: true, ElementTypeWithAnnotations: { } elementType } && - (destination.OriginalDefinition.IsSpan() || destination.OriginalDefinition.IsReadOnlySpan())) + (destination.IsSpan() || destination.IsReadOnlySpan())) { var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; return HasIdentityOrReferenceConversion(elementType.Type, spanElementType.Type, ref useSiteInfo) && @@ -4017,26 +4037,12 @@ private bool HasExplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio private bool IgnoreUserDefinedSpanConversions(TypeSymbol? source, TypeSymbol? target) { + // SPEC: User-defined conversions are not considered when converting between types + // for which an implicit or an explicit span conversion exists. + var discarded = CompoundUseSiteInfo.Discarded; return source is not null && target is not null && - IsFeatureFirstClassSpanEnabled && - (ignoreUserDefinedSpanConversionsInOneDirection(source, target) || - ignoreUserDefinedSpanConversionsInOneDirection(target, source)); - - static bool ignoreUserDefinedSpanConversionsInOneDirection(TypeSymbol a, TypeSymbol b) - { - // SPEC: User-defined conversions are not considered when converting between - // SPEC: - any single-dimensional `array_type` and `System.Span`/`System.ReadOnlySpan` - if (a is ArrayTypeSymbol { IsSZArray: true } && - (b.OriginalDefinition.IsSpan() || b.OriginalDefinition.IsReadOnlySpan())) - { - return true; - } - - // PROTOTYPE: - any combination of `System.Span`/`System.ReadOnlySpan` - // PROTOTYPE: - `string` and `System.ReadOnlySpan` - - return false; - } + (HasImplicitSpanConversion(source, target, ref discarded) || + HasExplicitSpanConversion(source, target, ref discarded)); } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 3be4f5f03c861..92f245f3dc41b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8904,8 +8904,8 @@ private TypeWithState VisitConversion( { var previousKind = conversion.Kind; conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument, isChecked: conversionOpt?.Checked ?? false); - Debug.Assert(!conversion.Exists || conversion.Kind == previousKind); - canConvertNestedNullability = conversion.Exists; + // We do not want user-defined conversions to relax nullability, so we consider only span conversions. + canConvertNestedNullability = conversion.Exists && conversion.IsSpan; } break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index b276230485812..fd02ebbb224b0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -617,25 +617,98 @@ private BoundExpression MakeConversionNodeCore( case ConversionKind.ImplicitSpan: case ConversionKind.ExplicitSpan: { - var spanType = (NamedTypeSymbol)rewrittenType; + var sourceType = rewrittenOperand.Type; + var destinationType = (NamedTypeSymbol)rewrittenType; - if (Binder.TryFindImplicitOperatorFromArray(spanType.OriginalDefinition) is not { } methodDefinition) + Debug.Assert(sourceType is not null); + + // array to Span/ReadOnlySpan (implicit or explicit) + if (sourceType is ArrayTypeSymbol) { - throw ExceptionUtilities.Unreachable(); + if (Binder.TryFindImplicitOperatorFromArray(destinationType.OriginalDefinition) is not { } methodDefinition) + { + throw ExceptionUtilities.Unreachable(); + } + + MethodSymbol method = methodDefinition.AsMember(destinationType); + + rewrittenOperand = _factory.Convert(method.ParameterTypesWithAnnotations[0].Type, rewrittenOperand); + + if (!_inExpressionLambda && _compilation.IsReadOnlySpanType(destinationType)) + { + return new BoundReadOnlySpanFromArray(syntax, rewrittenOperand, method, destinationType) { WasCompilerGenerated = true }; + } + + return _factory.Call(null, method, rewrittenOperand); } - else + + // Span to ReadOnlySpan (implicit only) + if (sourceType.IsSpan()) { - MethodSymbol method = methodDefinition.AsMember(spanType); + Debug.Assert(destinationType.IsReadOnlySpan()); + Debug.Assert(conversion.Kind is ConversionKind.ImplicitSpan); + + if (Binder.TryFindImplicitOperatorFromSpan(sourceType.OriginalDefinition, destinationType.OriginalDefinition) is not { } implicitOperatorDefinition) + { + throw ExceptionUtilities.Unreachable(); + } + + MethodSymbol implicitOperator = implicitOperatorDefinition.AsMember((NamedTypeSymbol)sourceType); + + rewrittenOperand = _factory.Convert(implicitOperator.ParameterTypesWithAnnotations[0].Type, rewrittenOperand); + rewrittenOperand = _factory.Call(null, implicitOperator, rewrittenOperand); + + if (Binder.NeedsSpanCastUp(sourceType, destinationType)) + { + if (Binder.TryFindCastUpMethod(implicitOperator.ReturnType.OriginalDefinition, destinationType.OriginalDefinition) is not { } castUpMethodDefinition) + { + throw ExceptionUtilities.Unreachable(); + } + + TypeWithAnnotations sourceElementType = ((NamedTypeSymbol)sourceType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]; + MethodSymbol castUpMethod = castUpMethodDefinition.AsMember(destinationType).Construct([sourceElementType]); + + return _factory.Call(null, castUpMethod, rewrittenOperand); + } + + return rewrittenOperand; + } + + // ReadOnlySpan to ReadOnlySpan (implicit only) + if (sourceType.IsReadOnlySpan()) + { + Debug.Assert(destinationType.IsReadOnlySpan()); + Debug.Assert(conversion.Kind is ConversionKind.ImplicitSpan); + Debug.Assert(Binder.NeedsSpanCastUp(sourceType, destinationType)); + + if (Binder.TryFindCastUpMethod(sourceType.OriginalDefinition, destinationType.OriginalDefinition) is not { } methodDefinition) + { + throw ExceptionUtilities.Unreachable(); + } + + TypeWithAnnotations sourceElementType = ((NamedTypeSymbol)sourceType).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0]; + MethodSymbol method = methodDefinition.AsMember(destinationType).Construct([sourceElementType]); rewrittenOperand = _factory.Convert(method.ParameterTypesWithAnnotations[0].Type, rewrittenOperand); + return _factory.Call(null, method, rewrittenOperand); + } - if (!_inExpressionLambda && _compilation.IsReadOnlySpanType(spanType)) + // string to ReadOnlySpan (implicit only) + if (sourceType.IsStringType()) + { + Debug.Assert(destinationType.IsReadOnlySpan()); + Debug.Assert(conversion.Kind is ConversionKind.ImplicitSpan); + + if (Binder.TryFindAsSpanCharMethod(_compilation, destinationType) is not { } method) { - return new BoundReadOnlySpanFromArray(syntax, rewrittenOperand, method, spanType) { WasCompilerGenerated = true }; + throw ExceptionUtilities.Unreachable(); } + rewrittenOperand = _factory.Convert(method.ParameterTypesWithAnnotations[0].Type, rewrittenOperand); return _factory.Call(null, method, rewrittenOperand); } + + throw ExceptionUtilities.Unreachable(); } default: diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadOnlySpanConstructionTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadOnlySpanConstructionTest.cs index 2e808a3d5ec6f..67b214853ab3a 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadOnlySpanConstructionTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadOnlySpanConstructionTest.cs @@ -135,10 +135,10 @@ .maxstack 2 .locals init (System.ReadOnlySpan V_0, //s1 System.ReadOnlySpan V_1) //s2 IL_0000: ldstr """" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldstr """" - IL_0010: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0010: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_0015: stloc.1 IL_0016: ldloca.s V_0 IL_0018: call ""int System.ReadOnlySpan.Length.get"" @@ -147,10 +147,10 @@ .locals init (System.ReadOnlySpan V_0, //s1 IL_0024: ceq IL_0026: call ""void System.Console.Write(bool)"" IL_002b: ldnull - IL_002c: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_002c: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_0031: stloc.0 IL_0032: ldnull - IL_0033: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0033: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_0038: stloc.1 IL_0039: ldloca.s V_0 IL_003b: call ""int System.ReadOnlySpan.Length.get"" @@ -612,7 +612,7 @@ public static void Test1(ReadOnlySpan arg1, ReadOnlySpan arg2) // Code size 28 (0x1c) .maxstack 3 IL_0000: ldstr ""QWERTYUIOP"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: ldsflda "".__StaticArrayInitTypeSize=10 .C848E1013F9F04A9D63FA43CE7FD4AF035152C7C669A4A404B67107CEE5F2E4E"" IL_000f: ldc.i4.s 10 IL_0011: newobj ""System.ReadOnlySpan..ctor(void*, int)"" diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/ForeachTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/ForeachTest.cs index 92fb52b297fc0..4eee673fa0ff1 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/ForeachTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/ForeachTest.cs @@ -504,7 +504,7 @@ .maxstack 2 .locals init (System.ReadOnlySpan V_0, int V_1) IL_0000: ldstr ""hello"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.0 IL_000c: stloc.1 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs index 0749e47f7df78..8d3003d88174b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs @@ -826,7 +826,7 @@ public static void Main() } } }"; - var comp = CreateCompilationWithIndexAndRangeAndSpan(src, TestOptions.ReleaseExe); + var comp = CreateCompilationWithIndexAndRangeAndSpanAndMemoryExtensions(src, TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: @" abcd abcd"); @@ -848,7 +848,7 @@ .locals init (System.ReadOnlySpan V_0, //span IL_000a: callvirt ""int string.Length.get"" IL_000f: callvirt ""string string.Substring(int, int)"" IL_0014: call ""void System.Console.WriteLine(string)"" - IL_0019: call ""System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(string)"" + IL_0019: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_001e: stloc.0 IL_001f: ldloca.s V_0 IL_0021: stloc.s V_4 @@ -1081,7 +1081,7 @@ .locals init (System.Span V_0) //s [Fact] public void PatternIndexAndRangeSpanChar() { - var comp = CreateCompilationWithIndexAndRangeAndSpan(@" + var comp = CreateCompilationWithIndexAndRangeAndSpanAndMemoryExtensions(@" using System; class C { @@ -1110,7 +1110,7 @@ .locals init (System.ReadOnlySpan V_0, //s int V_3, int V_4) IL_0000: ldstr ""abcdefg"" - IL_0005: call ""System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_0 IL_000d: dup diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs index 745c0e4d1c167..6df4ef7aef6c9 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs @@ -17019,12 +17019,22 @@ static void Main() } } "; - var comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src + Buffer10Definition, parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (16,31): error CS0457: Ambiguous user defined conversions 'C.implicit operator C(ReadOnlySpan)' and 'C.implicit operator C(Span)' when converting from 'Buffer10' to 'C' // System.Console.Write(((C)b).F); Diagnostic(ErrorCode.ERR_AmbigUDConv, "(C)b").WithArguments("C.implicit operator C(System.ReadOnlySpan)", "C.implicit operator C(System.Span)", "Buffer10", "C").WithLocation(16, 31) ); + + // NOTE: No longer ambiguous because there is a standard implicit span conversion from Span to ReadOnlySpan which makes the Span operator better. + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "110" : null; + + comp = CreateCompilation(src + Buffer10Definition, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + + comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); } [Fact] @@ -17054,12 +17064,27 @@ static void Test(in Buffer10 b) } } "; - var comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(src + Buffer10Definition, parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // (21,31): error CS0457: Ambiguous user defined conversions 'C.implicit operator C(ReadOnlySpan)' and 'C.implicit operator C(Span)' when converting from 'Buffer10' to 'C' // System.Console.Write(((C)b).F); Diagnostic(ErrorCode.ERR_AmbigUDConv, "(C)b").WithArguments("C.implicit operator C(System.ReadOnlySpan)", "C.implicit operator C(System.Span)", "Buffer10", "C").WithLocation(21, 31) ); + + // NOTE: No longer ambiguous because there is a standard implicit span conversion from Span to ReadOnlySpan which makes the Span operator better. + + var expectedDiagnostics = new[] + { + // (21,34): error CS9164: Cannot convert expression to 'Span' because it is not an assignable variable + // System.Console.Write(((C)b).F); + Diagnostic(ErrorCode.ERR_InlineArrayConversionToSpanNotSupported, "b").WithArguments("System.Span").WithLocation(21, 34) + }; + + comp = CreateCompilation(src + Buffer10Definition, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs index c31180383fb4a..514fafa67a90b 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests.cs @@ -7512,7 +7512,7 @@ .maxstack 2 .locals init (System.ReadOnlySpan V_0, //chars int V_1) IL_0000: ldstr ""string 2"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_0 IL_000d: call ""int System.ReadOnlySpan.Length.get"" diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs index 6cdc9572dc7d3..c9e40b28b64ba 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs @@ -7117,10 +7117,7 @@ struct Example() Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), - // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope - // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); } public static IEnumerable ParameterScope_MemberData() diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index 19dd483b3b044..14050ec846eae 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -323,145 +323,722 @@ .maxstack 1 """); } - [Fact] - public void Conversion_Array_Span_Implicit_MissingHelper() + [Theory, CombinatorialData] + public void Conversion_Span_ReadOnlySpan_Implicit( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) { - var source = """ + var source = $$""" using System; - Span s = arr(); - static int[] arr() => new int[] { 1, 2, 3 }; + ReadOnlySpan s = {{(cast ? "(ReadOnlySpan)" : "")}}source(); + report(s); + static Span source() => new Span(new int[] { 1, 2, 3 }); + static void report(ReadOnlySpan s) { foreach (var x in s) { Console.Write(x); } } + """; - namespace System + var expectedOutput = "123"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ { - public readonly ref struct Span - { - } + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_000f: ret } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit(bool cast) + { + var source = $$""" + using System; + ReadOnlySpan s = {{(cast ? "(ReadOnlySpan)" : "")}}source(); + report(s); + static Span source() => new Span(new[] { "a", "b" }); + static void report(ReadOnlySpan s) { foreach (var x in s) { Console.Write(x); } } """; - CreateCompilation(source).VerifyDiagnostics( - // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' - // Span s = arr(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + if (!cast) + { + comp.VerifyDiagnostics( + // (2,26): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(2, 26)); + } + else + { + comp.VerifyDiagnostics( + // (2,26): error CS0030: Cannot convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan s = (ReadOnlySpan)source(); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)source()").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(2, 26)); + } + + var expectedOutput = "ab"; + + var expectedIL = """ + { + // Code size 21 (0x15) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000f: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_0014: ret + } + """; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", expectedIL); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", expectedIL); } [Theory, CombinatorialData] - public void Conversion_Array_Span_Implicit_DifferentOperator( - [CombinatorialValues("int[]", "T[][]")] string parameterType) + public void Conversion_Span_ReadOnlySpan_Implicit_IdentityInnerConversion( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) { var source = $$""" using System; - Span s = arr(); - static int[] arr() => new int[] { 1, 2, 3 }; + ReadOnlySpan s = {{(cast ? "(ReadOnlySpan)" : "")}}source(); + report(s); + static Span source() => new Span(new object[] { "a", "b" }); + static void report(ReadOnlySpan s) { foreach (var x in s) { Console.Write(x); } } + """; - namespace System + var expectedOutput = "ab"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ { - public readonly ref struct Span - { - public static implicit operator Span({{parameterType}} array) => throw null; - } + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_000f: ret } + """); + } + + [Theory, CombinatorialData] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit(bool cast) + { + var source = $$""" + using System; + ReadOnlySpan s = {{(cast ? "(ReadOnlySpan)" : "")}}source(); + report(s); + static ReadOnlySpan source() => new ReadOnlySpan(new[] { "a", "b" }); + static void report(ReadOnlySpan s) { foreach (var x in s) { Console.Write(x); } } """; - CreateCompilation(source).VerifyDiagnostics( - // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' - // Span s = arr(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + if (!cast) + { + comp.VerifyDiagnostics( + // (2,26): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(2, 26)); + } + else + { + comp.VerifyDiagnostics( + // (2,26): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan s = (ReadOnlySpan)source(); + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)source()").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(2, 26)); + } + + var expectedOutput = "ab"; + + var expectedIL = """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "System.ReadOnlySpan Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000a: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_000f: ret + } + """; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", expectedIL); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", expectedIL); + } + + [Theory, CombinatorialData] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_IdentityInnerConversion( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) + { + var source = $$""" + using System; + ReadOnlySpan s = {{(cast ? "(ReadOnlySpan)" : "")}}source(); + report(s); + static ReadOnlySpan source() => new ReadOnlySpan(new string[] { "a", "b" }); + static void report(ReadOnlySpan s) { foreach (var x in s) { Console.Write(x); } } + """; + + var expectedOutput = "ab"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 11 (0xb) + .maxstack 1 + IL_0000: call "System.ReadOnlySpan Program.<
$>g__source|0_0()" + IL_0005: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_000a: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_string_ReadOnlySpan_Implicit(bool cast) + { + var source = $$""" + using System; + ReadOnlySpan s = {{(cast ? "(ReadOnlySpan)" : "")}}source(); + report(s); + static string source() => "abc"; + static void report(ReadOnlySpan s) { foreach (var x in s) { Console.Write(x + " "); } } + """; + + var expectedOutput = "a b c"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + var owner = ExecutionConditionUtil.IsCoreClr ? "string" : "System.ReadOnlySpan"; + verifier.VerifyIL("", $$""" + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan {{owner}}.op_Implicit(string)" + IL_000a: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_000f: ret + } + """); + + var expectedIL = """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" + IL_000a: call "void Program.<
$>g__report|0_1(System.ReadOnlySpan)" + IL_000f: ret + } + """; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", expectedIL); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", expectedIL); } [Fact] - public void Conversion_Array_Span_Implicit_ExplicitOperator() + public void Conversion_Array_Span_Implicit_MissingHelper() { var source = """ using System; Span s = arr(); - static int[] arr() => new int[] { 1, 2, 3 }; + static int[] arr() => throw null; namespace System { public readonly ref struct Span { - public static explicit operator Span(T[] array) => throw null; } } """; - CreateCompilation(source).VerifyDiagnostics( + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,15): error CS0029: Cannot implicitly convert type 'int[]' to 'System.Span' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arr()").WithArguments("int[]", "System.Span").WithLocation(2, 15)); + + var expectedDiagnostics = new[] + { // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' // Span s = arr(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); } [Fact] - public void Conversion_Array_Span_Explicit_ExplicitOperator() + public void Conversion_Span_ReadOnlySpan_Implicit_MissingHelper() { var source = """ using System; - Span s = (Span)arr(); - static int[] arr() => new int[] { 1, 2, 3 }; + ReadOnlySpan s = source(); + static Span source() => throw null; namespace System { - public readonly ref struct Span - { - public static explicit operator Span(T[] array) => throw null; - } + public readonly ref struct Span { } + public readonly ref struct ReadOnlySpan { } } """; - CreateCompilation(source).VerifyDiagnostics( - // (2,15): error CS0656: Missing compiler required member 'Span.op_Implicit' - // Span s = (Span)arr(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "(Span)arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,23): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(2, 23)); + + var expectedDiagnostics = new[] + { + // (2,23): error CS0656: Missing compiler required member 'Span.op_Implicit' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 23) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); } [Fact] - public void Conversion_Array_Span_Implicit_UnrecognizedModreq() + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MissingHelper_01() { - /* - public struct Span - { - public static implicit operator modreq(A) Span(T[] array) => throw null; - } - public class A { } - public static class C - { - public static void M(Span s) { } - } - */ - var ilSource = """ - .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType - { - .pack 0 - .size 1 - .method public hidebysig specialname static valuetype System.Span`1 modreq(A) op_Implicit(!T[] 'array') cil managed - { - .maxstack 1 - ret - } - } - .class public auto ansi sealed beforefieldinit A extends System.Object + var source = """ + using System; + ReadOnlySpan s = source(); + static Span source() => throw null; + + namespace System { + public readonly ref struct Span { } + public readonly ref struct ReadOnlySpan { } } - .class public auto ansi abstract sealed beforefieldinit C extends System.Object + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,26): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(2, 26)); + + var expectedDiagnostics = new[] + { + // (2,26): error CS0656: Missing compiler required member 'Span.op_Implicit' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 26), + // (2,26): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 26) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MissingHelper_02() + { + var source = """ + using System; + ReadOnlySpan s = source(); + static Span source() => throw null; + + namespace System { - .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + public readonly ref struct Span { - .maxstack 1 - ret + public static implicit operator ReadOnlySpan(Span s) => throw null; } + public readonly ref struct ReadOnlySpan { } } """; - var source = """ - C.M(new int[] { 1, 2, 3 }); - """; - CreateCompilationWithIL(source, ilSource).VerifyDiagnostics( - // (1,5): error CS0570: 'Span.implicit operator Span(T[])' is not supported by the language - // C.M(new int[] { 1, 2, 3 }); - Diagnostic(ErrorCode.ERR_BindToBogus, "new int[] { 1, 2, 3 }").WithArguments("System.Span.implicit operator System.Span(T[])").WithLocation(1, 5)); + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,26): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(2, 26)); + + var expectedDiagnostics = new[] + { + // (2,26): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 26) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); } [Fact] - public void Conversion_Array_Span_Implicit_UnrecognizedModopt() + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MissingHelper_03() { - /* - public struct Span + var source = """ + using System; + ReadOnlySpan s = source(); + static Span source() => throw null; + + namespace System + { + public readonly ref struct Span { } + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan s) where TDerived : class, T => throw null; + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,26): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(2, 26)); + + var expectedDiagnostics = new[] + { + // (2,26): error CS0656: Missing compiler required member 'Span.op_Implicit' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 26) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MissingHelper() + { + var source = """ + using System; + ReadOnlySpan s = source(); + static ReadOnlySpan source() => throw null; + + namespace System + { + public readonly ref struct ReadOnlySpan { } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,26): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(2, 26)); + + var expectedDiagnostics = new[] + { + // (2,26): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 26) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_MissingHelper() + { + var source = """ + using System; + ReadOnlySpan s = source(); + static string source() => throw null; + + namespace System + { + public readonly ref struct ReadOnlySpan { } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (2,24): error CS0029: Cannot implicitly convert type 'string' to 'System.ReadOnlySpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "source()").WithArguments("string", "System.ReadOnlySpan").WithLocation(2, 24)); + + var expectedDiagnostics = new[] + { + // (2,24): error CS0656: Missing compiler required member 'System.MemoryExtensions.AsSpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.MemoryExtensions", "AsSpan").WithLocation(2, 24) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_DifferentOperator( + [CombinatorialValues("int[]", "T[][]")] string parameterType) + { + var source = $$""" + using System; + Span s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static implicit operator Span({{parameterType}} array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Theory] + [InlineData("ReadOnlySpan AsSpan(string s)", true)] + [InlineData("ReadOnlySpan AsSpan(string s)", false)] + [InlineData("ReadOnlySpan AsSpan(string s)", false)] + [InlineData("ReadOnlySpan AsSpan(object o)", false)] + [InlineData("Span AsSpan(string s)", false)] + public void Conversion_string_ReadOnlySpan_Implicit_DifferentHelper(string signature, bool works) + { + var source = $$""" + using System; + ReadOnlySpan s = source(); + static string source() => ""; + + namespace System + { + public readonly ref struct ReadOnlySpan { } + public readonly ref struct Span { } + public static class MemoryExtensions + { + public static {{signature}} => default; + } + } + """; + var comp = CreateCompilation(source); + if (works) + { + // Make sure the code above can work so we are testing something useful. + comp.VerifyDiagnostics(); + } + else + { + comp.VerifyDiagnostics( + // (2,24): error CS0656: Missing compiler required member 'System.MemoryExtensions.AsSpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.MemoryExtensions", "AsSpan").WithLocation(2, 24)); + } + } + + [Theory, CombinatorialData] + public void Conversion_string_ReadOnlySpan_Implicit_HelperAccessibility_01( + [CombinatorialValues("public", "internal")] string classModifier, + [CombinatorialValues("public", "internal")] string methodModifier) + { + var source = $$""" + using System; + ReadOnlySpan s = source(); + static string source() => ""; + + namespace System + { + public readonly ref struct ReadOnlySpan { } + {{classModifier}} static class MemoryExtensions + { + {{methodModifier}} static ReadOnlySpan AsSpan(string s) => default; + } + } + """; + var comp = CreateCompilation(source); + if (classModifier == "internal" || methodModifier == "internal") + { + comp.VerifyDiagnostics( + // (2,24): error CS0656: Missing compiler required member 'System.MemoryExtensions.AsSpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.MemoryExtensions", "AsSpan").WithLocation(2, 24)); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_HelperAccessibility_02() + { + var source1 = """ + namespace System; + public readonly ref struct ReadOnlySpan; + internal static class MemoryExtensions + { + public static ReadOnlySpan AsSpan(string s) => default; + } + """; + var comp1 = CreateCompilation(source1).VerifyDiagnostics().EmitToImageReference(); + + var source2 = """ + using System; + ReadOnlySpan s1 = source(); + ReadOnlySpan s2 = MemoryExtensions.AsSpan(source()); + static string source() => ""; + """; + CreateCompilation(source2, [comp1]).VerifyDiagnostics( + // (2,25): error CS0656: Missing compiler required member 'System.MemoryExtensions.AsSpan' + // ReadOnlySpan s1 = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.MemoryExtensions", "AsSpan").WithLocation(2, 25), + // (3,25): error CS0122: 'MemoryExtensions' is inaccessible due to its protection level + // ReadOnlySpan s2 = MemoryExtensions.AsSpan(source()); + Diagnostic(ErrorCode.ERR_BadAccess, "MemoryExtensions").WithArguments("System.MemoryExtensions").WithLocation(3, 25)); + } + + [Theory, CombinatorialData] + public void Conversion_string_ReadOnlySpan_Implicit_RedefinedHelper( + [CombinatorialValues("public", "internal")] string classModifier, + [CombinatorialValues("public", "internal")] string methodModifier) + { + var source = $$""" + using System; + ReadOnlySpan s = source(); + static string source() => ""; + + namespace System + { + {{classModifier}} static class MemoryExtensions + { + {{methodModifier}} static ReadOnlySpan AsSpan(string s) => default; + } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + if (classModifier == "public" && methodModifier == "public") + { + comp.VerifyDiagnostics( + // (2,24): error CS0656: Missing compiler required member 'System.MemoryExtensions.AsSpan' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.MemoryExtensions", "AsSpan").WithLocation(2, 24)); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Fact] + public void Conversion_Array_Span_Implicit_ExplicitOperator() + { + var source = """ + using System; + Span s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static explicit operator Span(T[] array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Explicit_ExplicitOperator() + { + var source = """ + using System; + Span s = (Span)arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static explicit operator Span(T[] array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'Span.op_Implicit' + // Span s = (Span)arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "(Span)arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_UnrecognizedModreq() + { + /* + public struct Span + { + public static implicit operator modreq(A) Span(T[] array) => throw null; + } + public class A { } + public static class C + { + public static void M(Span s) { } + } + */ + var ilSource = """ + .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType + { + .pack 0 + .size 1 + .method public hidebysig specialname static valuetype System.Span`1 modreq(A) op_Implicit(!T[] 'array') cil managed + { + .maxstack 1 + ret + } + } + .class public auto ansi sealed beforefieldinit A extends System.Object + { + } + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + { + .maxstack 1 + ret + } + } + """; + var source = """ + C.M(new int[] { 1, 2, 3 }); + """; + CreateCompilationWithIL(source, ilSource).VerifyDiagnostics( + // (1,5): error CS0570: 'Span.implicit operator Span(T[])' is not supported by the language + // C.M(new int[] { 1, 2, 3 }); + Diagnostic(ErrorCode.ERR_BindToBogus, "new int[] { 1, 2, 3 }").WithArguments("System.Span.implicit operator System.Span(T[])").WithLocation(1, 5)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_UnrecognizedModopt() + { + /* + public struct Span { public static implicit operator modopt(A) Span(T[] array) => throw null; } @@ -512,20 +1089,133 @@ .maxstack 1 """); } - [Theory, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Implicit_ConstantData(LanguageVersion langVersion) + [Theory] + [InlineData("where TDerived : T")] + [InlineData("")] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MissingClassConstraint(string constraints) { - var source = """ + var source = $$""" using System; - - C.M1(new[] { 1 }); - C.M2(new[] { 2 }); - - static class C + ReadOnlySpan s = source(); + static ReadOnlySpan source() => throw null; + + namespace System { - public static void M1(Span s) => Console.Write(s[0]); - public static void M2(ReadOnlySpan s) => Console.Write(s[0]); - } + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan items) {{constraints}} => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,26): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 26)); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MissingInheritsConstraint() + { + var source = """ + using System; + ReadOnlySpan s = source(); + static ReadOnlySpan source() => throw null; + + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan items) where TDerived : class => throw null; + } + } + """; + var verifier = CompileAndVerify(source); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "System.ReadOnlySpan Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000a: pop + IL_000b: ret + } + """); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_AdditionalConstraint() + { + var source = """ + using System; + ReadOnlySpan s = source(); + static ReadOnlySpan source() => throw null; + + public class C { } + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan items) where TDerived : C => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,26): error CS0311: The type 'string' cannot be used as type parameter 'TDerived' in the generic type or method 'ReadOnlySpan.CastUp(ReadOnlySpan)'. There is no implicit reference conversion from 'string' to 'C'. + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "source()").WithArguments("System.ReadOnlySpan.CastUp(System.ReadOnlySpan)", "C", "TDerived", "string").WithLocation(2, 26)); + } + + [Theory] + [InlineData("T", "T", false)] + [InlineData("T", "TDerived", false)] + [InlineData("TDerived", "TDerived", false)] + [InlineData("TDerived", "T", true)] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_WrongTypeArgument(string arg, string result, bool works) + { + var source = $$""" + using System; + ReadOnlySpan s = source(); + static ReadOnlySpan source() => throw null; + + public class C { } + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan<{{result}}> CastUp(ReadOnlySpan<{{arg}}> items) where TDerived : class, T => throw null; + } + } + """; + var comp = CreateCompilation(source); + if (works) + { + // Make sure the code above can work so we are testing something useful. + comp.VerifyDiagnostics(); + } + else + { + comp.VerifyDiagnostics( + // (2,26): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 26)); + } + } + + [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 = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); @@ -729,106 +1419,1303 @@ .maxstack 1 IL_000a: pop IL_000b: ret } - """); + """); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_MultipleSpans_01() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct ReadOnlySpan; + public readonly ref struct Span + { + public static implicit operator ReadOnlySpan(Span span) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(); + + var source = """ + using System; + ReadOnlySpan s = source(); + use(s); + static Span source() => default; + static void use(ReadOnlySpan s) { } + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer", + // warning CS0436: Type conflicts with imported type + options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("CS0436", ReportDiagnostic.Suppress)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Internal"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: call "void Program.<
$>g__use|0_1(System.ReadOnlySpan)" + IL_000f: ret + } + """); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_MultipleSpans_02() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct ReadOnlySpan; + public readonly ref struct Span + { + public static implicit operator ReadOnlySpan(Span span) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["lib"]); + + var source = """ + extern alias lib; + lib::System.ReadOnlySpan s = source(); + static lib::System.Span source() => default; + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "External"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: pop + IL_000b: ret + } + """); + + source = """ + extern alias lib; + lib::System.ReadOnlySpan s = source(); + static System.Span source() => default; + """; + CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer").VerifyDiagnostics( + // (2,35): error CS0656: Missing compiler required member 'Span.op_Implicit' + // lib::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 35)); + + source = """ + extern alias lib; + System.ReadOnlySpan s = source(); + static lib::System.Span source() => default; + """; + CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer").VerifyDiagnostics( + // (2,30): error CS0656: Missing compiler required member 'Span.op_Implicit' + // System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 30)); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_MultipleSpans_03() + { + var span1 = CreateCompilation(""" + namespace System; + public readonly ref struct ReadOnlySpan; + public readonly ref struct Span; + """, assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span1"]); + var span2 = CreateCompilation(""" + extern alias span1; + namespace System; + public readonly ref struct ReadOnlySpan; + public readonly ref struct Span + { + public static implicit operator span1::System.ReadOnlySpan(Span span) + { + Console.Write("Span2"); + return default; + } + } + """, [span1], assemblyName: "Span2") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span2"]); + + var source = """ + extern alias span1; + extern alias span2; + span1::System.ReadOnlySpan s = source(); + static span2::System.Span source() => default; + """; + + var comp = CreateCompilation(source, [span1, span2], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Span2"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: pop + IL_000b: ret + } + """); + + source = """ + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static span2::System.Span source() => default; + """; + CreateCompilation(source, [span2], assemblyName: "Consumer").VerifyDiagnostics( + // (2,37): error CS0656: Missing compiler required member 'Span.op_Implicit' + // span2::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 37)); + + source = """ + extern alias span1; + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static span1::System.Span source() => default; + """; + CreateCompilation(source, [span1, span2], assemblyName: "Consumer").VerifyDiagnostics( + // (3,37): error CS0656: Missing compiler required member 'Span.op_Implicit' + // span2::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.Span", "op_Implicit").WithLocation(3, 37)); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MultipleSpans() + { + var span1 = CreateCompilation(""" + namespace System; + public readonly ref struct Span; + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan span) where TDerived : class, T + { + Console.WriteLine("CastUp"); + return default; + } + } + """, assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span1"]); + var span2 = CreateCompilation(""" + extern alias span1; + namespace System; + public readonly ref struct Span + { + public static implicit operator span1::System.ReadOnlySpan(Span span) + { + Console.WriteLine("Span2 to ReadOnlySpan1"); + return default; + } + } + """, [span1], assemblyName: "Span2") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span2"]); + + var source = """ + extern alias span1; + extern alias span2; + span1::System.ReadOnlySpan s = source(); + static span2::System.Span source() => default; + """; + + var comp = CreateCompilation(source, [span1, span2], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: """ + Span2 to ReadOnlySpan1 + CastUp + """); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 17 (0x11) + .maxstack 1 + IL_0000: call "System.Span Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_000a: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000f: pop + IL_0010: ret + } + """); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_01() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan other) where TDerived : class, T + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(); + + var source = """ + using System; + ReadOnlySpan s = source(); + use(s); + static ReadOnlySpan source() => default; + static void use(ReadOnlySpan s) { } + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer", + // warning CS0436: Type conflicts with imported type + options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("CS0436", ReportDiagnostic.Suppress)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Internal"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "System.ReadOnlySpan Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000a: call "void Program.<
$>g__use|0_1(System.ReadOnlySpan)" + IL_000f: ret + } + """); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_02() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(ReadOnlySpan other) where TDerived : class, T + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["lib"]); + + var source = """ + extern alias lib; + lib::System.ReadOnlySpan s = source(); + static lib::System.ReadOnlySpan source() => default; + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "External"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "System.ReadOnlySpan Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000a: pop + IL_000b: ret + } + """); + + source = """ + extern alias lib; + lib::System.ReadOnlySpan s = source(); + static System.ReadOnlySpan source() => default; + """; + CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer").VerifyDiagnostics( + // (2,38): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // lib::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 38)); + + source = """ + extern alias lib; + System.ReadOnlySpan s = source(); + static lib::System.ReadOnlySpan source() => default; + """; + CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer").VerifyDiagnostics( + // (2,33): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 33)); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_03() + { + var span1 = CreateCompilation(""" + namespace System; + public readonly ref struct ReadOnlySpan; + """, assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span1"]); + var span2 = CreateCompilation(""" + extern alias span1; + namespace System; + public readonly ref struct ReadOnlySpan + { + public static span1::System.ReadOnlySpan CastUp(ReadOnlySpan other) where TDerived : class, T + { + Console.Write("Span2"); + return default; + } + } + """, [span1], assemblyName: "Span2") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span2"]); + + var source = """ + extern alias span1; + extern alias span2; + span1::System.ReadOnlySpan s = source(); + static span2::System.ReadOnlySpan source() => default; + """; + CreateCompilation(source, [span1, span2], assemblyName: "Consumer").VerifyDiagnostics( + // (3,40): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // span1::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(3, 40)); + + source = """ + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static span2::System.ReadOnlySpan source() => default; + """; + CreateCompilation(source, [span2], assemblyName: "Consumer").VerifyDiagnostics( + // (2,40): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // span2::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 40)); + + source = """ + extern alias span1; + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static span1::System.ReadOnlySpan source() => default; + """; + CreateCompilation(source, [span1, span2], assemblyName: "Consumer").VerifyDiagnostics( + // (3,40): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // span2::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(3, 40)); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_04() + { + var span1 = CreateCompilation(""" + namespace System; + public readonly ref struct ReadOnlySpan; + """, assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span1"]); + var span2 = CreateCompilation(""" + extern alias span1; + namespace System; + public readonly ref struct ReadOnlySpan + { + public static ReadOnlySpan CastUp(span1::System.ReadOnlySpan other) where TDerived : class, T + { + Console.Write("Span2"); + return default; + } + } + """, [span1], assemblyName: "Span2") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span2"]); + + var source = """ + extern alias span1; + extern alias span2; + span1::System.ReadOnlySpan s = source(); + static span2::System.ReadOnlySpan source() => default; + """; + CreateCompilation(source, [span1, span2], assemblyName: "Consumer").VerifyDiagnostics( + // (3,40): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // span1::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(3, 40)); + + source = """ + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static span2::System.ReadOnlySpan source() => default; + """; + CreateCompilation(source, [span2], assemblyName: "Consumer").VerifyDiagnostics( + // (2,40): error CS0656: Missing compiler required member 'ReadOnlySpan.CastUp' + // span2::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.ReadOnlySpan", "CastUp").WithLocation(2, 40)); + + source = """ + extern alias span1; + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static span1::System.ReadOnlySpan source() => default; + """; + var comp = CreateCompilation(source, [span1, span2], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Span2"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "System.ReadOnlySpan Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000a: pop + IL_000b: ret + } + """); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_MultipleSpans_01() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct ReadOnlySpan; + public static class MemoryExtensions + { + public static ReadOnlySpan AsSpan(string s) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(); + + var source = """ + using System; + ReadOnlySpan s = source(); + use(s); + static string source() => ""; + static void use(ReadOnlySpan s) { } + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer", + // warning CS0436: Type conflicts with imported type + options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("CS0436", ReportDiagnostic.Suppress)); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Internal"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" + IL_000a: call "void Program.<
$>g__use|0_1(System.ReadOnlySpan)" + IL_000f: ret + } + """); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_MultipleSpans_02() + { + static string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct ReadOnlySpan; + public static class MemoryExtensions + { + public static ReadOnlySpan AsSpan(string s) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["lib"]); + + var source = """ + extern alias lib; + lib::System.ReadOnlySpan s = source(); + static string source() => ""; + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "External"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" + IL_000a: pop + IL_000b: ret + } + """); + + source = """ + System.ReadOnlySpan s = source(); + static string source() => ""; + """; + + comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Internal"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" + IL_000a: pop + IL_000b: ret + } + """); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_MultipleSpans_03() + { + var span1 = CreateCompilation(""" + namespace System; + public readonly ref struct ReadOnlySpan; + """, assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span1"]); + var span2 = CreateCompilation($$""" + extern alias span1; + namespace System; + public readonly ref struct ReadOnlySpan; + public static class MemoryExtensions + { + public static span1::System.ReadOnlySpan AsSpan(string s) + { + Console.Write("Span2"); + return default; + } + } + """, [span1], assemblyName: "Span2") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span2"]); + + var source = """ + extern alias span1; + span1::System.ReadOnlySpan s = source(); + static string source() => ""; + """; + var comp = CreateCompilation(source, [span1, span2], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, expectedOutput: "Span2"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" + IL_000a: pop + IL_000b: ret + } + """); + + source = """ + extern alias span2; + span2::System.ReadOnlySpan s = source(); + static string source() => ""; + """; + CreateCompilation(source, [span2], assemblyName: "Consumer").VerifyDiagnostics( + // (2,38): error CS0656: Missing compiler required member 'System.MemoryExtensions.AsSpan' + // span2::System.ReadOnlySpan s = source(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "source()").WithArguments("System.MemoryExtensions", "AsSpan").WithLocation(2, 38)); + } + + [Theory] + [InlineData("System.Int32[]", "System.Span")] + [InlineData("System.Int32[]", "System.ReadOnlySpan")] + [InlineData("System.Span", "System.ReadOnlySpan")] + [InlineData("System.ReadOnlySpan", "System.ReadOnlySpan")] + [InlineData("System.String", "System.ReadOnlySpan")] + public void Conversion_Span_Implicit_SemanticModel(string sourceType, string targetType) + { + var source = $$""" + class C + { + {{targetType}} M({{sourceType}} arg) { return arg; } + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(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(sourceType, argType.Type.ToTestDisplayString()); + Assert.Equal(targetType, 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_Explicit_SemanticModel() + { + 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 = CreateCompilationWithSpanAndMemoryExtensions(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.ExplicitSpan, 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.ExplicitSpan, 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') + """); + } + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + class C + { + System.Span M(int[] arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(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)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Explicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M(int[] arg) => (Span)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,34): error CS0030: Cannot convert type 'int[]' to 'System.Span' + // Span M(int[] arg) => (Span)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)arg").WithArguments("int[]", "System.Span").WithLocation(4, 34)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + class C + { + System.ReadOnlySpan M(int[] arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,49): error CS0029: Cannot implicitly convert type 'int[]' to 'System.ReadOnlySpan' + // System.ReadOnlySpan M(int[] arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("int[]", "System.ReadOnlySpan").WithLocation(3, 49)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_Array_Implicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + string[] M(Span arg) => arg; + } + namespace System + { + public readonly ref struct Span + { + public static implicit operator T[](Span span) => default; + } + } + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "string[] System.Span.op_Implicit(System.Span)" + IL_0006: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_ReadOnlySpan_Explicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(int[] arg) => (ReadOnlySpan)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,42): error CS0030: Cannot convert type 'int[]' to 'System.ReadOnlySpan' + // ReadOnlySpan M(int[] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("int[]", "System.ReadOnlySpan").WithLocation(4, 42)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(Span arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,46): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan M(Span arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(4, 46)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Explicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(Span arg) => (ReadOnlySpan)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,46): error CS0030: Cannot convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan M(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(4, 46)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Implicit_Struct(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(Span arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Implicit_Struct_Generic(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan> M(Span> arg) => arg; + } + struct S; + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,55): error CS0029: Cannot implicitly convert type 'System.Span>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M(Span> arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(4, 55)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Implicit_Struct_Generic_NullabilityAnnotation(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M(Span> arg) => arg; + } + struct S; + """; + var targetType = langVersion > LanguageVersion.CSharp13 ? "System.Span>" : "System.ReadOnlySpan>"; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,56): warning CS8619: Nullability of reference types in value of type 'Span>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M(Span> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments(targetType, "System.ReadOnlySpan>").WithLocation(5, 56)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(4, 54)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Explicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(ReadOnlySpan arg) => (ReadOnlySpan)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,54): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(4, 54)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_Struct(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics(); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_Struct_Generic(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + } + struct S; + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,63): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(4, 63)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_Struct_Generic_NullabilityAnnotation(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + } + struct S; + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (5,64): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(5, 64)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_UserDefined_01(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(ReadOnlySpan arg) => arg; + } + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static implicit operator ReadOnlySpan(ReadOnlySpan span) => default; + } + } + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), verify: Verification.FailsILVerify) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_UserDefined_02() + { + var source = """ + using System; + class C + { + ReadOnlySpan M(ReadOnlySpan arg) => arg; + } + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static implicit operator ReadOnlySpan(ReadOnlySpan span) => default; + public static ReadOnlySpan CastUp(ReadOnlySpan s) where TDerived : class, T => throw null; + } + } + """; + + CompileAndVerify(source, parseOptions: TestOptions.Regular13, verify: Verification.FailsILVerify) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); + + var expectedIl = """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_0006: ret + } + """; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, verify: Verification.FailsILVerify) + .VerifyDiagnostics().VerifyIL("C.M", expectedIl); + CompileAndVerify(source, verify: Verification.FailsILVerify) + .VerifyDiagnostics().VerifyIL("C.M", expectedIl); + } + + [Theory, CombinatorialData] + public void Conversion_ReadOnlySpan_Span_Implicit( + [CombinatorialLangVersions] LanguageVersion langVersion, + [CombinatorialValues("string", "object")] string type) + { + var source = $$""" + using System; + class C + { + Span<{{type}}> M(ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,49): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.Span' + // Span M(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", $"System.Span<{type}>").WithLocation(4, 49)); + } + + [Theory, CombinatorialData] + public void Conversion_ReadOnlySpan_Span_Explicit( + [CombinatorialLangVersions] LanguageVersion langVersion, + [CombinatorialValues("string", "object")] string type) + { + var source = $$""" + using System; + class C + { + Span<{{type}}> M(ReadOnlySpan arg) => (Span<{{type}}>)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,49): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.Span' + // Span M(ReadOnlySpan arg) => (Span)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, $"(Span<{type}>)arg").WithArguments("System.ReadOnlySpan", $"System.Span<{type}>").WithLocation(4, 49)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_Span_Implicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M(ReadOnlySpan arg) => arg; + } + namespace System + { + public readonly ref struct Span; + public readonly ref struct ReadOnlySpan + { + public static implicit operator Span(ReadOnlySpan span) => default; + } + } + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), verify: Verification.FailsILVerify) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.Span System.ReadOnlySpan.op_Implicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_Span_Explicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M(ReadOnlySpan arg) => (Span)arg; + } + namespace System + { + public readonly ref struct Span; + public readonly ref struct ReadOnlySpan + { + public static explicit operator Span(ReadOnlySpan span) => default; + } + } + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), verify: Verification.FailsILVerify) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.Span System.ReadOnlySpan.op_Explicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_Span_Explicit_UserDefined_Covariant(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M(ReadOnlySpan arg) => (Span)arg; + } + namespace System + { + public readonly ref struct Span; + public readonly ref struct ReadOnlySpan + { + public static explicit operator Span(ReadOnlySpan span) => default; + } + } + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), verify: Verification.FailsILVerify) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.Span System.ReadOnlySpan.op_Explicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_ReadOnlySpan_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(string arg) => arg; + } + + namespace System + { + public readonly ref struct ReadOnlySpan; + } + """; + CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,42): error CS0029: Cannot implicitly convert type 'string' to 'System.ReadOnlySpan' + // ReadOnlySpan M(string arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("string", "System.ReadOnlySpan").WithLocation(4, 42)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_Span_Implicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M(string arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,33): error CS0029: Cannot implicitly convert type 'string' to 'System.Span' + // Span M(string arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("string", "System.Span").WithLocation(4, 33)); } - [Theory, CombinatorialData] - public void Conversion_Array_Span_Implicit_SemanticModel( - [CombinatorialValues("Span", "ReadOnlySpan")] string destination) + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_Span_Explicit(LanguageVersion langVersion) { - var source = $$""" + var source = """ + using System; class C { - System.{{destination}} M(int[] arg) { return arg; } + Span M(string arg) => (Span)arg; } """; - - var comp = CreateCompilationWithSpanAndMemoryExtensions(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); + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,33): error CS0030: Cannot convert type 'string' to 'System.Span' + // Span M(string arg) => (Span)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)arg").WithArguments("string", "System.Span").WithLocation(4, 33)); } - [Fact] - public void Conversion_Array_Span_Explicit_SemanticModel() + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_Span_Implicit_UserDefined(LanguageVersion langVersion) { var source = """ using System; - class C - where T : class - where U : class, T + class C { - ReadOnlySpan F1(T[] x) => (ReadOnlySpan)x; - ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + Span M(string arg) => arg; + } + namespace System + { + public readonly ref struct Span + { + public static implicit operator Span(string s) => default; + } } """; - - var comp = CreateCompilationWithSpanAndMemoryExtensions(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.ExplicitSpan, 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.ExplicitSpan, 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') - """); - } + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), verify: Verification.FailsILVerify) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.Span System.Span.op_Implicit(string)" + IL_0006: ret + } + """); } [Theory, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Implicit_UnrelatedElementType(LanguageVersion langVersion) + public void Conversion_ReadOnlySpan_string_Implicit_UserDefined(LanguageVersion langVersion) { var source = """ + using System; class C { - System.Span M(int[] arg) => arg; + string M(ReadOnlySpan arg) => arg; + } + namespace System + { + public readonly ref struct ReadOnlySpan + { + public static implicit operator string(ReadOnlySpan span) => default; + } } """; - CreateCompilationWithSpanAndMemoryExtensions(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)); + CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)) + .VerifyDiagnostics() + .VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "string System.ReadOnlySpan.op_Implicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); } [Fact] @@ -907,6 +2794,7 @@ class C ReadOnlySpan M6(string?[] arg) => (ReadOnlySpan)arg; ReadOnlySpan M7(object?[] arg) => (ReadOnlySpan)arg; + ReadOnlySpan M8(string?[] arg) => (ReadOnlySpan)arg; } """; @@ -922,7 +2810,10 @@ class C Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("string?[]", "string[]").WithLocation(11, 47), // (12,47): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'string[]'. // ReadOnlySpan M7(object?[] arg) => (ReadOnlySpan)arg; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[]", "string[]").WithLocation(12, 47)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[]", "string[]").WithLocation(12, 47), + // (13,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'object[]'. + // ReadOnlySpan M8(string?[] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("string?[]", "object[]").WithLocation(13, 47)); var expectedDiagnostics = new[] { @@ -937,13 +2828,157 @@ class C Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("string?[]", "System.ReadOnlySpan").WithLocation(11, 47), // (12,47): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'ReadOnlySpan'. // ReadOnlySpan M7(object?[] arg) => (ReadOnlySpan)arg; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[]", "System.ReadOnlySpan").WithLocation(12, 47) + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("object?[]", "System.ReadOnlySpan").WithLocation(12, 47), + // (13,47): warning CS8619: Nullability of reference types in value of type 'string?[]' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M8(string?[] arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("string?[]", "System.ReadOnlySpan").WithLocation(13, 47) }; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); } + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_NullableAnalysis() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan M1(Span arg) => arg; + ReadOnlySpan M2(Span arg) => arg; + ReadOnlySpan M3(Span arg) => arg; + ReadOnlySpan M4(Span arg) => arg; + ReadOnlySpan M5(Span arg) => arg; + + ReadOnlySpan M6(Span arg) => (ReadOnlySpan)arg; + ReadOnlySpan M7(Span arg) => (ReadOnlySpan)arg; + ReadOnlySpan M8(Span arg) => (ReadOnlySpan)arg; + } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (6,51): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M2(Span arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(6, 51), + // (7,51): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M3(Span arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(7, 51), + // (9,51): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan M5(Span arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(9, 51), + // (11,51): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M6(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(11, 51), + // (12,51): error CS0030: Cannot convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan M7(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(12, 51), + // (13,51): error CS0030: Cannot convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan M8(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(13, 51)); + + var expectedDiagnostics = new[] + { + // (6,51): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M2(Span arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(6, 51), + // (9,51): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M5(Span arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(9, 51), + // (11,51): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M6(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(11, 51), + // (12,51): error CS0030: Cannot convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan M7(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(12, 51), + // (13,51): warning CS8619: Nullability of reference types in value of type 'Span' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M8(Span arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(13, 51) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_NullableAnalysis() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan M1(ReadOnlySpan arg) => arg; + ReadOnlySpan M2(ReadOnlySpan arg) => arg; + ReadOnlySpan M3(ReadOnlySpan arg) => arg; + ReadOnlySpan M4(ReadOnlySpan arg) => arg; + ReadOnlySpan M5(ReadOnlySpan arg) => arg; + + ReadOnlySpan M6(ReadOnlySpan arg) => (ReadOnlySpan)arg; + ReadOnlySpan M7(ReadOnlySpan arg) => (ReadOnlySpan)arg; + ReadOnlySpan M8(ReadOnlySpan arg) => (ReadOnlySpan)arg; + } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (6,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M2(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(6, 59), + // (7,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M3(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(7, 59), + // (9,59): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M5(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(9, 59), + // (11,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M6(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(11, 59), + // (12,59): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M7(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(12, 59), + // (13,59): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M8(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(13, 59)); + + var expectedDiagnostics = new[] + { + // (6,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M2(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(6, 59), + // (9,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M5(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(9, 59), + // (11,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M6(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(11, 59), + // (12,59): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M7(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(12, 59), + // (13,59): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan' doesn't match target type 'ReadOnlySpan'. + // ReadOnlySpan M8(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(13, 59) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_ReadOnlySpan_Implicit_NullableAnalysis(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan M1(string arg) => arg; + ReadOnlySpan M2(string? arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics(); + } + [Fact] public void Conversion_Array_Span_Implicit_NullableAnalysis_NestedArrays() { @@ -1108,6 +3143,99 @@ string targetType(string inner) => langVersion > LanguageVersion.CSharp13 ? $"System.ReadOnlySpan>" : $"S<{inner}>[]"; } + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M1(Span> arg) => arg; + ReadOnlySpan> M2(Span> arg) => arg; + ReadOnlySpan> M3(Span> arg) => arg; + ReadOnlySpan> M4(Span> arg) => arg; + ReadOnlySpan> M5(Span> arg) => arg; + + ReadOnlySpan> M6(Span> arg) => (ReadOnlySpan>)arg; + ReadOnlySpan> M7(Span> arg) => (ReadOnlySpan>)arg; + } + interface I { } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (6,57): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M2(Span> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(6, 57), + // (7,57): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M3(Span> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(7, 57), + // (9,57): error CS0029: Cannot implicitly convert type 'System.Span>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M5(Span> arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(9, 57), + // (11,57): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M6(Span> arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan>)arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(11, 57), + // (12,57): error CS0030: Cannot convert type 'System.Span>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M7(Span> arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan>)arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(12, 57)); + + var expectedDiagnostics = new[] + { + // (6,57): warning CS8619: Nullability of reference types in value of type 'Span>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M2(Span> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(6, 57), + // (9,57): warning CS8619: Nullability of reference types in value of type 'Span>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M5(Span> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(9, 57), + // (11,57): warning CS8619: Nullability of reference types in value of type 'Span>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M6(Span> arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan>)arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(11, 57), + // (12,57): error CS0030: Cannot convert type 'System.Span>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M7(Span> arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan>)arg").WithArguments("System.Span>", "System.ReadOnlySpan>").WithLocation(12, 57) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability(LanguageVersion langVersion) + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M1(ReadOnlySpan> arg) => arg; + ReadOnlySpan> M2(ReadOnlySpan> arg) => arg; + ReadOnlySpan> M3(ReadOnlySpan> arg) => arg; + ReadOnlySpan> M4(ReadOnlySpan> arg) => arg; + ReadOnlySpan> M5(ReadOnlySpan> arg) => arg; + + ReadOnlySpan> M6(ReadOnlySpan> arg) => (ReadOnlySpan>)arg; + ReadOnlySpan> M7(ReadOnlySpan> arg) => (ReadOnlySpan>)arg; + } + struct S { } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,65): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M2(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(6, 65), + // (7,65): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M3(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(7, 65), + // (9,65): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M5(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(9, 65), + // (11,65): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M6(ReadOnlySpan> arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(ReadOnlySpan>)arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(11, 65), + // (12,65): error CS0030: Cannot convert type 'System.ReadOnlySpan>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M7(ReadOnlySpan> arg) => (ReadOnlySpan>)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan>)arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(12, 65)); + } + [Theory, MemberData(nameof(LangVersions))] public void Conversion_Array_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability_Covariant(LanguageVersion langVersion) { @@ -1127,6 +3255,35 @@ interface I { } Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("I[]", targetType).WithLocation(5, 52)); } + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability_Covariant() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + } + interface I { } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (5,64): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan>' to 'System.ReadOnlySpan>' + // ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(5, 64)); + + var expectedDiagnostics = new[] + { + // (5,64): warning CS8619: Nullability of reference types in value of type 'ReadOnlySpan>' doesn't match target type 'ReadOnlySpan>'. + // ReadOnlySpan> M(ReadOnlySpan> arg) => arg; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("System.ReadOnlySpan>", "System.ReadOnlySpan>").WithLocation(5, 64) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + } + [Theory, MemberData(nameof(LangVersions))] public void Conversion_Array_Span_Implicit_NullableAnalysis_Outer(LanguageVersion langVersion) { @@ -1149,36 +3306,101 @@ class C } [Fact] - public void Conversion_Array_Span_Implicit_NullableAnalysis_ExtensionMethodReceiver() + 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) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(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)); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_NullableAnalysis_ExtensionMethodReceiver() + { + var source = """ + #nullable enable + using System; + static class C + { + static void MS(Span a, Span b) + { + a.M1(); b.M1(); + a.M2(); b.M2(); + } + static void M1(this ReadOnlySpan arg) { } + static void M2(this ReadOnlySpan arg) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics( + // (7,17): warning CS8620: Argument of type 'Span' cannot be used for parameter 'arg' of type 'ReadOnlySpan' in 'void C.M1(ReadOnlySpan arg)' due to differences in the nullability of reference types. + // a.M1(); b.M1(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "b").WithArguments("System.Span", "System.ReadOnlySpan", "arg", "void C.M1(ReadOnlySpan arg)").WithLocation(7, 17)); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_NullableAnalysis_ExtensionMethodReceiver() { var source = """ #nullable enable using System; static class C { - static void MS(string[] a, string?[] b) + static void MS(ReadOnlySpan a, ReadOnlySpan 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) { } + static void M1(this ReadOnlySpan arg) { } + static void M2(this ReadOnlySpan arg) { } } """; - CreateCompilationWithSpanAndMemoryExtensions(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. + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (7,17): warning CS8620: Argument of type 'ReadOnlySpan' cannot be used for parameter 'arg' of type 'ReadOnlySpan' in 'void C.M1(ReadOnlySpan 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)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "b").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan", "arg", "void C.M1(ReadOnlySpan arg)").WithLocation(7, 17)); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_NullableAnalysis_ExtensionMethodReceiver() + { + var source = """ + #nullable enable + using System; + static class C + { + static void MS(string a, string? b) + { + a.M(); + b.M(); + } + static void M(this ReadOnlySpan arg) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); } [Theory, CombinatorialData] @@ -1378,6 +3600,54 @@ class C Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); } + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Opposite_Implicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M1(ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,45): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.Span' + // Span M1(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "System.Span").WithLocation(4, 45)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Opposite_Implicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M1(ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,59): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M1(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(4, 59)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_ReadOnlySpan_Opposite_Implicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + string M1(ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,43): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'string' + // string M1(ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "string").WithLocation(4, 43)); + } + [Theory, MemberData(nameof(LangVersions))] public void Conversion_Array_Span_Opposite_Explicit(LanguageVersion langVersion) { @@ -1405,8 +3675,56 @@ class C Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string[])arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); } - [Fact] - public void Conversion_Array_Span_Opposite_Explicit_UserDefined() + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Opposite_Explicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M1(ReadOnlySpan arg) => (Span)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,45): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.Span' + // Span M1(ReadOnlySpan arg) => (Span)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)arg").WithArguments("System.ReadOnlySpan", "System.Span").WithLocation(4, 45)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Opposite_Explicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M1(ReadOnlySpan arg) => (ReadOnlySpan)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,59): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan M1(ReadOnlySpan arg) => (ReadOnlySpan)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)arg").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(4, 59)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_ReadOnlySpan_Opposite_Explicit(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + string M1(ReadOnlySpan arg) => (string)arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,43): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'string' + // string M1(ReadOnlySpan arg) => (string)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string)arg").WithArguments("System.ReadOnlySpan", "string").WithLocation(4, 43)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Opposite_Explicit_UserDefined(LanguageVersion langVersion) { var source = """ class C @@ -1422,7 +3740,7 @@ readonly ref struct Span } } """; - var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular13); + var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { @@ -1433,20 +3751,73 @@ .maxstack 1 IL_0006: ret } """); + } - var expectedDiagnostics = new[] - { - // (3,39): error CS0030: Cannot convert type 'System.Span' to 'int[]' - // int[] M(System.Span arg) => (int[])arg; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int[])arg").WithArguments("System.Span", "int[]").WithLocation(3, 39) - }; + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Opposite_Explicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + Span M(ReadOnlySpan arg) => (Span)arg; + } - CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); - CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + namespace System + { + readonly ref struct Span; + readonly ref struct ReadOnlySpan + { + public static explicit operator Span(ReadOnlySpan span) => throw null; + } + } + """; + var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.1 + IL_0001: call "System.Span System.ReadOnlySpan.op_Explicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); } - [Fact] - public void Conversion_Array_Span_Implicit_Params() + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_ReadOnlySpan_Opposite_Explicit_UserDefined(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + string M(ReadOnlySpan arg) => (string)arg; + } + + namespace System + { + readonly ref struct ReadOnlySpan + { + public static explicit operator string(ReadOnlySpan 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 "string System.ReadOnlySpan.op_Explicit(System.ReadOnlySpan)" + IL_0006: ret + } + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_Params(LanguageVersion langVersion) { var source = """ using System; @@ -1464,7 +3835,7 @@ void M2(params ReadOnlySpan s) { } void M3(params string[] s) { } } """; - var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); var verifier = CompileAndVerify(comp).VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { @@ -1505,9 +3876,35 @@ void M2(params ReadOnlySpan s) { } void M3(params object[] p) { } } """; - var comp = CreateCompilationWithSpanAndMemoryExtensions(source); + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13); 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 + } + """); + + var expectedIl = """ { // Code size 45 (0x2d) .maxstack 5 @@ -1534,7 +3931,165 @@ .locals init (object[] V_0) IL_0027: call "void C.M3(params object[])" IL_002c: ret } - """); + """; + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Implicit_Params(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + void M(Span a) + { + M1(a); + } + void M1(params ReadOnlySpan s) { } + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0007: call "void C.M1(params System.ReadOnlySpan)" + IL_000c: ret + } + """); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_Params() + { + var source = """ + using System; + class C + { + void M(Span a) + { + M1(a); + } + void M1(params ReadOnlySpan s) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (6,12): error CS1503: Argument 1: cannot convert from 'System.Span' to 'object' + // M1(a); + Diagnostic(ErrorCode.ERR_BadArgType, "a").WithArguments("1", "System.Span", "object").WithLocation(6, 12)); + + var expectedIl = """ + { + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0007: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000c: call "void C.M1(params System.ReadOnlySpan)" + IL_0011: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_CastUp_Implicit_Params() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a) + { + M1(a); + } + void M1(params ReadOnlySpan s) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (6,12): error CS1503: Argument 1: cannot convert from 'System.ReadOnlySpan' to 'object' + // M1(a); + Diagnostic(ErrorCode.ERR_BadArgType, "a").WithArguments("1", "System.ReadOnlySpan", "object").WithLocation(6, 12)); + + var expectedIl = """ + { + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_0007: call "void C.M1(params System.ReadOnlySpan)" + IL_000c: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_CastUp_Implicit_Params() + { + var source = """ + using System; + class C + { + void M(ReadOnlySpan a) + { + M1(a); + } + void M1(params ReadOnlySpan s) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (6,12): error CS1503: Argument 1: cannot convert from 'System.ReadOnlySpan' to 'object' + // M1(a); + Diagnostic(ErrorCode.ERR_BadArgType, "a").WithArguments("1", "System.ReadOnlySpan", "object").WithLocation(6, 12)); + + var expectedIl = """ + { + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_0007: call "void C.M1(params System.ReadOnlySpan)" + IL_000c: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); } [Theory, MemberData(nameof(LangVersions))] @@ -2113,6 +4668,123 @@ static class C Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "default").WithArguments(type).WithLocation(4, 15)); } + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Span_ReadOnlySpan_Implicit_ExpressionTree(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Linq.Expressions; + + C.R(x => C.M(x)); + + delegate void D(Span x); + + static class C + { + public static void R(Expression e) { } + public static void M(ReadOnlySpan x) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (4,5): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'Span'. + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("Span").WithLocation(4, 5), + // (4,14): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'Span'. + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("Span").WithLocation(4, 14)); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_ExpressionTree() + { + var source = """ + using System; + using System.Linq.Expressions; + + C.R(x => C.M(x)); + + delegate void D(Span x); + + static class C + { + public static void R(Expression e) { } + public static void M(ReadOnlySpan x) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,14): error CS1503: Argument 1: cannot convert from 'System.Span' to 'System.ReadOnlySpan' + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "System.Span", "System.ReadOnlySpan").WithLocation(4, 14)); + + var expectedDiagnostics = new[] + { + // (4,5): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'Span'. + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("Span").WithLocation(4, 5), + // (4,14): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'Span'. + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("Span").WithLocation(4, 14) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_ExpressionTree() + { + var source = """ + using System; + using System.Linq.Expressions; + + C.R(x => C.M(x)); + + delegate void D(ReadOnlySpan x); + + static class C + { + public static void R(Expression e) { } + public static void M(ReadOnlySpan x) { } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,14): error CS1503: Argument 1: cannot convert from 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(4, 14)); + + var expectedDiagnostics = new[] + { + // (4,5): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'ReadOnlySpan'. + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("ReadOnlySpan").WithLocation(4, 5), + // (4,14): error CS8640: Expression tree cannot contain value of ref struct or restricted type 'ReadOnlySpan'. + // C.R(x => C.M(x)); + Diagnostic(ErrorCode.ERR_ExpressionTreeCantContainRefStruct, "x").WithArguments("ReadOnlySpan").WithLocation(4, 14) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_string_ReadOnlySpan_Implicit_ExpressionTree(LanguageVersion langVersion) + { + var source = """ + using System; + using System.Linq.Expressions; + + C.R(x => C.M(x)); + + static class C + { + public static void R(Expression> e) => e.Compile()("s"); + public static void M(ReadOnlySpan x) => Console.Write(x.Length + " " + x[0]); + } + """; + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + CompileAndVerify(comp, expectedOutput: "1 s").VerifyDiagnostics(); + } + [Theory, CombinatorialData] public void Conversion_Array_ReadOnlySpan_Covariant( [CombinatorialLangVersions] LanguageVersion langVersion, @@ -2451,93 +5123,273 @@ class C {{constraints}} Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)x").WithArguments("T[]", "System.Span").WithLocation(7, 26)); } - [Theory, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Variance_02(LanguageVersion langVersion) - { - var source = """ - using System; - class C - where T : class - where U : class, T + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Variance_02(LanguageVersion langVersion) + { + 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; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).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; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); + + // Note: although a breaking change, the previous would fail with a runtime exception + // (Span's constructor checks that the element types are identical). + + var expectedDiagnostics = new[] + { + // (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) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_Variance() + { + var source = """ + using System; + class C + where T : class + where U : class, T + { + ReadOnlySpan F1(Span x) => x; + ReadOnlySpan F2(Span x) => (ReadOnlySpan)x; + ReadOnlySpan F3(Span x) => x; + ReadOnlySpan F4(Span x) => (ReadOnlySpan)x; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,38): error CS0029: Cannot implicitly convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan F1(Span x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(6, 38), + // (7,38): error CS0030: Cannot convert type 'System.Span' to 'System.ReadOnlySpan' + // ReadOnlySpan F2(Span x) => (ReadOnlySpan)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)x").WithArguments("System.Span", "System.ReadOnlySpan").WithLocation(7, 38)); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Variance() + { + var source = """ + using System; + class C + where T : class + where U : class, T + { + ReadOnlySpan F1(ReadOnlySpan x) => x; + ReadOnlySpan F2(ReadOnlySpan x) => (ReadOnlySpan)x; + ReadOnlySpan F3(ReadOnlySpan x) => x; + ReadOnlySpan F4(ReadOnlySpan x) => (ReadOnlySpan)x; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (6,46): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan F1(ReadOnlySpan x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(6, 46), + // (7,46): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.ReadOnlySpan' + // ReadOnlySpan F2(ReadOnlySpan x) => (ReadOnlySpan)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)x").WithArguments("System.ReadOnlySpan", "System.ReadOnlySpan").WithLocation(7, 46)); + } + + [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); + } + } + } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).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 = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_ThroughUserImplicit() + { + var source = """ + using System; + + D.M(new C()); + + class C + { + public static implicit operator Span(C c) => new Span(new int[] { 4, 5, 6 }); + } + + static class D + { + public static void M(ReadOnlySpan xs) => Console.Write(xs.Length + " " + xs[0]); + } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (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)); + + var expectedOutput = "3 4"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_ThroughUserImplicit() + { + var source = """ + using System; + + D.M(new C()); + + class C + { + public static implicit operator Span(C c) => new Span(new string[] { "x" }); + } + + static class D { - ReadOnlySpan F1(T[] x) => x; - ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; - Span F3(T[] x) => x; - Span F4(T[] x) => (Span)x; + public static void M(ReadOnlySpan xs) => Console.Write(xs.Length + " " + xs[0]); } """; - CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).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)); + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (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)); + + var expectedOutput = "1 x"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); } [Fact] - public void Conversion_Array_Span_Variance_03() + public void Conversion_ReadOnlySpan_ReadOnlySpan_ThroughUserImplicit() { var source = """ using System; - class C - where T : class, U - where U : class + + D.M(new C()); + + class C { - ReadOnlySpan F1(T[] x) => x; - ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; - Span F3(T[] x) => x; - Span F4(T[] x) => (Span)x; + public static implicit operator ReadOnlySpan(C c) => new ReadOnlySpan(new string[] { "x" }); + } + + static class D + { + public static void M(ReadOnlySpan xs) => Console.Write(xs.Length + " " + xs[0]); } """; - CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(); - // Note: although a breaking change, the previous would fail with a runtime exception - // (Span's constructor checks that the element types are identical). + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (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)); - var expectedDiagnostics = new[] - { - // (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) - }; + var expectedOutput = "1 x"; - CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); - CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); } - [Theory, CombinatorialData] - public void Conversion_Array_Span_ThroughUserImplicit( - [CombinatorialValues("Span", "ReadOnlySpan")] string destination) + [Fact] + public void Conversion_string_ReadOnlySpan_ThroughUserImplicit() { - var source = $$""" + var source = """ using System; D.M(new C()); class C { - public static implicit operator int[](C c) => new int[] { 4, 5, 6 }; + public static implicit operator string(C c) => "x"; } static class D { - public static void M({{destination}} xs) - { - foreach (var x in xs) - { - Console.Write(x); - } - } + public static void M(ReadOnlySpan xs) => Console.Write(xs.Length + " " + xs[0]); } """; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (3,5): error CS1503: Argument 1: cannot convert from 'C' to 'System.Span' + // (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.{destination}").WithLocation(3, 5)); + Diagnostic(ErrorCode.ERR_BadArgType, "new C()").WithArguments("1", "C", "System.ReadOnlySpan").WithLocation(3, 5)); - var expectedOutput = "456"; + var expectedOutput = "1 x"; var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -2800,7 +5652,172 @@ .maxstack 1 var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); verifier.VerifyIL("C.M", expectedIl); - comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_ExtensionMethodReceiver_Implicit() + { + var source = """ + using System; + + C.M(new Span(new int[] { 7, 8, 9 })); + + static class C + { + public static void M(Span arg) => arg.E(); + public static void E(this ReadOnlySpan arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (7,44): error CS1929: 'Span' 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(Span arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.Span", "E", "C.E(System.ReadOnlySpan)", "System.ReadOnlySpan").WithLocation(7, 44)); + + var expectedOutput = "8"; + + var expectedIl = """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0006: call "void C.E(System.ReadOnlySpan)" + IL_000b: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_ExtensionMethodReceiver_Implicit() + { + var source = """ + using System; + + C.M(new Span(new string[] { "x", "y" })); + + static class C + { + public static void M(Span arg) => arg.E(); + public static void E(this ReadOnlySpan arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (7,47): error CS1929: 'Span' 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(Span arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.Span", "E", "C.E(System.ReadOnlySpan)", "System.ReadOnlySpan").WithLocation(7, 47)); + + var expectedOutput = "y"; + + var expectedIl = """ + { + // Code size 17 (0x11) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan System.Span.op_Implicit(System.Span)" + IL_0006: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_000b: call "void C.E(System.ReadOnlySpan)" + IL_0010: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_ExtensionMethodReceiver_Implicit() + { + var source = """ + using System; + + C.M(new ReadOnlySpan(new string[] { "x", "y" })); + + static class C + { + public static void M(ReadOnlySpan arg) => arg.E(); + public static void E(this ReadOnlySpan arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (7,55): error CS1929: 'ReadOnlySpan' 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(ReadOnlySpan arg) => arg.E(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "arg").WithArguments("System.ReadOnlySpan", "E", "C.E(System.ReadOnlySpan)", "System.ReadOnlySpan").WithLocation(7, 55)); + + var expectedOutput = "y"; + + var expectedIl = """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan System.ReadOnlySpan.CastUp(System.ReadOnlySpan)" + IL_0006: call "void C.E(System.ReadOnlySpan)" + IL_000b: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_ExtensionMethodReceiver_Implicit() + { + var source = """ + using System; + + C.M("xyz"); + + static class C + { + public static void M(string arg) => arg.E(); + public static void E(this ReadOnlySpan arg) => Console.Write(arg[1]); + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (7,41): 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, 41)); + + var expectedOutput = "y"; + + var expectedIl = """ + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" + IL_0006: call "void C.E(System.ReadOnlySpan)" + IL_000b: ret + } + """; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + verifier.VerifyIL("C.M", expectedIl); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source); verifier = CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); verifier.VerifyIL("C.M", expectedIl); } @@ -3296,8 +6313,8 @@ static void E3(this string[] arg) { } Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string[])arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 57)); } - [Fact] - public void Conversion_Array_Span_ExtensionMethodReceiver_Opposite_Explicit_UserDefined() + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_ExtensionMethodReceiver_Opposite_Explicit_UserDefined(LanguageVersion langVersion) { var source = """ static class C @@ -3314,7 +6331,7 @@ readonly ref struct Span } } """; - var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular13); + var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { @@ -3326,16 +6343,6 @@ .maxstack 1 IL_000b: ret } """); - - var expectedDiagnostics = new[] - { - // (3,45): error CS0030: Cannot convert type 'System.Span' to 'int[]' - // static void M(System.Span arg) => ((int[])arg).E(); - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(int[])arg").WithArguments("System.Span", "int[]").WithLocation(3, 45) - }; - - CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); - CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); } [Theory] @@ -3440,6 +6447,46 @@ .locals init (object[] V_0) verifier.VerifyIL("C.M", expectedIl); } + [Theory, CombinatorialData] + public void Conversion_Span_ReadOnlySpan_RefSafety(bool cast) + { + var source = $$""" + using System; + class C + { + ReadOnlySpan M() + { + Span x = {{(cast ? "(Span)" : "")}}["a"]; + return x; + } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (7,16): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope + // return x; + Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(7, 16)); + } + + [Theory, CombinatorialData] + public void Conversion_ReadOnlySpan_ReadOnlySpan_RefSafety(bool cast) + { + var source = $$""" + using System; + class C + { + ReadOnlySpan M() + { + ReadOnlySpan x = {{(cast ? "(ReadOnlySpan)" : "")}}["a"]; + return x; + } + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (7,16): error CS8352: Cannot use variable 'x' in this context because it may expose referenced variables outside of their declaration scope + // return x; + Diagnostic(ErrorCode.ERR_EscapeVariable, "x").WithArguments("x").WithLocation(7, 16)); + } + [Fact] public void OverloadResolution_SpanVsIEnumerable() { @@ -3717,11 +6764,11 @@ public void OverloadResolution_ReadOnlySpanVsArray_ExpressionTree_04(LanguageVer var source = """ using System; using System.Linq.Expressions; - + C.R(() => C.M(null)); C.R(() => C.M(default)); C.R(() => C.M(default(object[]))); - + static class C { public static void R(Expression e) => e.Compile()(); @@ -4102,8 +7149,8 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } - [Theory, MemberData(nameof(LangVersions))] - public void OverloadResolution_SpanVsReadOnlySpan_03(LanguageVersion langVersion) + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_03() { var source = """ using System; @@ -4113,17 +7160,25 @@ public void OverloadResolution_SpanVsReadOnlySpan_03(LanguageVersion langVersion static class C { - public static void M(Span arg) { } - public static void M(ReadOnlySpan arg) { } + public static void M(Span arg) => Console.Write(1); + public static void M(ReadOnlySpan arg) => Console.Write(2); } """; - CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).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)); + + var expectedOutput = "22"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -4205,8 +7260,8 @@ static class C CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } - [Theory, MemberData(nameof(LangVersions))] - public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_04(LanguageVersion langVersion) + [Fact] + public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_04() { var source = """ using System; @@ -4216,11 +7271,11 @@ public void OverloadResolution_SpanVsReadOnlySpan_ExtensionMethodReceiver_04(Lan static class C { - public static void E(this Span arg) { } - public static void E(this ReadOnlySpan arg) { } + public static void E(this Span arg) => Console.Write(1); + public static void E(this ReadOnlySpan arg) => Console.Write(2); } """; - CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).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), @@ -4228,7 +7283,13 @@ public static void E(this ReadOnlySpan arg) { } // 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. + var expectedOutput = "22"; + + var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithSpanAndMemoryExtensions(source, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index 4f5656dafe83c..66c7ffdcd12cd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -645,7 +645,7 @@ .maxstack 4 .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 @@ -683,7 +683,7 @@ .maxstack 4 .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 @@ -725,7 +725,7 @@ .maxstack 4 .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 @@ -770,7 +770,7 @@ .maxstack 4 .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldc.i4.4 @@ -820,7 +820,7 @@ .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 @@ -862,7 +862,7 @@ .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 @@ -911,7 +911,7 @@ .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 @@ -957,7 +957,7 @@ .locals init (System.ReadOnlySpan V_0, //a System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, bool V_2) IL_0000: ldstr ""1"" - IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_0005: call ""System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)"" IL_000a: stloc.0 IL_000b: ldc.i4.4 IL_000c: ldc.i4.4 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs index 1024cb2514c2a..5b0e41ef03aa2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs @@ -3721,15 +3721,9 @@ public Example() {} // (5,38): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Field = stackalloc int[512]; Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 38), - // (5,38): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope - // public ReadOnlySpan Field = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 38), // (6,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(6, 50), - // (6,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope - // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(6, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(6, 50)); } [WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")] @@ -3808,10 +3802,7 @@ public Example() {} Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), - // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope - // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); } [WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")] @@ -3838,10 +3829,7 @@ record struct Example() Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), - // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope - // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); } [WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")] @@ -3868,10 +3856,7 @@ class Example Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), - // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope - // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); } [ConditionalFact(typeof(CoreClrOnly))] // For conversion from Span to ReadOnlySpan. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs index 37475d8c100c6..6a478e8fe103a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/Utf8StringsLiteralsTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; 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; @@ -3277,13 +3278,13 @@ ReadOnlySpan Test() var node = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single().Expression; var symbolInfo = model.GetSymbolInfo(node); - Assert.Equal("System.ReadOnlySpan System.String.op_Implicit(System.String? value)", symbolInfo.Symbol.ToTestDisplayString()); + Assert.Null(symbolInfo.Symbol); var typeInfo = model.GetTypeInfo(node); Assert.Equal("System.String", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.ReadOnlySpan", typeInfo.ConvertedType.ToTestDisplayString()); - Assert.True(model.GetConversion(node).IsUserDefined); + Assert.True(model.GetConversion(node).IsSpan); } [ConditionalFact(typeof(CoreClrOnly))] @@ -3305,13 +3306,14 @@ ReadOnlySpan Test() var node = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Single().Expression; var symbolInfo = model.GetSymbolInfo(node); - Assert.Equal("System.ReadOnlySpan System.String.op_Implicit(System.String? value)", symbolInfo.Symbol.ToTestDisplayString()); + Assert.Null(symbolInfo.Symbol); var typeInfo = model.GetTypeInfo(node); Assert.Equal("System.ReadOnlySpan", typeInfo.Type.ToTestDisplayString()); Assert.Equal("System.ReadOnlySpan", typeInfo.ConvertedType.ToTestDisplayString()); Assert.True(model.GetConversion(node).IsIdentity); + Assert.True(((IConversionOperation)model.GetOperation(node)).GetConversion().IsSpan); } [Theory] diff --git a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs index f22c1902567b0..85feb4e5a2da5 100644 --- a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs +++ b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs @@ -402,5 +402,9 @@ public static class WellKnownMemberNames internal const string LockTypeName = "Lock"; internal const string EnterScopeMethodName = "EnterScope"; internal const string LockScopeTypeName = "Scope"; + + internal const string CastUpMethodName = "CastUp"; + internal const string MemoryExtensionsTypeFullName = "System.MemoryExtensions"; + internal const string AsSpanMethodName = "AsSpan"; } } diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index fc8157feeaa3f..b187e0dcd877e 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -2357,6 +2357,24 @@ protected static CSharpCompilation CreateCompilationWithSpanAndMemoryExtensions( } } + protected static CSharpCompilation CreateCompilationWithIndexAndRangeAndSpanAndMemoryExtensions(CSharpTestSource text, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null, TargetFramework targetFramework = TargetFramework.NetCoreApp) + { + if (ExecutionConditionUtil.IsCoreClr) + { + return CreateCompilation(text, targetFramework: targetFramework, options: options, parseOptions: parseOptions); + } + else + { + var reference = CreateCompilation(new[] { TestSources.Index, TestSources.Range, TestSources.Span, TestSources.MemoryExtensions }, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + + return CreateCompilation( + text, + references: new List() { reference.EmitToImageReference() }, + options: options, + parseOptions: parseOptions); + } + } + internal static string GetIdForErrorCode(ErrorCode code) { return MessageProvider.Instance.GetIdForErrorCode((int)code); diff --git a/src/Compilers/Test/Utilities/CSharp/TestSources.cs b/src/Compilers/Test/Utilities/CSharp/TestSources.cs index 31e4e082c12a0..7af0695589dde 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestSources.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestSources.cs @@ -32,10 +32,10 @@ public Span(T[] arr) this.arr = arr; this.Length = arr is null ? 0 : arr.Length; } - + public Span(T[] arr, int start, int length) { - if (start + length > arr.Length) + if (start + length > arr?.Length) { throw new ArgumentOutOfRangeException(); } @@ -117,10 +117,10 @@ public ReadOnlySpan(T[] arr) this.arr = arr; this.Length = arr is null ? 0 : arr.Length; } - + public ReadOnlySpan(T[] arr, int start, int length) { - if (start + length > arr.Length) + if (start + length > arr?.Length) { throw new ArgumentOutOfRangeException(); } @@ -180,6 +180,13 @@ public ref readonly T Current public static implicit operator ReadOnlySpan(string stringValue) => string.IsNullOrEmpty(stringValue) ? default : new ReadOnlySpan((T[])(object)stringValue.ToCharArray()); public ReadOnlySpan Slice(int offset, int length) => new ReadOnlySpan(this.arr, offset, length); + +#nullable enable + public static ReadOnlySpan CastUp(ReadOnlySpan items) where TDerived : class?, T + { + return new ReadOnlySpan(items.arr, items.start, items.Length); + } +#nullable restore } public readonly ref struct SpanLike