@@ -11300,5 +11300,162 @@ public static void M({{scoped2}} {{modifier}} R r) { }
1130011300 """ ;
1130111301 CreateCompilation ( source ) . VerifyDiagnostics ( ) ;
1130211302 }
11303+
11304+ [ Theory , WorkItem ( "https://github.com/dotnet/roslyn/issues/78711" ) ]
11305+ [ InlineData ( "public void UseSpan(Span<int> span)" , 17 ) ]
11306+ [ InlineData ( "void I.UseSpan(Span<int> span)" , 12 ) ]
11307+ public void RefStructInterface_ScopedDifference ( string implSignature , int column )
11308+ {
11309+ // This is a case where interface methods need to be treated specially in scoped variance checks.
11310+ // Because a ref struct can implement the interface, we need to compare the signatures as if the interface has a receiver parameter which is ref-to-ref-struct.
11311+ var source = $$ """
11312+ using System;
11313+
11314+ interface I
11315+ {
11316+ void UseSpan(scoped Span<int> span);
11317+ }
11318+
11319+ ref struct RS : I
11320+ {
11321+ public Span<int> Span { get; set; }
11322+ public RS(Span<int> span) => Span = span;
11323+
11324+ {{ implSignature }} // 1
11325+ {
11326+ this.Span = span;
11327+ }
11328+ }
11329+ """ ;
11330+
11331+ var comp = CreateCompilation ( source , targetFramework : TargetFramework . Net90 ) ;
11332+ comp . VerifyEmitDiagnostics (
11333+ // (13,17): error CS8987: The 'scoped' modifier of parameter 'span' doesn't match overridden or implemented member.
11334+ // public void UseSpan(Span<int> span)
11335+ Diagnostic ( ErrorCode . ERR_ScopedMismatchInParameterOfOverrideOrImplementation , "UseSpan" ) . WithArguments ( "span" ) . WithLocation ( 13 , column ) ) ;
11336+ }
11337+
11338+ [ Theory , WorkItem ( "https://github.com/dotnet/roslyn/issues/78711" ) ]
11339+ [ InlineData ( "public readonly void UseSpan(Span<int> span)" ) ]
11340+ [ InlineData ( "readonly void I.UseSpan(Span<int> span)" ) ]
11341+ public void RefStructInterface_ScopedDifference_ReadonlyImplementation ( string implSignature )
11342+ {
11343+ var source = $$ """
11344+ using System;
11345+
11346+ interface I
11347+ {
11348+ void UseSpan(scoped Span<int> span);
11349+ }
11350+
11351+ ref struct RS : I
11352+ {
11353+ public Span<int> Span { get; set; }
11354+ public RS(Span<int> span) => Span = span;
11355+
11356+ {{ implSignature }}
11357+ {
11358+ Span = span; // 1
11359+ }
11360+ }
11361+ """ ;
11362+
11363+ var comp = CreateCompilation ( source , targetFramework : TargetFramework . Net90 ) ;
11364+ comp . VerifyEmitDiagnostics (
11365+ // (15,9): error CS1604: Cannot assign to 'Span' because it is read-only
11366+ // Span = span; // 1
11367+ Diagnostic ( ErrorCode . ERR_AssgReadonlyLocal , "Span" ) . WithArguments ( "Span" ) . WithLocation ( 15 , 9 ) ) ;
11368+ }
11369+
11370+ [ Fact , WorkItem ( "https://github.com/dotnet/roslyn/issues/78711" ) ]
11371+ public void SimilarScenarioAs78711InvolvingNonReceiverParameter ( )
11372+ {
11373+ // Demonstrate a scenario similar to https://github.com/dotnet/roslyn/issues/78711 involving a non-receiver parameter has consistent behavior
11374+ // In this case, the interface and implementation parameters cannot differ by type. But it as close as we can get to the receiver parameter case.
11375+ var source = """
11376+ using System;
11377+
11378+ interface I
11379+ {
11380+ void UseSpan1(ref RS rs, scoped Span<int> span);
11381+ void UseSpan2(ref readonly RS rs, scoped Span<int> span);
11382+ }
11383+
11384+ class C : I
11385+ {
11386+ public void UseSpan1(ref RS rs, Span<int> span) // 1
11387+ {
11388+ rs.Span = span;
11389+ }
11390+
11391+ public void UseSpan2(ref readonly RS rs, Span<int> span)
11392+ {
11393+ rs.Span = span; // 2
11394+ }
11395+ }
11396+
11397+ ref struct RS
11398+ {
11399+ public Span<int> Span { get; set; }
11400+ public RS(Span<int> span) => Span = span;
11401+ }
11402+ """ ;
11403+
11404+ var comp = CreateCompilation ( source , targetFramework : TargetFramework . Net90 ) ;
11405+ comp . VerifyEmitDiagnostics (
11406+ // (11,17): error CS8987: The 'scoped' modifier of parameter 'span' doesn't match overridden or implemented member.
11407+ // public void UseSpan1(ref RS rs, Span<int> span) // 1
11408+ Diagnostic ( ErrorCode . ERR_ScopedMismatchInParameterOfOverrideOrImplementation , "UseSpan1" ) . WithArguments ( "span" ) . WithLocation ( 11 , 17 ) ,
11409+ // (18,9): error CS8332: Cannot assign to a member of variable 'rs' or use it as the right hand side of a ref assignment because it is a readonly variable
11410+ // rs.Span = span; // 2
11411+ Diagnostic ( ErrorCode . ERR_AssignReadonlyNotField2 , "rs.Span" ) . WithArguments ( "variable" , "rs" ) . WithLocation ( 18 , 9 ) ) ;
11412+ }
11413+
11414+ [ Fact , WorkItem ( "https://github.com/dotnet/roslyn/issues/78711" ) ]
11415+ public void Repro_78711 ( )
11416+ {
11417+ var source = """
11418+ using System;
11419+
11420+ public static class Demo
11421+ {
11422+ public static void Show()
11423+ {
11424+ var stru = new Stru();
11425+
11426+ Unsafe(ref stru);
11427+
11428+ Console.Out.WriteLine(stru.Span);
11429+ }
11430+
11431+ private static void Unsafe<T>(ref T stru) where T : IStru, allows ref struct
11432+ {
11433+ Span<char> span = stackalloc char[10];
11434+
11435+ "bug".CopyTo(span);
11436+
11437+ stru.UseSpan(span);
11438+ }
11439+ }
11440+
11441+ internal interface IStru
11442+ {
11443+ public void UseSpan(scoped Span<char> span);
11444+ }
11445+
11446+ internal ref struct Stru : IStru
11447+ {
11448+ public Span<char> Span;
11449+
11450+ public void UseSpan(Span<char> span) => Span = span; // 1
11451+ }
11452+ """ ;
11453+
11454+ var comp = CreateCompilation ( source , targetFramework : TargetFramework . Net90 ) ;
11455+ comp . VerifyEmitDiagnostics (
11456+ // (33,17): error CS8987: The 'scoped' modifier of parameter 'span' doesn't match overridden or implemented member.
11457+ // public void UseSpan(Span<char> span) => Span = span;
11458+ Diagnostic ( ErrorCode . ERR_ScopedMismatchInParameterOfOverrideOrImplementation , "UseSpan" ) . WithArguments ( "span" ) . WithLocation ( 33 , 17 ) ) ;
11459+ }
1130311460 }
1130411461}
0 commit comments