From 238db4f781f4d78886ed3cd9e07f79f277ba3ed1 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 7 Oct 2015 16:47:00 -0700 Subject: [PATCH] Add support for custom modifiers referencing generic types. Fixes #5725. --- .../Emitter/Model/CustomModifierAdapter.cs | 1 - .../Portable/Symbols/AbstractTypeMap.cs | 43 +++- .../CSharp/Portable/Symbols/Symbol.cs | 10 +- .../Portable/Symbols/TypeWithModifiers.cs | 7 +- .../Symbol/Symbols/CustomModifiersTests.cs | 209 +++++++++++++++++ .../MetadataReader/MetadataDecoder.cs | 203 ++++++++++------- .../Portable/Emit/CustomModifierAdapter.vb | 1 - .../Portable/Symbols/TypeSubstitution.vb | 34 +++ .../Portable/Symbols/TypeWithModifiers.vb | 7 +- .../SymbolsTests/CustomModifiersTests.vb | 215 ++++++++++++++++++ 10 files changed, 641 insertions(+), 89 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/CustomModifierAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/CustomModifierAdapter.cs index 147365a5c2c..1d8e1fb1810 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/CustomModifierAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/CustomModifierAdapter.cs @@ -15,7 +15,6 @@ bool Cci.ICustomModifier.IsOptional Cci.ITypeReference Cci.ICustomModifier.GetModifier(EmitContext context) { - Debug.Assert(this.Modifier.IsDefinition); return ((PEModuleBuilder)context.Module).Translate(this.Modifier, (CSharpSyntaxNode)context.SyntaxNodeOpt, context.Diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs b/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs index f83df279d94..e5c26ab259c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AbstractTypeMap.cs @@ -145,9 +145,50 @@ internal ImmutableArray SubstituteCustomModifiers(TypeSymbol typ return new TypeWithModifiers(type, customModifiers).SubstituteType(this).CustomModifiers; } - return customModifiers; + return SubstituteCustomModifiers(customModifiers); } + internal ImmutableArray SubstituteCustomModifiers(ImmutableArray customModifiers) + { + if (customModifiers.IsDefaultOrEmpty) + { + return customModifiers; + } + + for (int i = 0; i < customModifiers.Length; i++) + { + var modifier = (NamedTypeSymbol)customModifiers[i].Modifier; + var substituted = SubstituteNamedType(modifier); + + if (modifier != substituted) + { + var builder = ArrayBuilder.GetInstance(customModifiers.Length); + builder.AddRange(customModifiers, i); + + builder.Add(customModifiers[i].IsOptional ? CSharpCustomModifier.CreateOptional(substituted) : CSharpCustomModifier.CreateRequired(substituted)); + for (i++; i < customModifiers.Length; i++) + { + modifier = (NamedTypeSymbol)customModifiers[i].Modifier; + substituted = SubstituteNamedType(modifier); + + if (modifier != substituted) + { + builder.Add(customModifiers[i].IsOptional ? CSharpCustomModifier.CreateOptional(substituted) : CSharpCustomModifier.CreateRequired(substituted)); + } + else + { + builder.Add(customModifiers[i]); + } + } + + Debug.Assert(builder.Count == customModifiers.Length); + return builder.ToImmutableAndFree(); + } + } + + return customModifiers; + } + protected virtual TypeSymbol SubstituteDynamicType() { return DynamicTypeSymbol.Instance; diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index c073f37f7f4..fb562484cc7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -934,7 +934,15 @@ internal bool DeriveUseSiteDiagnosticFromCustomModifiers(ref DiagnosticInfo resu { foreach (CustomModifier modifier in customModifiers) { - if (DeriveUseSiteDiagnosticFromType(ref result, (TypeSymbol)modifier.Modifier)) + var modifierType = (NamedTypeSymbol)modifier.Modifier; + + // Unbound generic type is valid as a modifier, let's not report any use site diagnostics because of that. + if (modifierType.IsUnboundGenericType ) + { + modifierType = modifierType.OriginalDefinition; + } + + if (DeriveUseSiteDiagnosticFromType(ref result, modifierType)) { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithModifiers.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithModifiers.cs index b85f76dbb3b..2c414030937 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithModifiers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithModifiers.cs @@ -86,14 +86,15 @@ public TypeSymbol AsTypeSymbolOnly() public TypeWithModifiers SubstituteType(AbstractTypeMap typeMap) { + var newCustomModifiers = typeMap.SubstituteCustomModifiers(this.CustomModifiers); var newTypeWithModifiers = typeMap.SubstituteType(this.Type); - if (!newTypeWithModifiers.Is(this.Type)) + if (!newTypeWithModifiers.Is(this.Type) || newCustomModifiers != this.CustomModifiers) { - return new TypeWithModifiers(newTypeWithModifiers.Type, this.CustomModifiers.Concat(newTypeWithModifiers.CustomModifiers)); + return new TypeWithModifiers(newTypeWithModifiers.Type, newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers)); } else { - return this; // substitution had no effect on the type + return this; // substitution had no effect on the type or modifiers } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CustomModifiersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CustomModifiersTests.cs index d81e00e8fed..b3626d9633b 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CustomModifiersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CustomModifiersTests.cs @@ -1370,5 +1370,214 @@ public override void Test(int [,] c) CompileAndVerify(compilation, expectedOutput: @"Test Overriden"); } + + [ClrOnlyFact(ClrOnlyReason.Ilasm), WorkItem(5725, "https://github.com/dotnet/roslyn/issues/5725")] + public void ModifiersWithConstructedType_01() + { + var ilSource = @" +.class public auto ansi beforefieldinit CL1`1 + extends[mscorlib] System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void[mscorlib] System.Object::.ctor() + IL_0006: + ret + } // end of method CL1`1::.ctor + + .method public hidebysig newslot virtual + instance void Test(!T1 modopt(valuetype [mscorlib]System.Nullable`1) t1) cil managed + { + // Code size 1 (0x1) + .maxstack 1 + IL_0000: ldstr ""Test"" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_000a: ret + } // end of method CL1`1::Test +} // end of class CL1`1 + +.class public auto ansi beforefieldinit CL2 + extends class CL1`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void class CL1`1::.ctor() + IL_0006: ret + } // end of method CL2::.ctor +} // end of class CL2 +"; + + var source = @" +class Test +{ + static void Main() + { + var x = new CL2(); + x.Test(1); + x = new CL3(); + x.Test(1); + } +} + +class CL3 : CL2 +{ + public override void Test(int c) + { + System.Console.WriteLine(""Overriden""); + } +}"; + var compilation = CreateCompilationWithCustomILSource(source, ilSource, options: TestOptions.ReleaseExe); + + CompileAndVerify(compilation, expectedOutput: @"Test +Overriden"); + } + + [ClrOnlyFact(ClrOnlyReason.Ilasm), WorkItem(5725, "https://github.com/dotnet/roslyn/issues/5725")] + public void ModifiersWithConstructedType_02() + { + var ilSource = @" +.class public auto ansi beforefieldinit CL1`1 + extends[mscorlib] System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void[mscorlib] System.Object::.ctor() + IL_0006: + ret + } // end of method CL1`1::.ctor + + .method public hidebysig newslot virtual + instance void Test(!T1 modopt(valuetype [mscorlib]System.Nullable`1) t1) cil managed + { + // Code size 1 (0x1) + .maxstack 1 + IL_0000: ldstr ""Test"" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_000a: ret + } // end of method CL1`1::Test +} // end of class CL1`1 + +.class public auto ansi beforefieldinit CL2 + extends class CL1`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void class CL1`1::.ctor() + IL_0006: ret + } // end of method CL2::.ctor +} // end of class CL2 +"; + + var source = @" +class Test +{ + static void Main() + { + var x = new CL2(); + x.Test(1); + x = new CL3(); + x.Test(1); + } +} + +class CL3 : CL2 +{ + public override void Test(int c) + { + System.Console.WriteLine(""Overriden""); + } +}"; + var compilation = CreateCompilationWithCustomILSource(source, ilSource, options: TestOptions.ReleaseExe); + + CompileAndVerify(compilation, expectedOutput: @"Test +Overriden"); + } + + [ClrOnlyFact(ClrOnlyReason.Ilasm), WorkItem(5725, "https://github.com/dotnet/roslyn/issues/5725")] + public void ModifiersWithConstructedType_03() + { + var ilSource = @" +.class public auto ansi beforefieldinit CL1`1 + extends[mscorlib] System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void[mscorlib] System.Object::.ctor() + IL_0006: + ret + } // end of method CL1`1::.ctor + + .method public hidebysig newslot virtual + instance int32 modopt(CL2) modopt(valuetype [mscorlib]System.Nullable`1) modopt(valuetype [mscorlib]System.Nullable`1) modopt(CL2) [] Test(!T1 t1) cil managed + { + // Code size 1 (0x1) + .maxstack 1 + IL_0000: ldstr ""Test"" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_0006: ldnull + IL_000a: ret + } // end of method CL1`1::Test +} // end of class CL1`1 + +.class public auto ansi beforefieldinit CL2 + extends class CL1`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void class CL1`1::.ctor() + IL_0006: ret + } // end of method CL2::.ctor +} // end of class CL2 +"; + + var source = @" +class Test +{ + static void Main() + { + var x = new CL2(); + x.Test(1); + x = new CL3(); + x.Test(1); + } +} + +class CL3 : CL2 +{ + public override int[] Test(int c) + { + System.Console.WriteLine(""Overriden""); + return null; + } +}"; + var compilation = CreateCompilationWithCustomILSource(source, ilSource, options: TestOptions.ReleaseExe); + + CompileAndVerify(compilation, expectedOutput: @"Test +Overriden"); + } } } \ No newline at end of file diff --git a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs index aa2efd2ec62..e4f38128820 100644 --- a/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs +++ b/src/Compilers/Core/Portable/MetadataReader/MetadataDecoder.cs @@ -309,56 +309,63 @@ private TypeSymbol DecodeTypeOrThrow(ref BlobReader ppSig, SignatureTypeCode typ break; case SignatureTypeCode.GenericTypeInstance: - SignatureTypeCode elementTypeCode = ppSig.ReadSignatureTypeCode(); - if (elementTypeCode != SignatureTypeCode.TypeHandle) - { - throw new UnsupportedSignatureContent(); - } + typeSymbol = DecodeGenericTypeInstanceOrThrow(ref ppSig, out refersToNoPiaLocalType); + break; - EntityHandle tokenGeneric = ppSig.ReadTypeHandle(); - int argumentCount; - if (!ppSig.TryReadCompressedInteger(out argumentCount)) - { - throw new UnsupportedSignatureContent(); - } + default: + throw new UnsupportedSignatureContent(); + } - TypeSymbol generic = GetTypeOfToken(tokenGeneric, out refersToNoPiaLocalType); - Debug.Assert(!refersToNoPiaLocalType || generic.TypeKind == TypeKind.Error); + return typeSymbol; + } - var argumentsBuilder = ArrayBuilder>>>.GetInstance(argumentCount); - var argumentRefersToNoPiaLocalTypeBuilder = ArrayBuilder.GetInstance(argumentCount); + private TypeSymbol DecodeGenericTypeInstanceOrThrow(ref BlobReader ppSig, out bool refersToNoPiaLocalType) + { + SignatureTypeCode elementTypeCode = ppSig.ReadSignatureTypeCode(); + if (elementTypeCode != SignatureTypeCode.TypeHandle) + { + throw new UnsupportedSignatureContent(); + } - for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) - { - bool argumentRefersToNoPia; - modifiers = DecodeModifiersOrThrow(ref ppSig, out typeCode); - argumentsBuilder.Add(KeyValuePair.Create(DecodeTypeOrThrow(ref ppSig, typeCode, out argumentRefersToNoPia), modifiers)); - argumentRefersToNoPiaLocalTypeBuilder.Add(argumentRefersToNoPia); - } + EntityHandle tokenGeneric = ppSig.ReadTypeHandle(); + int argumentCount; + if (!ppSig.TryReadCompressedInteger(out argumentCount)) + { + throw new UnsupportedSignatureContent(); + } - // The instantiated type might have a generic parent, in which case some or all of the type - // arguments might actually be for the parent. + TypeSymbol generic = GetTypeOfToken(tokenGeneric, out refersToNoPiaLocalType); + Debug.Assert(!refersToNoPiaLocalType || generic.TypeKind == TypeKind.Error); - var arguments = argumentsBuilder.ToImmutableAndFree(); - var argumentRefersToNoPiaLocalType = argumentRefersToNoPiaLocalTypeBuilder.ToImmutableAndFree(); - typeSymbol = SubstituteTypeParameters(generic, arguments, argumentRefersToNoPiaLocalType); + var argumentsBuilder = ArrayBuilder>>>.GetInstance(argumentCount); + var argumentRefersToNoPiaLocalTypeBuilder = ArrayBuilder.GetInstance(argumentCount); - foreach (bool flag in argumentRefersToNoPiaLocalType) - { - if (flag) - { - refersToNoPiaLocalType = true; - break; - } - } + for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++) + { + bool argumentRefersToNoPia; + SignatureTypeCode typeCode; + ImmutableArray> modifiers = DecodeModifiersOrThrow(ref ppSig, out typeCode); + argumentsBuilder.Add(KeyValuePair.Create(DecodeTypeOrThrow(ref ppSig, typeCode, out argumentRefersToNoPia), modifiers)); + argumentRefersToNoPiaLocalTypeBuilder.Add(argumentRefersToNoPia); + } - break; + // The instantiated type might have a generic parent, in which case some or all of the type + // arguments might actually be for the parent. - default: - throw new UnsupportedSignatureContent(); + var arguments = argumentsBuilder.ToImmutableAndFree(); + var argumentRefersToNoPiaLocalType = argumentRefersToNoPiaLocalTypeBuilder.ToImmutableAndFree(); + TypeSymbol typeSymbol = SubstituteTypeParameters(generic, arguments, argumentRefersToNoPiaLocalType); + + foreach (bool flag in argumentRefersToNoPiaLocalType) + { + if (flag) + { + refersToNoPiaLocalType = true; + break; + } } - return typeSymbol; + return typeSymbol; } /// If the encoded type is invalid. @@ -653,13 +660,7 @@ private ImmutableArray> DecodeModifiersOrThrow(ref Blob if (typeCode == SignatureTypeCode.OptionalModifier) { - EntityHandle token = signatureReader.ReadTypeHandle(); - ModifierInfo modifier = new ModifierInfo(true, GetTypeOfToken(token)); - - if (!IsAcceptableModOptModifier(token, modifier.Modifier)) - { - throw new UnsupportedSignatureContent(); - } + ModifierInfo modifier = new ModifierInfo(true, DecodeModifierTypeOrThrow(ref signatureReader)); if (modifiers == null) { @@ -676,48 +677,83 @@ private ImmutableArray> DecodeModifiersOrThrow(ref Blob return modifiers?.ToImmutableAndFree() ?? default(ImmutableArray>); } - /// - /// According to ECMA spec: - /// The CMOD_OPT or CMOD_REQD is followed by a metadata token that - /// indexes a row in the TypeDef table or the TypeRef table. - /// i.e. No modopt in DecodeType (though it still works in DecodeModifier). - /// - private static bool IsAcceptableModOptModifier(EntityHandle token, TypeSymbol modifier) + private TypeSymbol DecodeModifierTypeOrThrow(ref BlobReader signatureReader) { + EntityHandle token = signatureReader.ReadTypeHandle(); + TypeSymbol type; + bool isNoPiaLocalType; + + // According to ECMA spec: + // The CMOD_OPT or CMOD_REQD is followed by a metadata token that + // indexes a row in the TypeDef table or the TypeRef table. +tryAgain: switch (token.Kind) { case HandleKind.TypeDefinition: + type = GetTypeOfTypeDef((TypeDefinitionHandle)token, out isNoPiaLocalType, isContainingType: false); + // it is valid for a modifier to refer to an unconstructed type, we need to preserve this fact + type = SubstituteWithUnboundIfGeneric(type); + break; + case HandleKind.TypeReference: - return true; + type = GetTypeOfTypeRef((TypeReferenceHandle)token, out isNoPiaLocalType); + // it is valid for a modifier to refer to an unconstructed type, we need to preserve this fact + type = SubstituteWithUnboundIfGeneric(type); + break; + case HandleKind.TypeSpecification: // Section 23.2.7 of the CLI spec specifically says that this is not allowed (see comment on method), // but, apparently, ilasm turns modopt(int32) into a TypeSpec. - if (modifier != null) + // In addition, managed C++ compiler can use constructed generic types as modifiers, for example Nullable, etc. + // We will support only cases like these even though it looks like CLR allows any types that can be encoded through a TypeSpec. + + BlobReader memoryReader = this.Module.GetTypeSpecificationSignatureReaderOrThrow((TypeSpecificationHandle)token); + + SignatureTypeCode typeCode = memoryReader.ReadSignatureTypeCode(); + bool refersToNoPiaLocalType; + + switch (typeCode) { - switch (modifier.SpecialType) - { - case SpecialType.System_Void: - case SpecialType.System_Boolean: - case SpecialType.System_SByte: - case SpecialType.System_Byte: - case SpecialType.System_Int16: - case SpecialType.System_UInt16: - case SpecialType.System_Int32: - case SpecialType.System_UInt32: - case SpecialType.System_Int64: - case SpecialType.System_UInt64: - case SpecialType.System_Single: - case SpecialType.System_Double: - case SpecialType.System_Char: - case SpecialType.System_String: - case SpecialType.System_Object: - return true; - } + case SignatureTypeCode.Void: + case SignatureTypeCode.Boolean: + case SignatureTypeCode.SByte: + case SignatureTypeCode.Byte: + case SignatureTypeCode.Int16: + case SignatureTypeCode.UInt16: + case SignatureTypeCode.Int32: + case SignatureTypeCode.UInt32: + case SignatureTypeCode.Int64: + case SignatureTypeCode.UInt64: + case SignatureTypeCode.Single: + case SignatureTypeCode.Double: + case SignatureTypeCode.Char: + case SignatureTypeCode.String: + case SignatureTypeCode.IntPtr: + case SignatureTypeCode.UIntPtr: + case SignatureTypeCode.Object: + case SignatureTypeCode.TypedReference: + type = GetSpecialType(typeCode.ToSpecialType()); + break; + + case SignatureTypeCode.TypeHandle: + + token = memoryReader.ReadTypeHandle(); + goto tryAgain; + + case SignatureTypeCode.GenericTypeInstance: + type = DecodeGenericTypeInstanceOrThrow(ref memoryReader, out refersToNoPiaLocalType); + break; + + default: + throw new UnsupportedSignatureContent(); } - return false; + break; + default: - return false; + throw new UnsupportedSignatureContent(); } + + return type; } /// If the encoded local variable type is invalid. @@ -1645,14 +1681,23 @@ protected TypeSymbol DecodeFieldSignature(ref BlobReader signatureReader, out bo if (typeCode == SignatureTypeCode.OptionalModifier || typeCode == SignatureTypeCode.RequiredModifier) { - EntityHandle token = signatureReader.ReadTypeHandle(); - ModifierInfo modifier = new ModifierInfo((typeCode == SignatureTypeCode.OptionalModifier), GetTypeOfToken(token)); + TypeSymbol type; - if (!IsAcceptableModOptModifier(token, modifier.Modifier)) + try + { + type = DecodeModifierTypeOrThrow(ref signatureReader); + } + catch (BadImageFormatException mrEx) + { + return GetUnsupportedMetadataTypeSymbol(mrEx); // an exception from metadata reader. + } + catch (UnsupportedSignatureContent) { return GetUnsupportedMetadataTypeSymbol(); // unsupported signature content } + ModifierInfo modifier = new ModifierInfo((typeCode == SignatureTypeCode.OptionalModifier), type); + if (IsVolatileModifierType(modifier.Modifier)) { isVolatile = true; diff --git a/src/Compilers/VisualBasic/Portable/Emit/CustomModifierAdapter.vb b/src/Compilers/VisualBasic/Portable/Emit/CustomModifierAdapter.vb index 7a367a1173d..53d2717e224 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/CustomModifierAdapter.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/CustomModifierAdapter.vb @@ -15,7 +15,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Property Private Function CciGetModifier(context As EmitContext) As Cci.ITypeReference Implements Cci.ICustomModifier.GetModifier - Debug.Assert(Me.Modifier Is Me.Modifier.OriginalDefinition) Return DirectCast(context.Module, PEModuleBuilder).Translate(Me.Modifier, DirectCast(context.SyntaxNodeOpt, VisualBasicSyntaxNode), context.Diagnostics) End Function End Class diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb index 7d1272888a2..8defe7b145a 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSubstitution.vb @@ -866,6 +866,40 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return New TypeWithModifiers(type, customModifiers).InternalSubstituteTypeParameters(Me).CustomModifiers End If + Return SubstituteCustomModifiers(customModifiers) + End Function + + Function SubstituteCustomModifiers(customModifiers As ImmutableArray(Of CustomModifier)) As ImmutableArray(Of CustomModifier) + + If customModifiers.IsDefaultOrEmpty Then + Return customModifiers + End If + + For i As Integer = 0 To customModifiers.Length - 1 + Dim modifier = DirectCast(customModifiers(i).Modifier, NamedTypeSymbol) + Dim substituted = DirectCast(modifier.InternalSubstituteTypeParameters(Me).AsTypeSymbolOnly(), NamedTypeSymbol) + + If modifier <> substituted Then + Dim builder = ArrayBuilder(Of CustomModifier).GetInstance(customModifiers.Length) + builder.AddRange(customModifiers, i) + builder.Add(If(customModifiers(i).IsOptional, VisualBasicCustomModifier.CreateOptional(substituted), VisualBasicCustomModifier.CreateRequired(substituted))) + + For j As Integer = i + 1 To customModifiers.Length - 1 + modifier = DirectCast(customModifiers(j).Modifier, NamedTypeSymbol) + substituted = DirectCast(modifier.InternalSubstituteTypeParameters(Me).AsTypeSymbolOnly(), NamedTypeSymbol) + + If modifier <> substituted Then + builder.Add(If(customModifiers(j).IsOptional, VisualBasicCustomModifier.CreateOptional(substituted), VisualBasicCustomModifier.CreateRequired(substituted))) + Else + builder.Add(customModifiers(j)) + End If + Next + + Debug.Assert(builder.Count = customModifiers.Length) + Return builder.ToImmutableAndFree() + End If + Next + Return customModifiers End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeWithModifiers.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeWithModifiers.vb index 7f616c192bc..ddba47e3c2a 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeWithModifiers.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeWithModifiers.vb @@ -66,11 +66,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Function Function InternalSubstituteTypeParameters(substitution As TypeSubstitution) As TypeWithModifiers + Dim newCustomModifiers = If(substitution IsNot Nothing, substitution.SubstituteCustomModifiers(Me.CustomModifiers), Me.CustomModifiers) Dim newTypeWithModifiers As TypeWithModifiers = Me.Type.InternalSubstituteTypeParameters(substitution) - If Not newTypeWithModifiers.Is(Me.Type) Then - Return New TypeWithModifiers(newTypeWithModifiers.Type, Me.CustomModifiers.Concat(newTypeWithModifiers.CustomModifiers)) + If Not newTypeWithModifiers.Is(Me.Type) OrElse newCustomModifiers <> Me.CustomModifiers Then + Return New TypeWithModifiers(newTypeWithModifiers.Type, newCustomModifiers.Concat(newTypeWithModifiers.CustomModifiers)) Else - Return Me ' substitution had no effect on the type + Return Me ' substitution had no effect on the type or modifiers End If End Function End Structure diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CustomModifiersTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CustomModifiersTests.vb index 5f12fc54bfb..191956eb968 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CustomModifiersTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/CustomModifiersTests.vb @@ -1318,5 +1318,220 @@ End Class Overriden") End Sub + + Public Sub ModifiersWithConstructedType_01() + Dim ilSource = + extends[mscorlib] System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void[mscorlib] System.Object::.ctor() + IL_0006: + ret + } // end of method CL1`1::.ctor + + .method public hidebysig newslot virtual + instance void Test(!T1 modopt(valuetype [mscorlib]System.Nullable`1) t1) cil managed + { + // Code size 1 (0x1) + .maxstack 1 + IL_0000: ldstr "Test" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_000a: ret + } // end of method CL1`1::Test +} // end of class CL1`1 + +.class public auto ansi beforefieldinit CL2 + extends class CL1`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void class CL1`1::.ctor() + IL_0006: ret + } // end of method CL2::.ctor +} // end of class CL2 +]]>.Value + Dim vbSource = + + + + + + Dim compilation = CreateCompilationWithCustomILSource(vbSource, ilSource, options:=TestOptions.ReleaseExe) + + CompileAndVerify(compilation, expectedOutput:="Test +Overriden") + End Sub + + + Public Sub ModifiersWithConstructedType_02() + Dim ilSource = + extends[mscorlib] System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void[mscorlib] System.Object::.ctor() + IL_0006: + ret + } // end of method CL1`1::.ctor + + .method public hidebysig newslot virtual + instance void Test(!T1 modopt(valuetype [mscorlib]System.Nullable`1) t1) cil managed + { + // Code size 1 (0x1) + .maxstack 1 + IL_0000: ldstr "Test" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_000a: ret + } // end of method CL1`1::Test +} // end of class CL1`1 + +.class public auto ansi beforefieldinit CL2 + extends class CL1`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void class CL1`1::.ctor() + IL_0006: ret + } // end of method CL2::.ctor +} // end of class CL2 +]]>.Value + Dim vbSource = + + + + + + Dim compilation = CreateCompilationWithCustomILSource(vbSource, ilSource, options:=TestOptions.ReleaseExe) + + CompileAndVerify(compilation, expectedOutput:="Test +Overriden") + End Sub + + + Public Sub ModifiersWithConstructedType_03() + Dim ilSource = + extends[mscorlib] System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void[mscorlib] System.Object::.ctor() + IL_0006: + ret + } // end of method CL1`1::.ctor + + .method public hidebysig newslot virtual + instance int32 modopt(CL2) modopt(valuetype [mscorlib]System.Nullable`1) modopt(valuetype [mscorlib]System.Nullable`1) modopt(CL2) [] Test(!T1 t1) cil managed + { + // Code size 1 (0x1) + .maxstack 1 + IL_0000: ldstr "Test" + IL_0005: call void [mscorlib]System.Console::WriteLine(string) + IL_0006: ldnull + IL_000a: ret + } // end of method CL1`1::Test +} // end of class CL1`1 + +.class public auto ansi beforefieldinit CL2 + extends class CL1`1 +{ + .method public hidebysig specialname rtspecialname + instance void .ctor() cil managed + { + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call instance void class CL1`1::.ctor() + IL_0006: ret + } // end of method CL2::.ctor +} // end of class CL2 +]]>.Value + Dim vbSource = + + + + + + Dim compilation = CreateCompilationWithCustomILSource(vbSource, ilSource, options:=TestOptions.ReleaseExe) + + CompileAndVerify(compilation, expectedOutput:="Test +Overriden") + End Sub + End Class End Namespace -- GitLab