diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 9310fe215b6ef..e61ebf6cc4dbb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -487,51 +487,223 @@ void checkConstraintLanguageVersionAndRuntimeSupportForConversion(SyntaxNode syn } else if (conversion.IsSpan) { + Debug.Assert(source.Type is not null); Debug.Assert(destination.OriginalDefinition.IsSpan() || destination.OriginalDefinition.IsReadOnlySpan()); CheckFeatureAvailability(syntax, MessageID.IDS_FeatureFirstClassSpan, diagnostics); - // PROTOTYPE: Check runtime APIs used for other span conversions once they are implemented. // 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.OriginalDefinition.IsSpan()) { - Error(diagnostics, - ErrorCode.ERR_MissingPredefinedMember, + Debug.Assert(destination.OriginalDefinition.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.OriginalDefinition.IsSpan() || source.Type.OriginalDefinition.IsReadOnlySpan()) + { + Debug.Assert(destination.OriginalDefinition.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.OriginalDefinition.IsSpan() + ? destination.OriginalDefinition + : source.Type.OriginalDefinition; + + MethodSymbol? castUpMethod = TryFindCastUpMethod(sourceForCastUp.OriginalDefinition, 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 TryFindSingleMember(type, WellKnownMemberNames.ImplicitConversionName, - static (method) => method is + return TryFindImplicitOperator(type, 0, static (_, parameterType) => + parameterType 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, + parameterPredicate: static (_, parameterType) => parameterType.IsSpan(), + returnTypePredicate: static (readonlySpanType, returnType) => readonlySpanType.Equals(returnType.OriginalDefinition, TypeCompareKind.ConsiderEverything)); + } + + private static MethodSymbol? TryFindImplicitOperator(TypeSymbol type, TArg arg, + Func parameterPredicate, + Func? returnTypePredicate = null) + { + return TryFindSingleMember(type, WellKnownMemberNames.ImplicitConversionName, (parameterPredicate, returnTypePredicate, arg), + static (arg, method) => method is { ParameterCount: 1, Arity: 0, IsStatic: true, DeclaredAccessibility: Accessibility.Public, - Parameters: [{ Type: ArrayTypeSymbol { IsSZArray: true, ElementType: TypeParameterSymbol } }] - }); + Parameters: [{ Type: { } parameterType }], + } && arg.parameterPredicate(arg.arg, parameterType) && + arg.returnTypePredicate?.Invoke(arg.arg, method.ReturnType) != false); + } + + internal static bool NeedsSpanCastUp(TypeSymbol source, TypeSymbol destination) + { + Debug.Assert(source.OriginalDefinition.IsSpan() || source.OriginalDefinition.IsReadOnlySpan()); + Debug.Assert(destination.OriginalDefinition.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); + + if (source.OriginalDefinition.IsReadOnlySpan()) + { + Debug.Assert(!sameElementTypes); + } + else + { + Debug.Assert(source.OriginalDefinition.IsSpan()); + } + + 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 TryFindSingleMember(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 (TryFindSingleMember(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? TryFindSingleMember(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..bca7036b54ccc 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; } @@ -3975,6 +3975,26 @@ private bool HasImplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio 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.OriginalDefinition.IsSpan() || source.OriginalDefinition.IsReadOnlySpan()) + { + if (destination.OriginalDefinition.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.OriginalDefinition.IsReadOnlySpan()) + { + var spanElementType = ((NamedTypeSymbol)destination).TypeArgumentsWithDefinitionUseSiteDiagnostics(ref useSiteInfo)[0]; + return spanElementType.SpecialType is SpecialType.System_Char; + } + } return false; @@ -4019,7 +4039,8 @@ private bool IgnoreUserDefinedSpanConversions(TypeSymbol? source, TypeSymbol? ta { return source is not null && target is not null && IsFeatureFirstClassSpanEnabled && - (ignoreUserDefinedSpanConversionsInOneDirection(source, target) || + (ignoreUserDefinedSpanConversionsInAnyDirection(source, target) || + ignoreUserDefinedSpanConversionsInOneDirection(source, target) || ignoreUserDefinedSpanConversionsInOneDirection(target, source)); static bool ignoreUserDefinedSpanConversionsInOneDirection(TypeSymbol a, TypeSymbol b) @@ -4032,11 +4053,21 @@ static bool ignoreUserDefinedSpanConversionsInOneDirection(TypeSymbol a, TypeSym return true; } - // PROTOTYPE: - any combination of `System.Span`/`System.ReadOnlySpan` - // PROTOTYPE: - `string` and `System.ReadOnlySpan` + // SPEC: - `string` and `System.ReadOnlySpan` + if (a.IsStringType() && b.IsReadOnlySpanChar()) + { + return true; + } return false; } + + static bool ignoreUserDefinedSpanConversionsInAnyDirection(TypeSymbol a, TypeSymbol b) + { + // SPEC: - any combination of `System.Span`/`System.ReadOnlySpan` + return (a.OriginalDefinition.IsSpan() || a.OriginalDefinition.IsReadOnlySpan()) && + (b.OriginalDefinition.IsSpan() || b.OriginalDefinition.IsReadOnlySpan()); + } } } } 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/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index 19dd483b3b044..a0ca992d6a705 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -323,13 +323,255 @@ .maxstack 1 """); } + [Theory, CombinatorialData] + public void Conversion_Span_ReadOnlySpan_Implicit( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) + { + var source = $$""" + using System; + 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); } } + """; + + 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("", """ + { + // 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); } } + """; + + 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_Span_ReadOnlySpan_Implicit_IdentityInnerConversion( + [CombinatorialLangVersions] LanguageVersion langVersion, + bool cast) + { + var source = $$""" + using System; + 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); } } + """; + + 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 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); } } + """; + + 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_MissingHelper() { var source = """ using System; Span s = arr(); - static int[] arr() => new int[] { 1, 2, 3 }; + static int[] arr() => throw null; namespace System { @@ -338,311 +580,1234 @@ public readonly ref struct Span } } """; - 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); } - [Theory, CombinatorialData] - public void Conversion_Array_Span_Implicit_DifferentOperator( - [CombinatorialValues("int[]", "T[][]")] string parameterType) + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_MissingHelper() { - var source = $$""" + var source = """ using System; - Span s = 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 implicit operator Span({{parameterType}} array) => throw null; - } + public readonly ref struct Span { } + public readonly ref struct ReadOnlySpan { } } """; - 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)); + + 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_ExplicitOperator() + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MissingHelper_01() { var source = """ using System; - Span s = 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 'System.Span.op_Implicit' - // Span s = arr(); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + + 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_Array_Span_Explicit_ExplicitOperator() + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MissingHelper_02() { 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 static implicit operator ReadOnlySpan(Span s) => throw null; } + 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,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_UnrecognizedModreq() + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MissingHelper_03() { - /* - 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 + var source = """ + using System; + ReadOnlySpan s = source(); + static Span source() => throw null; + + namespace System { - .pack 0 - .size 1 - .method public hidebysig specialname static valuetype System.Span`1 modreq(A) op_Implicit(!T[] 'array') cil managed + public readonly ref struct Span { } + public readonly ref struct ReadOnlySpan { - .maxstack 1 - ret + public static ReadOnlySpan CastUp(ReadOnlySpan s) where TDerived : class, T => throw null; } } - .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 - } + """; + + 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)] + public void Conversion_Array_Span_Implicit_DifferentHelper(string signature, bool works) + { + var source = $$""" + using System; + ReadOnlySpan s = source(); + static string source() => ""; + + namespace System + { + public readonly ref struct ReadOnlySpan { } + 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)); + } + } + + [Fact] + public void Conversion_Array_Span_Implicit_ExplicitOperator() + { + var source = """ + using System; + Span s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static explicit operator Span(T[] array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'System.Span.op_Implicit' + // Span s = arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Explicit_ExplicitOperator() + { + var source = """ + using System; + Span s = (Span)arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + + namespace System + { + public readonly ref struct Span + { + public static explicit operator Span(T[] array) => throw null; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,15): error CS0656: Missing compiler required member 'Span.op_Implicit' + // Span s = (Span)arr(); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "(Span)arr()").WithArguments("System.Span", "op_Implicit").WithLocation(2, 15)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_UnrecognizedModreq() + { + /* + public struct Span + { + public static implicit operator modreq(A) Span(T[] array) => throw null; + } + public class A { } + public static class C + { + public static void M(Span s) { } + } + */ + var ilSource = """ + .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType + { + .pack 0 + .size 1 + .method public hidebysig specialname static valuetype System.Span`1 modreq(A) op_Implicit(!T[] 'array') cil managed + { + .maxstack 1 + ret + } + } + .class public auto ansi sealed beforefieldinit A extends System.Object + { + } + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + { + .maxstack 1 + ret + } + } + """; + var source = """ + C.M(new int[] { 1, 2, 3 }); + """; + CreateCompilationWithIL(source, ilSource).VerifyDiagnostics( + // (1,5): error CS0570: 'Span.implicit operator Span(T[])' is not supported by the language + // C.M(new int[] { 1, 2, 3 }); + Diagnostic(ErrorCode.ERR_BindToBogus, "new int[] { 1, 2, 3 }").WithArguments("System.Span.implicit operator System.Span(T[])").WithLocation(1, 5)); + } + + [Fact] + public void Conversion_Array_Span_Implicit_UnrecognizedModopt() + { + /* + public struct Span + { + public static implicit operator modopt(A) Span(T[] array) => throw null; + } + public class A { } + public static class C + { + public static void M(Span s) { } + } + */ + var ilSource = """ + .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType + { + .pack 0 + .size 1 + .method public hidebysig specialname static valuetype System.Span`1 modopt(A) op_Implicit(!T[] 'array') cil managed + { + .maxstack 1 + ret + } + } + .class public auto ansi sealed beforefieldinit A extends System.Object + { + } + .class public auto ansi abstract sealed beforefieldinit C extends System.Object + { + .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + { + .maxstack 1 + ret + } + } + """; + var source = """ + C.M(arr()); + static int[] arr() => new int[] { 1, 2, 3 }; + """; + var comp = CreateCompilationWithIL(source, ilSource); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "int[] Program.<
$>g__arr|0_0()" + IL_0005: call "System.Span System.Span.op_Implicit(int[])" + IL_000a: call "void C.M(System.Span)" + IL_000f: ret + } + """); + } + + [Theory] + [InlineData("where TDerived : T")] + [InlineData("")] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MissingClassConstraint(string constraints) + { + 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) {{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)); + var verifier = CompileAndVerify(comp, expectedOutput: "12").VerifyDiagnostics(); + if (ExecutionConditionUtil.IsCoreClr) + { + verifier.VerifyIL("", """ + { + // Code size 36 (0x24) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: call "System.Span System.Span.op_Implicit(int[])" + IL_000f: call "void C.M1(System.Span)" + IL_0014: ldtoken ".__StaticArrayInitTypeSize=4_Align=4 .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE4" + IL_0019: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" + IL_001e: call "void C.M2(System.ReadOnlySpan)" + IL_0023: ret + } + """); + } + else + { + verifier.VerifyIL("", """ + { + // Code size 63 (0x3f) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: call "System.Span System.Span.op_Implicit(int[])" + IL_000f: call "void C.M1(System.Span)" + IL_0014: ldsfld "int[] .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE_A6" + IL_0019: dup + IL_001a: brtrue.s IL_0034 + IL_001c: pop + IL_001d: ldc.i4.1 + IL_001e: newarr "int" + IL_0023: dup + IL_0024: ldtoken "int .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE" + IL_0029: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_002e: dup + IL_002f: stsfld "int[] .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE_A6" + IL_0034: newobj "System.ReadOnlySpan..ctor(int[])" + IL_0039: call "void C.M2(System.ReadOnlySpan)" + IL_003e: ret + } + """); + } + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_ConstantData_NotWellKnownSpan(LanguageVersion langVersion) + { + var source = """ + extern alias span; + C.M1(new[] { 1 }); + C.M2(new[] { 2 }); + static class C + { + public static void M1(span::System.Span s) => System.Console.Write(s[0]); + public static void M2(span::System.ReadOnlySpan s) => System.Console.Write(s[0]); + } + """; + var spanDll = CreateCompilation(TestSources.Span, options: TestOptions.UnsafeReleaseDll) + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span"]); + var verifier = CompileAndVerify([source, TestSources.Span], + references: [spanDll], + expectedOutput: "12", + verify: Verification.Fails, + // warning CS0436: Type conflicts with imported type + options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("CS0436", ReportDiagnostic.Suppress), + parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", """ + { + // Code size 41 (0x29) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stelem.i4 + IL_000a: call "System.Span System.Span.op_Implicit(int[])" + IL_000f: call "void C.M1(System.Span)" + IL_0014: ldc.i4.1 + IL_0015: newarr "int" + IL_001a: dup + IL_001b: ldc.i4.0 + IL_001c: ldc.i4.2 + IL_001d: stelem.i4 + IL_001e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" + IL_0023: call "void C.M2(System.ReadOnlySpan)" + IL_0028: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_MultipleSpans_01( + [CombinatorialValues("Span", "ReadOnlySpan")] string type) + { + string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct {{type}} + { + public static implicit operator {{type}}(T[] array) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(); + + var source = $$""" + using System; + {{type}} s = arr(); + use(s); + static int[] arr() => new int[] { 1, 2, 3 }; + static void use({{type}} s) { } + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Internal"); + verifier.VerifyDiagnostics( + // (2,1): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. + // Span s = arr(); + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(2, 1), + // (5,17): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. + // static void use(Span s) { } + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(5, 17), + // (5,41): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. + // public static implicit operator Span(T[] array) + Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(5, 41)); + + verifier.VerifyIL("", $$""" + { + // Code size 16 (0x10) + .maxstack 1 + IL_0000: call "int[] Program.<
$>g__arr|0_0()" + IL_0005: call "System.{{type}} System.{{type}}.op_Implicit(int[])" + IL_000a: call "void Program.<
$>g__use|0_1(System.{{type}})" + IL_000f: ret + } + """); + } + + [Theory, CombinatorialData] + public void Conversion_Array_Span_Implicit_MultipleSpans_02( + [CombinatorialValues("Span", "ReadOnlySpan")] string type) + { + string getSpanSource(string output) => $$""" + namespace System + { + public readonly ref struct {{type}} + { + public static implicit operator {{type}}(T[] array) + { + Console.Write("{{output}}"); + return default; + } + } + } + """; + + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["lib"]); + + var source = $$""" + extern alias lib; + lib::System.{{type}} s = arr(); + static int[] arr() => new int[] { 1, 2, 3 }; + """; + + var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "External"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", $$""" + { + // Code size 12 (0xc) + .maxstack 1 + IL_0000: call "int[] Program.<
$>g__arr|0_0()" + IL_0005: call "System.{{type}} System.{{type}}.op_Implicit(int[])" + 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 } - """; - 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)); + """); + + 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_Array_Span_Implicit_UnrecognizedModopt() + public void Conversion_Span_ReadOnlySpan_CastUp_Implicit_MultipleSpans() { - /* - public struct Span - { - public static implicit operator modopt(A) Span(T[] array) => throw null; - } - public class A { } - public static class C + var span1 = CreateCompilation(""" + namespace System; + public readonly ref struct Span; + public readonly ref struct ReadOnlySpan { - public static void M(Span s) { } + public static ReadOnlySpan CastUp(ReadOnlySpan span) where TDerived : class, T + { + Console.WriteLine("CastUp"); + return default; + } } - */ - var ilSource = """ - .class public sequential ansi sealed beforefieldinit System.Span`1 extends System.ValueType + """, assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["span1"]); + var span2 = CreateCompilation(""" + extern alias span1; + namespace System; + public readonly ref struct Span { - .pack 0 - .size 1 - .method public hidebysig specialname static valuetype System.Span`1 modopt(A) op_Implicit(!T[] 'array') cil managed + public static implicit operator span1::System.ReadOnlySpan(Span span) { - .maxstack 1 - ret + Console.WriteLine("Span2 to ReadOnlySpan1"); + return default; } } - .class public auto ansi sealed beforefieldinit A extends System.Object + """, [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 } - .class public auto ansi abstract sealed beforefieldinit C extends System.Object + """); + } + + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_01() + { + static string getSpanSource(string output) => $$""" + namespace System { - .method public hidebysig static void M(valuetype System.Span`1 s) cil managed + public readonly ref struct ReadOnlySpan { - .maxstack 1 - ret + 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 = """ - C.M(arr()); - static int[] arr() => new int[] { 1, 2, 3 }; + using System; + ReadOnlySpan s = source(); + use(s); + static ReadOnlySpan source() => default; + static void use(ReadOnlySpan s) { } """; - var comp = CreateCompilationWithIL(source, ilSource); - var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + + 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 "int[] Program.<
$>g__arr|0_0()" - IL_0005: call "System.Span System.Span.op_Implicit(int[])" - IL_000a: call "void C.M(System.Span)" + IL_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 } """); } - [Theory, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Implicit_ConstantData(LanguageVersion langVersion) + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_02() { - var source = """ - using System; + 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; + } + } + } + """; - C.M1(new[] { 1 }); - C.M2(new[] { 2 }); + var spanComp = CreateCompilation(getSpanSource("External"), assemblyName: "Span1") + .VerifyDiagnostics() + .EmitToImageReference(aliases: ["lib"]); - static class C + 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("", """ { - public static void M1(Span s) => Console.Write(s[0]); - public static void M2(ReadOnlySpan s) => Console.Write(s[0]); + // 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)); - var comp = CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); - var verifier = CompileAndVerify(comp, expectedOutput: "12").VerifyDiagnostics(); - if (ExecutionConditionUtil.IsCoreClr) - { - verifier.VerifyIL("", """ - { - // Code size 36 (0x24) - .maxstack 4 - IL_0000: ldc.i4.1 - IL_0001: newarr "int" - IL_0006: dup - IL_0007: ldc.i4.0 - IL_0008: ldc.i4.1 - IL_0009: stelem.i4 - IL_000a: call "System.Span System.Span.op_Implicit(int[])" - IL_000f: call "void C.M1(System.Span)" - IL_0014: ldtoken ".__StaticArrayInitTypeSize=4_Align=4 .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE4" - IL_0019: call "System.ReadOnlySpan System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan(System.RuntimeFieldHandle)" - IL_001e: call "void C.M2(System.ReadOnlySpan)" - IL_0023: ret - } - """); - } - else - { - verifier.VerifyIL("", """ + 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 { - // Code size 63 (0x3f) - .maxstack 4 - IL_0000: ldc.i4.1 - IL_0001: newarr "int" - IL_0006: dup - IL_0007: ldc.i4.0 - IL_0008: ldc.i4.1 - IL_0009: stelem.i4 - IL_000a: call "System.Span System.Span.op_Implicit(int[])" - IL_000f: call "void C.M1(System.Span)" - IL_0014: ldsfld "int[] .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE_A6" - IL_0019: dup - IL_001a: brtrue.s IL_0034 - IL_001c: pop - IL_001d: ldc.i4.1 - IL_001e: newarr "int" - IL_0023: dup - IL_0024: ldtoken "int .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE" - IL_0029: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - IL_002e: dup - IL_002f: stsfld "int[] .26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE_A6" - IL_0034: newobj "System.ReadOnlySpan..ctor(int[])" - IL_0039: call "void C.M2(System.ReadOnlySpan)" - IL_003e: ret + 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)); } - [Theory, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Implicit_ConstantData_NotWellKnownSpan(LanguageVersion langVersion) + [Fact] + public void Conversion_ReadOnlySpan_ReadOnlySpan_Implicit_MultipleSpans_04() { - var source = """ - extern alias span; - C.M1(new[] { 1 }); - C.M2(new[] { 2 }); - static class C + 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 void M1(span::System.Span s) => System.Console.Write(s[0]); - public static void M2(span::System.ReadOnlySpan s) => System.Console.Write(s[0]); + public static ReadOnlySpan CastUp(span1::System.ReadOnlySpan other) where TDerived : class, T + { + Console.Write("Span2"); + return default; + } } - """; - var spanDll = CreateCompilation(TestSources.Span, options: TestOptions.UnsafeReleaseDll) + """, [span1], assemblyName: "Span2") .VerifyDiagnostics() - .EmitToImageReference(aliases: ["span"]); - var verifier = CompileAndVerify([source, TestSources.Span], - references: [spanDll], - expectedOutput: "12", - verify: Verification.Fails, - // warning CS0436: Type conflicts with imported type - options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("CS0436", ReportDiagnostic.Suppress), - parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)); + .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 41 (0x29) - .maxstack 4 - IL_0000: ldc.i4.1 - IL_0001: newarr "int" - IL_0006: dup - IL_0007: ldc.i4.0 - IL_0008: ldc.i4.1 - IL_0009: stelem.i4 - IL_000a: call "System.Span System.Span.op_Implicit(int[])" - IL_000f: call "void C.M1(System.Span)" - IL_0014: ldc.i4.1 - IL_0015: newarr "int" - IL_001a: dup - IL_001b: ldc.i4.0 - IL_001c: ldc.i4.2 - IL_001d: stelem.i4 - IL_001e: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(int[])" - IL_0023: call "void C.M2(System.ReadOnlySpan)" - IL_0028: ret + // 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 } """); } - [Theory, CombinatorialData] - public void Conversion_Array_Span_Implicit_MultipleSpans_01( - [CombinatorialValues("Span", "ReadOnlySpan")] string type) + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_MultipleSpans_01() { - string getSpanSource(string output) => $$""" + static string getSpanSource(string output) => $$""" namespace System { - public readonly ref struct {{type}} + public readonly ref struct ReadOnlySpan; + public static class MemoryExtensions { - public static implicit operator {{type}}(T[] array) + public static ReadOnlySpan AsSpan(string s) { Console.Write("{{output}}"); return default; @@ -655,49 +1820,42 @@ public readonly ref struct {{type}} .VerifyDiagnostics() .EmitToImageReference(); - var source = $$""" + var source = """ using System; - {{type}} s = arr(); - use(s); - static int[] arr() => new int[] { 1, 2, 3 }; - static void use({{type}} s) { } - """; - - var comp = CreateCompilation([source, getSpanSource("Internal")], [spanComp], assemblyName: "Consumer"); - var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: "Internal"); - verifier.VerifyDiagnostics( - // (2,1): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. - // Span s = arr(); - Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(2, 1), - // (5,17): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. - // static void use(Span s) { } - Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(5, 17), - // (5,41): warning CS0436: The type 'Span' in '' conflicts with the imported type 'Span' in 'Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. Using the type defined in ''. - // public static implicit operator Span(T[] array) - Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, $"{type}").WithArguments("", $"System.{type}", "Span1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", $"System.{type}").WithLocation(5, 41)); + ReadOnlySpan s = source(); + use(s); + static string source() => ""; + static void use(ReadOnlySpan s) { } + """; - verifier.VerifyIL("", $$""" + 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 "int[] Program.<
$>g__arr|0_0()" - IL_0005: call "System.{{type}} System.{{type}}.op_Implicit(int[])" - IL_000a: call "void Program.<
$>g__use|0_1(System.{{type}})" + IL_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 } """); } - [Theory, CombinatorialData] - public void Conversion_Array_Span_Implicit_MultipleSpans_02( - [CombinatorialValues("Span", "ReadOnlySpan")] string type) + [Fact] + public void Conversion_string_ReadOnlySpan_Implicit_MultipleSpans_02() { - string getSpanSource(string output) => $$""" + static string getSpanSource(string output) => $$""" namespace System { - public readonly ref struct {{type}} + public readonly ref struct ReadOnlySpan; + public static class MemoryExtensions { - public static implicit operator {{type}}(T[] array) + public static ReadOnlySpan AsSpan(string s) { Console.Write("{{output}}"); return default; @@ -710,36 +1868,113 @@ public readonly ref struct {{type}} .VerifyDiagnostics() .EmitToImageReference(aliases: ["lib"]); - var source = $$""" + var source = """ extern alias lib; - lib::System.{{type}} s = arr(); - static int[] arr() => new int[] { 1, 2, 3 }; + 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 + } + """); - verifier.VerifyIL("", $$""" + 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 "int[] Program.<
$>g__arr|0_0()" - IL_0005: call "System.{{type}} System.{{type}}.op_Implicit(int[])" + IL_0000: call "string Program.<
$>g__source|0_0()" + IL_0005: call "System.ReadOnlySpan System.MemoryExtensions.AsSpan(string)" IL_000a: pop IL_000b: ret } """); } - [Theory, CombinatorialData] - public void Conversion_Array_Span_Implicit_SemanticModel( - [CombinatorialValues("Span", "ReadOnlySpan")] string destination) + [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 { - System.{{destination}} M(int[] arg) { return arg; } + {{targetType}} M({{sourceType}} arg) { return arg; } } """; @@ -751,8 +1986,8 @@ class C Assert.Equal("arg", arg!.ToString()); var argType = model.GetTypeInfo(arg); - Assert.Equal("System.Int32[]", argType.Type.ToTestDisplayString()); - Assert.Equal($"System.{destination}", argType.ConvertedType.ToTestDisplayString()); + Assert.Equal(sourceType, argType.Type.ToTestDisplayString()); + Assert.Equal(targetType, argType.ConvertedType.ToTestDisplayString()); var argConv = model.GetConversion(arg); Assert.Equal(ConversionKind.ImplicitSpan, argConv.Kind); @@ -799,36 +2034,333 @@ class C """); } - { - var cast = casts[1]; - Assert.Equal("(ReadOnlySpan)x", cast.ToString()); + { + 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_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, 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_string_ReadOnlySpan_Implicit_UnrelatedElementType(LanguageVersion langVersion) + { + var source = """ + using System; + class C + { + ReadOnlySpan M(string arg) => arg; + } - var op = (IConversionOperation)model.GetOperation(cast)!; - var conv = op.GetConversion(); - Assert.Equal(ConversionKind.ExplicitSpan, conv.Kind); + 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)); + } - 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_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, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Implicit_UnrelatedElementType(LanguageVersion langVersion) + public void Conversion_string_Span_Explicit(LanguageVersion langVersion) { var source = """ + using System; class C { - System.Span M(int[] arg) => arg; + Span M(string arg) => (Span)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)); + // (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] @@ -944,6 +2476,133 @@ class C 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; + } + """; + + 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)); + + 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) + }; + + 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; + } + """; + + 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)); + + 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) + }; + + 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 +2767,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 +2879,64 @@ interface I { } Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "arg").WithArguments("I[]", targetType).WithLocation(5, 52)); } + [Fact] + public void Conversion_Span_ReadOnlySpan_Implicit_NullableAnalysis_NestedNullability_Covariant() + { + var source = """ + #nullable enable + using System; + class C + { + ReadOnlySpan> M(Span> arg) => arg; + } + interface I { } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (5,56): 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(5, 56)); + + var expectedDiagnostics = new[] + { + // (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("System.Span>", "System.ReadOnlySpan>").WithLocation(5, 56) + }; + + 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_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) { @@ -1181,6 +2991,71 @@ static void M4(this ReadOnlySpan arg) { } 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(ReadOnlySpan a, ReadOnlySpan b) + { + a.M1(); b.M1(); + a.M2(); b.M2(); + } + static void M1(this ReadOnlySpan arg) { } + static void M2(this ReadOnlySpan arg) { } + } + """; + 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("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] public void Conversion_Array_Span_Implicit_Ref_01( [CombinatorialValues("ref", "ref readonly", "in")] string modifier) @@ -1348,34 +3223,82 @@ .locals init (System.Span V_0, //s IL_0018: stloc.1 IL_0019: ret } - """); + """); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Opposite_Implicit(LanguageVersion langVersion) + { + var source = """ + class C + { + int[] M1(System.Span arg) => arg; + int[] M2(System.ReadOnlySpan arg) => arg; + object[] M3(System.ReadOnlySpan arg) => arg; + string[] M4(System.ReadOnlySpan arg) => arg; + } + """; + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( + // (3,40): error CS0029: Cannot implicitly convert type 'System.Span' to 'int[]' + // int[] M1(System.Span arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span", "int[]").WithLocation(3, 40), + // (4,48): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'int[]' + // int[] M2(System.ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "int[]").WithLocation(4, 48), + // (5,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'object[]' + // object[] M3(System.ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "object[]").WithLocation(5, 54), + // (6,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'string[]' + // string[] M4(System.ReadOnlySpan arg) => arg; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_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_Array_Span_Opposite_Implicit(LanguageVersion langVersion) + public void Conversion_string_ReadOnlySpan_Opposite_Implicit(LanguageVersion langVersion) { var source = """ + using System; class C { - int[] M1(System.Span arg) => arg; - int[] M2(System.ReadOnlySpan arg) => arg; - object[] M3(System.ReadOnlySpan arg) => arg; - string[] M4(System.ReadOnlySpan arg) => arg; + string M1(ReadOnlySpan arg) => arg; } """; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( - // (3,40): error CS0029: Cannot implicitly convert type 'System.Span' to 'int[]' - // int[] M1(System.Span arg) => arg; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.Span", "int[]").WithLocation(3, 40), - // (4,48): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'int[]' - // int[] M2(System.ReadOnlySpan arg) => arg; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "int[]").WithLocation(4, 48), - // (5,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'object[]' - // object[] M3(System.ReadOnlySpan arg) => arg; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "object[]").WithLocation(5, 54), - // (6,54): error CS0029: Cannot implicitly convert type 'System.ReadOnlySpan' to 'string[]' - // string[] M4(System.ReadOnlySpan arg) => arg; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); + // (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))] @@ -1405,6 +3328,54 @@ class C Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string[])arg").WithArguments("System.ReadOnlySpan", "string[]").WithLocation(6, 54)); } + [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)); + } + [Fact] public void Conversion_Array_Span_Opposite_Explicit_UserDefined() { @@ -1446,7 +3417,90 @@ .maxstack 1 } [Fact] - public void Conversion_Array_Span_Implicit_Params() + public void Conversion_Span_ReadOnlySpan_Opposite_Explicit_UserDefined() + { + var source = """ + using System; + class C + { + Span M(ReadOnlySpan arg) => (Span)arg; + } + + 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.Regular13, 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 + } + """); + + var expectedDiagnostics = new[] + { + // (4,44): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'System.Span' + // Span M(ReadOnlySpan arg) => (Span)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)arg").WithArguments("System.ReadOnlySpan", "System.Span").WithLocation(4, 44) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Conversion_string_ReadOnlySpan_Opposite_Explicit_UserDefined() + { + 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.Regular13); + 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 + } + """); + + var expectedDiagnostics = new[] + { + // (4,42): error CS0030: Cannot convert type 'System.ReadOnlySpan' to 'string' + // string M(ReadOnlySpan arg) => (string)arg; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(string)arg").WithArguments("System.ReadOnlySpan", "string").WithLocation(4, 42) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, MemberData(nameof(LangVersions))] + public void Conversion_Array_Span_Implicit_Params(LanguageVersion langVersion) { var source = """ using System; @@ -1464,7 +3518,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,36 +3559,220 @@ 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 .locals init (object[] V_0) IL_0000: ldarg.0 - IL_0001: ldc.i4.1 - IL_0002: newarr "object" - IL_0007: dup - IL_0008: ldc.i4.0 - IL_0009: ldarg.1 - IL_000a: stelem.ref - IL_000b: newobj "System.Span..ctor(object[])" - IL_0010: call "void C.M1(params System.Span)" - IL_0015: ldarg.0 - IL_0016: ldarg.1 - IL_0017: stloc.0 - IL_0018: ldloc.0 - IL_0019: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(object[])" - IL_001e: call "void C.M2(params System.ReadOnlySpan)" - IL_0023: ldarg.0 - IL_0024: ldarg.1 - IL_0025: stloc.0 - IL_0026: ldloc.0 - IL_0027: call "void C.M3(params object[])" - IL_002c: ret + IL_0001: ldc.i4.1 + IL_0002: newarr "object" + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldarg.1 + IL_000a: stelem.ref + IL_000b: newobj "System.Span..ctor(object[])" + IL_0010: call "void C.M1(params System.Span)" + IL_0015: ldarg.0 + IL_0016: ldarg.1 + IL_0017: stloc.0 + IL_0018: ldloc.0 + IL_0019: call "System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(object[])" + IL_001e: call "void C.M2(params System.ReadOnlySpan)" + IL_0023: ldarg.0 + IL_0024: ldarg.1 + IL_0025: stloc.0 + IL_0026: ldloc.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 +4351,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, @@ -2430,114 +4785,294 @@ public void Conversion_Array_Span_Variance_01( using System; class C {{constraints}} { - ReadOnlySpan F1(T[] x) => x; - ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; - Span F3(T[] x) => x; - Span F4(T[] x) => (Span)x; + 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( + // (4,34): error CS0029: Cannot implicitly convert type 'T[]' to 'System.ReadOnlySpan' + // ReadOnlySpan F1(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(4, 34), + // (5,34): error CS0030: Cannot convert type 'T[]' to 'System.ReadOnlySpan' + // ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(5, 34), + // (6,26): error CS0029: Cannot implicitly convert type 'T[]' to 'System.Span' + // Span F3(T[] x) => x; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.Span").WithLocation(6, 26), + // (7,26): error CS0030: Cannot convert type 'T[]' to 'System.Span' + // Span F4(T[] x) => (Span)x; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)x").WithArguments("T[]", "System.Span").WithLocation(7, 26)); + } + + [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.Regular.WithLanguageVersion(langVersion)).VerifyDiagnostics( - // (4,34): error CS0029: Cannot implicitly convert type 'T[]' to 'System.ReadOnlySpan' - // ReadOnlySpan F1(T[] x) => x; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(4, 34), - // (5,34): error CS0030: Cannot convert type 'T[]' to 'System.ReadOnlySpan' - // ReadOnlySpan F2(T[] x) => (ReadOnlySpan)x; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(ReadOnlySpan)x").WithArguments("T[]", "System.ReadOnlySpan").WithLocation(5, 34), - // (6,26): error CS0029: Cannot implicitly convert type 'T[]' to 'System.Span' - // Span F3(T[] x) => x; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T[]", "System.Span").WithLocation(6, 26), - // (7,26): error CS0030: Cannot convert type 'T[]' to 'System.Span' - // Span F4(T[] x) => (Span)x; - Diagnostic(ErrorCode.ERR_NoExplicitConv, "(Span)x").WithArguments("T[]", "System.Span").WithLocation(7, 26)); + + 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(); } - [Theory, MemberData(nameof(LangVersions))] - public void Conversion_Array_Span_Variance_02(LanguageVersion langVersion) + [Fact] + public void Conversion_Span_ReadOnlySpan_CastUp_ThroughUserImplicit() { var source = """ using System; - class C - where T : class - where U : class, T + + 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 Span(C c) => new Span(new string[] { "x" }); + } + + static class D + { + 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 +5335,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); } @@ -3717,11 +6417,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 +6802,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 +6813,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 +6913,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 +6924,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 +6936,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/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/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