From 60dc82d88abbf551d459516d845f84323f655ded Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 17 Sep 2020 13:50:57 -0700 Subject: [PATCH] Consider NativeIntegerAttribute in CopyTypeCustomModifiers() (#47536) --- .../Metadata/PE/NativeIntegerTypeDecoder.cs | 9 +- .../Symbols/Source/CustomModifierUtils.cs | 17 +- .../Semantic/Semantics/NativeIntegerTests.cs | 196 ++++++++++++++++++ .../Symbols/InterfaceImplementationTests.cs | 9 +- 4 files changed, 215 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NativeIntegerTypeDecoder.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NativeIntegerTypeDecoder.cs index db6cbfaf1b2..7957d74b832 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NativeIntegerTypeDecoder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/NativeIntegerTypeDecoder.cs @@ -23,7 +23,7 @@ internal static TypeSymbol TransformType(TypeSymbol type, EntityHandle handle, P type; } - private static TypeSymbol TransformType(TypeSymbol type, ImmutableArray transformFlags) + internal static TypeSymbol TransformType(TypeSymbol type, ImmutableArray transformFlags) { var decoder = new NativeIntegerTypeDecoder(transformFlags); try @@ -104,7 +104,12 @@ private NamedTypeSymbol TransformNamedType(NamedTypeSymbol type) { throw new UnsupportedSignatureContent(); } - return _transformFlags[_index++] ? type.AsNativeInteger() : type; + return (_transformFlags[_index++], type.IsNativeIntegerType) switch + { + (false, true) => type.NativeIntegerUnderlyingType, + (true, false) => type.AsNativeInteger(), + _ => type, + }; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs index fd3041b6113..d2726f5204f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/CustomModifierUtils.cs @@ -74,18 +74,17 @@ internal static TypeSymbol CopyTypeCustomModifiers(TypeSymbol sourceType, TypeSy // code gen uses and then passing them to the dynamic type decoder that metadata reading uses. // NOTE: ref is irrelevant here since we are just encoding/decoding the type out of the signature context ImmutableArray flags = CSharpCompilation.DynamicTransformsEncoder.EncodeWithoutCustomModifierFlags(destinationType, refKind); - TypeSymbol typeWithDynamic = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(sourceType, containingAssembly, refKind, flags); + TypeSymbol resultType = DynamicTypeDecoder.TransformTypeWithoutCustomModifierFlags(sourceType, containingAssembly, refKind, flags); + + var builder = ArrayBuilder.GetInstance(); + CSharpCompilation.NativeIntegerTransformsEncoder.Encode(builder, destinationType); + resultType = NativeIntegerTypeDecoder.TransformType(resultType, builder.ToImmutableAndFree()); - TypeSymbol resultType; if (destinationType.ContainsTuple() && !sourceType.Equals(destinationType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreDynamic)) { // We also preserve tuple names, if present and different ImmutableArray names = CSharpCompilation.TupleNamesEncoder.Encode(destinationType); - resultType = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeWithDynamic, names); - } - else - { - resultType = typeWithDynamic; + resultType = TupleTypeDecoder.DecodeTupleTypesIfApplicable(resultType, names); } // Preserve nullable modifiers as well. @@ -100,9 +99,9 @@ internal static TypeSymbol CopyTypeCustomModifiers(TypeSymbol sourceType, TypeSy bool transformResult = resultType.ApplyNullableTransforms(defaultTransformFlag: 0, flagsBuilder.ToImmutableAndFree(), ref position, out resultType); Debug.Assert(transformResult && position == length); - Debug.Assert(resultType.Equals(sourceType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); // Same custom modifiers as source type. + Debug.Assert(resultType.Equals(sourceType, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes | TypeCompareKind.IgnoreNativeIntegers)); // Same custom modifiers as source type. - // Same object/dynamic, nullability and tuple names as destination type. + // Same object/dynamic, nullability, native integers, and tuple names as destination type. Debug.Assert(resultType.Equals(destinationType, TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds)); return resultType; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs index e3a629035ca..572834dd720 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs @@ -12237,6 +12237,202 @@ static object Execute(Func f) {(IntPtr.Size == 4 ? "System.OverflowException" : "0")}"); } + [WorkItem(42500, "https://github.com/dotnet/roslyn/issues/42500")] + [Fact] + public void ExplicitImplementationReturnTypeDifferences() + { + string source = +@"struct S +{ +} +interface I +{ + S F1(); + S F2(); + S F3(); + S F4(); +} +class C : I +{ + S I.F1() => default; + S I.F2() => default; + S I.F3() => default; + S I.F4() => default; +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyEmitDiagnostics(); + + var type = comp.GetTypeByMetadataName("I"); + Assert.Equal("S I.F1()", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("S I.F2()", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("S I.F3()", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("S I.F4()", type.GetMember("F4").ToTestDisplayString()); + + type = comp.GetTypeByMetadataName("C"); + Assert.Equal("S C.I.F1()", type.GetMember("I.F1").ToTestDisplayString()); + Assert.Equal("S C.I.F2()", type.GetMember("I.F2").ToTestDisplayString()); + Assert.Equal("S C.I.F3()", type.GetMember("I.F3").ToTestDisplayString()); + Assert.Equal("S C.I.F4()", type.GetMember("I.F4").ToTestDisplayString()); + } + + [WorkItem(42500, "https://github.com/dotnet/roslyn/issues/42500")] + [WorkItem(44358, "https://github.com/dotnet/roslyn/issues/44358")] + [Fact] + public void OverrideReturnTypeDifferences() + { + string source = +@"class A +{ + public virtual nint[] F1() => null; + public virtual System.IntPtr[] F2() => null; + public virtual nint[] F3() => null; + public virtual System.IntPtr[] F4() => null; +} +class B : A +{ + public override System.IntPtr[] F1() => null; + public override nint[] F2() => null; + public override nint[] F3() => null; + public override System.IntPtr[] F4() => null; +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyEmitDiagnostics(); + + var type = comp.GetTypeByMetadataName("A"); + Assert.Equal("nint[] A.F1()", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("System.IntPtr[] A.F2()", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("nint[] A.F3()", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("System.IntPtr[] A.F4()", type.GetMember("F4").ToTestDisplayString()); + + type = comp.GetTypeByMetadataName("B"); + Assert.Equal("System.IntPtr[] B.F1()", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("nint[] B.F2()", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("nint[] B.F3()", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("System.IntPtr[] B.F4()", type.GetMember("F4").ToTestDisplayString()); + } + + [WorkItem(42500, "https://github.com/dotnet/roslyn/issues/42500")] + [Fact] + public void OverrideParameterTypeCustomModifierDifferences() + { + var sourceA = +@".class private System.Runtime.CompilerServices.NativeIntegerAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class public A +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method public virtual void F1(native int modopt(int32) i) + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) + ret + } + .method public virtual void F2(native int modopt(int32) i) + { + ret + } + .method public virtual void F3(native int modopt(int32) i) + { + .param [1] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) + ret + } + .method public virtual void F4(native int modopt(int32) i) + { + ret + } +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class B : A +{ + public override void F1(System.IntPtr i) { } + public override void F2(nint i) { } + public override void F3(nint i) { } + public override void F4(System.IntPtr i) { } +}"; + var comp = CreateCompilation(sourceB, new[] { refA }, parseOptions: TestOptions.Regular9); + comp.VerifyEmitDiagnostics(); + + var type = comp.GetTypeByMetadataName("A"); + Assert.Equal("void A.F1(nint modopt(System.Int32) i)", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("void A.F2(System.IntPtr modopt(System.Int32) i)", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("void A.F3(nint modopt(System.Int32) i)", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("void A.F4(System.IntPtr modopt(System.Int32) i)", type.GetMember("F4").ToTestDisplayString()); + + type = comp.GetTypeByMetadataName("B"); + Assert.Equal("void B.F1(System.IntPtr modopt(System.Int32) i)", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("void B.F2(nint modopt(System.Int32) i)", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("void B.F3(nint modopt(System.Int32) i)", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("void B.F4(System.IntPtr modopt(System.Int32) i)", type.GetMember("F4").ToTestDisplayString()); + } + + [WorkItem(42500, "https://github.com/dotnet/roslyn/issues/42500")] + [Fact] + public void OverrideReturnTypeCustomModifierDifferences() + { + var sourceA = +@".class private System.Runtime.CompilerServices.NativeIntegerAttribute extends [mscorlib]System.Attribute +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } +} +.class public A +{ + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method public virtual native int[] modopt(int32) F1() + { + .param [0] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public virtual native int[] modopt(int32) F2() + { + ldnull + throw + } + .method public virtual native int[] modopt(int32) F3() + { + .param [0] + .custom instance void System.Runtime.CompilerServices.NativeIntegerAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + .method public virtual native int[] modopt(int32) F4() + { + ldnull + throw + } +}"; + var refA = CompileIL(sourceA); + + var sourceB = +@"class B : A +{ + public override System.IntPtr[] F1() => default; + public override nint[] F2() => default; + public override nint[] F3() => default; + public override System.IntPtr[] F4() => default; +}"; + var comp = CreateCompilation(sourceB, new[] { refA }, parseOptions: TestOptions.Regular9); + comp.VerifyEmitDiagnostics(); + + var type = comp.GetTypeByMetadataName("A"); + Assert.Equal("nint[] modopt(System.Int32) A.F1()", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("System.IntPtr[] modopt(System.Int32) A.F2()", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("nint[] modopt(System.Int32) A.F3()", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("System.IntPtr[] modopt(System.Int32) A.F4()", type.GetMember("F4").ToTestDisplayString()); + + type = comp.GetTypeByMetadataName("B"); + Assert.Equal("System.IntPtr[] modopt(System.Int32) B.F1()", type.GetMember("F1").ToTestDisplayString()); + Assert.Equal("nint[] modopt(System.Int32) B.F2()", type.GetMember("F2").ToTestDisplayString()); + Assert.Equal("nint[] modopt(System.Int32) B.F3()", type.GetMember("F3").ToTestDisplayString()); + Assert.Equal("System.IntPtr[] modopt(System.Int32) B.F4()", type.GetMember("F4").ToTestDisplayString()); + } + [WorkItem(44810, "https://github.com/dotnet/roslyn/issues/44810")] [Theory] [InlineData("void*")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs index 39b6430344c..5aefd7deb54 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/InterfaceImplementationTests.cs @@ -2554,11 +2554,10 @@ static void Main() [InlineData("(int X, int Y)", "(int X, int Y)", "(System.Int32 X, System.Int32 Y)", "System.ValueTuple`2[System.Int32,System.Int32]", false)] [InlineData("nint", "nint", "nint", "System.IntPtr", true)] [InlineData("nint", "nint", "nint", "System.IntPtr", false)] - // https://github.com/dotnet/roslyn/issues/42500: CopyTypeCustomModifiers() should copy NativeIntegerAttribute - //[InlineData("nint", "System.IntPtr", "System.IntPtr", "System.IntPtr", true)] - //[InlineData("nint", "System.IntPtr", "System.IntPtr", "System.IntPtr", false)] - //[InlineData("System.IntPtr", "nint", "nint", "System.IntPtr", true)] - //[InlineData("System.IntPtr", "nint", "nint", "System.IntPtr", false)] + [InlineData("nint", "System.IntPtr", "System.IntPtr", "System.IntPtr", true)] + [InlineData("nint", "System.IntPtr", "System.IntPtr", "System.IntPtr", false)] + [InlineData("System.IntPtr", "nint", "nint", "System.IntPtr", true)] + [InlineData("System.IntPtr", "nint", "nint", "System.IntPtr", false)] public void ExplicitImplementationInBaseType_02(string interfaceTypeArg, string baseTypeArg, string expectedTypeArg, string expectedOutput, bool useCompilationReference) { var source0 = -- GitLab