Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ private static bool IsValidExtensionReceiverParameter(ParameterSymbol thisParam)
return false;
}

if (thisParam.RefKind is RefKind.In or RefKind.RefReadOnlyParameter && thisParam.Type.TypeKind != TypeKind.Struct)
if (thisParam.RefKind is RefKind.In or RefKind.RefReadOnlyParameter
&& (!thisParam.Type.IsValueType || thisParam.TypeWithAnnotations.TypeKind == TypeKind.TypeParameter))
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ protected sealed override void ExtensionMethodChecks(BindingDiagnosticBag diagno
{
diagnostics.Add(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, _location, Name);
}
else if (parameter0RefKind is RefKind.In or RefKind.RefReadOnlyParameter && parameter0Type.TypeKind != TypeKind.Struct)
else if (parameter0RefKind is RefKind.In or RefKind.RefReadOnlyParameter
&& (!parameter0Type.Type.IsValueType || parameter0Type.TypeKind == TypeKind.TypeParameter))
{
diagnostics.Add(ErrorCode.ERR_InExtensionMustBeValueType, _location, Name);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4909,5 +4909,87 @@ .maxstack 1
var comp = CreateCompilation(src, references: new[] { libChanged.EmitToImageReference(), libUser.EmitToImageReference() });
CompileAndVerify(comp, expectedOutput: "Report1 11");
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73746")]
public void TestExtensionMethodInModifierWithEnumType()
{
var source = """
int i = 0;
i.M1();

E e = (E)0;
e.M2();

enum E;

static class Extensions
{
public static void M1(this in int e)
{
System.Console.WriteLine("int");
}

public static void M2(this in E e)
{
System.Console.WriteLine("enum");
}
}
""";

var comp = CompileAndVerify(source, expectedOutput: """
int
enum
""");

var expectedIL = """
.class private auto ansi abstract sealed beforefieldinit Extensions
extends [netstandard]System.Object
{
.custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public hidebysig static
void M1 (
[in] int32& e
) cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
.param [1]
.custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x208f
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "int"
IL_0005: call void [netstandard]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Extensions::M1
.method public hidebysig static
void M2 (
[in] valuetype E& e
) cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
.param [1]
.custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x209b
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "enum"
IL_0005: call void [netstandard]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Extensions::M2
} // end of class Extensions
""".Replace("[netstandard]", RuntimeUtilities.IsCoreClrRuntime ? "[netstandard]" : "[mscorlib]");
comp.VerifyTypeIL("Extensions", expectedIL);
}
}
}
82 changes: 82 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefOutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -832,5 +832,87 @@ void verify(CSharpCompilationOptions options, string expectedIL)
verifier.VerifyIL("<top-level-statements-entry-point>", expectedIL);
}
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73746")]
public void TestExtensionMethodRefReadonlyModifierWithEnumType()
{
var source = """
int i = 0;
i.M1();

E e = (E)0;
e.M2();

enum E;

static class Extensions
{
public static void M1(this ref readonly int e)
{
System.Console.WriteLine("int");
}

public static void M2(this ref readonly E e)
{
System.Console.WriteLine("enum");
}
}
""";

var comp = CompileAndVerify(source, expectedOutput: """
int
enum
""");

var expectedIL = """
.class private auto ansi abstract sealed beforefieldinit Extensions
extends [netstandard]System.Object
{
.custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method public hidebysig static
void M1 (
[in] int32& e
) cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
.param [1]
.custom instance void System.Runtime.CompilerServices.RequiresLocationAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x208f
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "int"
IL_0005: call void [netstandard]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Extensions::M1
.method public hidebysig static
void M2 (
[in] valuetype E& e
) cil managed
{
.custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = (
01 00 00 00
)
.param [1]
.custom instance void System.Runtime.CompilerServices.RequiresLocationAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x209b
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "enum"
IL_0005: call void [netstandard]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Extensions::M2
} // end of class Extensions
""".Replace("[netstandard]", RuntimeUtilities.IsCoreClrRuntime ? "[netstandard]" : "[mscorlib]");
comp.VerifyTypeIL("Extensions", expectedIL);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using Basic.Reference.Assemblies;
using ICSharpCode.Decompiler.IL;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -15,7 +17,6 @@
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using Basic.Reference.Assemblies;
using Utils = Microsoft.CodeAnalysis.CSharp.UnitTests.CompilationUtils;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols
Expand Down Expand Up @@ -4160,5 +4161,87 @@ public static void M<T>(this C<T> c)
// c.M = 42;
Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "c.M").WithArguments("M", "method group").WithLocation(7, 9));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73746")]
public void TestInModifier()
{
var source = """
struct S;
enum E;
class C;
interface I;
delegate void D();

static class Extensions
{
public static void M1(this in S s) { }
public static void M2(this in E e) { }
public static void M3(this in C c) { }
public static void M4(this in I i) { }
public static void M5(this in D d) { }
public static void M6(this in S[] s) { }
public static void M7<T>(this in T t) where T : struct { }
}
""";

var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics(
// (11,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M3' must be a concrete (non-generic) value type.
// public static void M3(this in C c) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M3").WithArguments("M3").WithLocation(11, 24),
// (12,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M4' must be a concrete (non-generic) value type.
// public static void M4(this in I i) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M4").WithArguments("M4").WithLocation(12, 24),
// (13,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M5' must be a concrete (non-generic) value type.
// public static void M5(this in D d) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M5").WithArguments("M5").WithLocation(13, 24),
// (14,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M6' must be a concrete (non-generic) value type.
// public static void M6(this in S[] s) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M6").WithArguments("M6").WithLocation(14, 24),
// (15,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M7' must be a concrete (non-generic) value type.
// public static void M7<T>(this in T t) where T : struct { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M7").WithArguments("M7").WithLocation(15, 24));
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73746")]
public void TestRefReadonlyModifier()
{
var source = """
struct S;
enum E;
class C;
interface I;
delegate void D();

static class Extensions
{
public static void M1(this ref readonly S s) { }
public static void M2(this ref readonly E e) { }
public static void M3(this ref readonly C c) { }
public static void M4(this ref readonly I i) { }
public static void M5(this ref readonly D d) { }
public static void M6(this ref readonly S[] s) { }
public static void M7<T>(this ref readonly T t) where T : struct { }
}
""";

var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics(
// (11,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M3' must be a concrete (non-generic) value type.
// public static void M3(this ref readonly C c) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M3").WithArguments("M3").WithLocation(11, 24),
// (12,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M4' must be a concrete (non-generic) value type.
// public static void M4(this ref readonly I i) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M4").WithArguments("M4").WithLocation(12, 24),
// (13,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M5' must be a concrete (non-generic) value type.
// public static void M5(this ref readonly D d) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M5").WithArguments("M5").WithLocation(13, 24),
// (14,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M6' must be a concrete (non-generic) value type.
// public static void M6(this ref readonly S[] s) { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M6").WithArguments("M6").WithLocation(14, 24),
// (15,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'M7' must be a concrete (non-generic) value type.
// public static void M7<T>(this ref readonly T t) where T : struct { }
Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "M7").WithArguments("M7").WithLocation(15, 24));
}
}
}