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.