diff --git a/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs
index 9fa9e7e85..c4a6a0492 100644
--- a/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/DisassemblerPrettyTestRunner.cs
@@ -52,6 +52,12 @@ namespace ICSharpCode.Decompiler.Tests
}
}
+ [Test]
+ public async Task GenericConstraints()
+ {
+ await Run();
+ }
+
[Test]
public async Task SecurityDeclarations()
{
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 92d741c7b..5c5dce460 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -91,6 +91,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 686233381..dfc3eb8ac 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -792,7 +792,7 @@ namespace ICSharpCode.Decompiler.Tests
configureDecompiler?.Invoke(settings);
var decompiled = await Tester.DecompileCSharp(exeFile, settings).ConfigureAwait(false);
- // 3. Compile
+ // 3. Compare
CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(cscOptions).Append("EXPECTED_OUTPUT").ToArray());
Tester.RepeatOnIOError(() => File.Delete(decompiled));
}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/GenericConstraints.il b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/GenericConstraints.il
new file mode 100644
index 000000000..e6283ef08
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Disassembler/Pretty/GenericConstraints.il
@@ -0,0 +1,45 @@
+.assembly extern mscorlib
+{
+ .publickeytoken = (
+ b7 7a 5c 56 19 34 e0 89
+ )
+ .ver 4:0:0:0
+}
+.assembly GenericConstraints
+{
+ .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = (
+ 01 00 07 31 2e 30 2e 30 2e 30 00 00
+ )
+ .hash algorithm 0x00008004 // SHA1
+ .ver 1:0:0:0
+}
+
+.module GenericConstraints.dll
+.imagebase 0x10000000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003 // WindowsCui
+.corflags 0x00000001 // ILOnly
+
+.class private auto ansi ''
+{
+} // end of class
+
+.class public auto ansi beforefieldinit TestType`1
+ extends [mscorlib]System.Object
+{
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Header size: 1
+ // Code size: 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TestType::.ctor
+
+} // end of class TestType
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs
index c02845c9c..e9875da6d 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Generics.cs
@@ -293,6 +293,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
#endif
+#if NET90
+ public static void AllowsRefStruct() where T : allows ref struct
+ {
+ }
+#endif
+
public static void Issue1959(int a, int b, int? c)
{
// This line requires parentheses around `a < b` to avoid a grammar ambiguity.
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
index 119185bc5..05a62d68d 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
@@ -2465,7 +2465,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
internal Constraint ConvertTypeParameterConstraint(ITypeParameter tp)
{
- if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType))
+ if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && !tp.AllowsRefLikeType && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType))
{
return null;
}
@@ -2518,6 +2518,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
c.BaseTypes.Add(new PrimitiveType("new"));
}
+ if (tp.AllowsRefLikeType)
+ {
+ c.BaseTypes.Add(new PrimitiveType("allows ref struct"));
+ }
return c;
}
diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
index 4836276e2..81365516d 100644
--- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
+++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
@@ -28,6 +28,7 @@ using System.Threading;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Disassembler
{
@@ -1733,6 +1734,10 @@ namespace ICSharpCode.Decompiler.Disassembler
{
output.Write("valuetype ");
}
+ if ((gp.Attributes & TypeUtils.AllowByRefLike) == TypeUtils.AllowByRefLike)
+ {
+ output.Write("byreflike ");
+ }
if ((gp.Attributes & GenericParameterAttributes.DefaultConstructorConstraint) == GenericParameterAttributes.DefaultConstructorConstraint)
{
output.Write(".ctor ");
diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
index 09bed7da3..8748d227b 100644
--- a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
@@ -97,6 +97,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
///
bool HasUnmanagedConstraint { get; }
+ ///
+ /// if the allows ref struct constraint is specified for the type parameter.
+ ///
+ bool AllowsRefLikeType { get; }
+
///
/// Nullability of the reference type constraint. (e.g. "where T : class?").
///
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs
index 289fc35e7..87f6ad524 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs
@@ -175,6 +175,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public abstract bool HasReferenceTypeConstraint { get; }
public abstract bool HasValueTypeConstraint { get; }
public abstract bool HasUnmanagedConstraint { get; }
+ public abstract bool AllowsRefLikeType { get; }
public abstract Nullability NullabilityConstraint { get; }
public TypeKind Kind {
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs
index 3e4398ec3..a2c225a53 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs
@@ -71,6 +71,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override bool HasReferenceTypeConstraint => hasReferenceTypeConstraint;
public override bool HasDefaultConstructorConstraint => hasDefaultConstructorConstraint;
public override bool HasUnmanagedConstraint => false;
+ public override bool AllowsRefLikeType => false;
public override Nullability NullabilityConstraint => nullabilityConstraint;
public override IReadOnlyList TypeConstraints { get; }
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs
index 16f96c6e5..dc0e73814 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs
@@ -179,6 +179,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool ITypeParameter.HasReferenceTypeConstraint => false;
bool ITypeParameter.HasValueTypeConstraint => false;
bool ITypeParameter.HasUnmanagedConstraint => false;
+ bool ITypeParameter.AllowsRefLikeType => false;
Nullability ITypeParameter.NullabilityConstraint => Nullability.Oblivious;
IReadOnlyList ITypeParameter.TypeConstraints => EmptyList.Instance;
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs
index 282e5c2bf..8fab173a9 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs
@@ -119,6 +119,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override bool HasDefaultConstructorConstraint => (attr & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
public override bool HasReferenceTypeConstraint => (attr & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
public override bool HasValueTypeConstraint => (attr & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
+ public override bool AllowsRefLikeType => (attr & TypeUtils.AllowByRefLike) != 0;
public override bool HasUnmanagedConstraint {
get {
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
index abfd5b184..141e5fe9f 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
@@ -115,6 +115,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool ITypeParameter.HasReferenceTypeConstraint => baseType.HasReferenceTypeConstraint;
bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint;
bool ITypeParameter.HasUnmanagedConstraint => baseType.HasUnmanagedConstraint;
+ bool ITypeParameter.AllowsRefLikeType => baseType.AllowsRefLikeType;
Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint;
IReadOnlyList ITypeParameter.TypeConstraints => baseType.TypeConstraints;
SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter;
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
index e72d6eeb7..0bcf4a49c 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
@@ -278,6 +278,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override bool HasReferenceTypeConstraint => baseTp.HasReferenceTypeConstraint;
public override bool HasDefaultConstructorConstraint => baseTp.HasDefaultConstructorConstraint;
public override bool HasUnmanagedConstraint => baseTp.HasUnmanagedConstraint;
+ public override bool AllowsRefLikeType => baseTp.AllowsRefLikeType;
public override Nullability NullabilityConstraint => baseTp.NullabilityConstraint;
diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs
index d2176704a..687dcf27d 100644
--- a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs
@@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
+using System.Reflection;
+
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
@@ -25,6 +27,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
public const int NativeIntSize = 6; // between 4 (Int32) and 8 (Int64)
+ public const GenericParameterAttributes AllowByRefLike = (GenericParameterAttributes)0x0020;
+
///
/// Gets the size (in bytes) of the input type.
/// Returns NativeIntSize for pointer-sized types.