diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index ae55575572c64..a3bc6a7fc4837 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -613,25 +614,8 @@ private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentInd if (haveExtensions) { - var seenGroupingTypes = PooledHashSet.GetInstance(); var groupingTypes = ArrayBuilder.GetInstance(); - - foreach (var type in symbol.GetTypeMembers("")) - { - if (!type.IsExtension) - { - continue; - } - - var groupingType = ((PENamedTypeSymbol)type).ExtensionGroupingType; - if (seenGroupingTypes.Add(groupingType)) - { - groupingTypes.Add(groupingType); - } - } - - seenGroupingTypes.Free(); - groupingTypes.Sort((x, y) => x.MetadataToken.CompareTo(y.MetadataToken)); + GetNestedExtensionGroupingTypes((PENamedTypeSymbol)symbol, groupingTypes); Debug.Assert(!groupingTypes.IsEmpty); foreach (var groupingType in groupingTypes) @@ -643,17 +627,40 @@ private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentInd } } - public sealed override ImmutableArray GetExportedTypes(DiagnosticBag diagnostics) + private static ArrayBuilder GetNestedExtensionGroupingTypes(PENamedTypeSymbol symbol, ArrayBuilder groupingTypes) + { + var seenGroupingTypes = PooledHashSet.GetInstance(); + + foreach (var type in symbol.GetTypeMembers("")) + { + if (!type.IsExtension) + { + continue; + } + + var groupingType = ((PENamedTypeSymbol)type).ExtensionGroupingType; + if (seenGroupingTypes.Add(groupingType)) + { + groupingTypes.Add(groupingType); + } + } + + seenGroupingTypes.Free(); + groupingTypes.Sort((x, y) => x.MetadataToken.CompareTo(y.MetadataToken)); + return groupingTypes; + } + + public sealed override ImmutableArray GetExportedTypes(EmitContext context) { Debug.Assert(HaveDeterminedTopLevelTypes); if (_lazyExportedTypes.IsDefault) { - var initialized = ImmutableInterlocked.InterlockedInitialize(ref _lazyExportedTypes, CalculateExportedTypes()); + var initialized = ImmutableInterlocked.InterlockedInitialize(ref _lazyExportedTypes, CalculateExportedTypes(context)); if (initialized && _lazyExportedTypes.Length > 0) { - ReportExportedTypeNameCollisions(_lazyExportedTypes, diagnostics); + ReportExportedTypeNameCollisions(_lazyExportedTypes, context.Diagnostics); } } @@ -664,7 +671,7 @@ private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentInd /// Builds an array of public type symbols defined in netmodules included in the compilation /// and type forwarders defined in this compilation or any included netmodule (in this order). /// - private ImmutableArray CalculateExportedTypes() + private ImmutableArray CalculateExportedTypes(EmitContext context) { SourceAssemblySymbol sourceAssembly = SourceModule.ContainingSourceAssembly; var builder = ArrayBuilder.GetInstance(); @@ -679,7 +686,7 @@ private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentInd } Debug.Assert(OutputKind.IsNetModule() == sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule()); - GetForwardedTypes(sourceAssembly, builder); + GetForwardedTypes(sourceAssembly, builder, context); return builder.ToImmutableAndFree(); } @@ -688,14 +695,14 @@ private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentInd /// /// Returns a set of top-level forwarded types /// - internal static HashSet GetForwardedTypes(SourceAssemblySymbol sourceAssembly, ArrayBuilder? builder) + internal static HashSet GetForwardedTypes(SourceAssemblySymbol sourceAssembly, ArrayBuilder? builder, EmitContext? context) { var seenTopLevelForwardedTypes = new HashSet(); - GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetSourceDecodedWellKnownAttributeData(), builder); + GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetSourceDecodedWellKnownAttributeData(), builder, context); if (!sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule()) { - GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetNetModuleDecodedWellKnownAttributeData(), builder); + GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetNetModuleDecodedWellKnownAttributeData(), builder, context); } return seenTopLevelForwardedTypes; @@ -709,15 +716,20 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e foreach (var exportedType in exportedTypes) { - var type = (NamedTypeSymbol)exportedType.Type.GetInternalSymbol(); + Debug.Assert(exportedType.Type.AsGenericTypeInstanceReference is null); + Debug.Assert(exportedType.Type.AsSpecializedNestedTypeReference is null); - Debug.Assert(type.IsDefinition); - - if (!type.IsTopLevelType()) + if (exportedType.Type.AsNestedTypeReference is not null) { continue; } + // Other types are expected to be top-level types backed by regular C# type symbols. + var type = (NamedTypeSymbol)exportedType.Type.GetInternalSymbol(); + + Debug.Assert(type.IsDefinition); + Debug.Assert(type.IsTopLevelType()); + // exported types are not emitted in EnC deltas (hence generation 0): string fullEmittedName = MetadataHelpers.BuildQualifiedName( ((Cci.INamespaceTypeReference)type.GetCciAdapter()).NamespaceName, @@ -772,8 +784,11 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e private static void GetForwardedTypes( HashSet seenTopLevelTypes, CommonAssemblyWellKnownAttributeData wellKnownAttributeData, - ArrayBuilder? builder) + ArrayBuilder? builder, + EmitContext? contextOpt) { + Debug.Assert(builder is null || contextOpt is not null); + if (wellKnownAttributeData?.ForwardedTypes?.Count > 0) { // (type, index of the parent exported type in builder, or -1 if the type is a top-level type) @@ -798,6 +813,9 @@ private static void GetForwardedTypes( if (builder is object) { + Debug.Assert(contextOpt is not null); + var context = contextOpt.GetValueOrDefault(); + // Return all nested types. // Note the order: depth first, children in reverse order (to match dev10, not a requirement). Debug.Assert(stack.Count == 0); @@ -805,38 +823,126 @@ private static void GetForwardedTypes( while (stack.Count > 0) { - var (type, parentIndex) = stack.Pop(); + processTopItemFromStack(stack, context, builder); + } + } + } + + stack.Free(); + } + + static void processTopItemFromStack(ArrayBuilder<(NamedTypeSymbol type, int parentIndex)> stack, EmitContext context, ArrayBuilder builder) + { + var (type, parentIndex) = stack.Pop(); + + Debug.Assert(type is { ContainingModule: SourceModuleSymbol } or PENamedTypeSymbol or RetargetingNamedTypeSymbol); + + // In general, we don't want private types to appear in the ExportedTypes table. + // BREAK: dev11 emits these types. The problem was discovered in dev10, but failed + // to meet the bar Bug: Dev10/258038 and was left as-is. + if (type.DeclaredAccessibility == Accessibility.Private) + { + // NOTE: this will also exclude nested types of type + return; + } + + // NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection. + + int index = builder.Count; + builder.Add(new Cci.ExportedType(type.GetCciAdapter(), parentIndex, isForwarder: true)); + + ImmutableArray nested = type.GetTypeMembers(); // Ordered. - // In general, we don't want private types to appear in the ExportedTypes table. - // BREAK: dev11 emits these types. The problem was discovered in dev10, but failed - // to meet the bar Bug: Dev10/258038 and was left as-is. - if (type.DeclaredAccessibility == Accessibility.Private) + if (nested.Any(n => n.IsExtension)) + { + switch (type) + { + case PENamedTypeSymbol peType: { - // NOTE: this will also exclude nested types of type - continue; - } + var groupingTypes = ArrayBuilder.GetInstance(); + GetNestedExtensionGroupingTypes(peType, groupingTypes); + Debug.Assert(!groupingTypes.IsEmpty); - // NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection. + // Iterate backwards so they get popped in forward order. + for (int i = groupingTypes.Count - 1; i >= 0; i--) + { + stack.Push((groupingTypes[i], index)); + } + + groupingTypes.Free(); - int index = builder.Count; - builder.Add(new Cci.ExportedType(type.GetCciAdapter(), parentIndex, isForwarder: true)); + pushNestedTypes(stack, index, nested); + } + break; - // Iterate backwards so they get popped in forward order. - ImmutableArray nested = type.GetTypeMembers(); // Ordered. - for (int i = nested.Length - 1; i >= 0; i--) + case SourceMemberContainerTypeSymbol sourceType: { - if (nested[i].IsExtension) + pushAndProcessNestedTypes(stack, context, index, nested, builder); + + foreach (var groupingType in sourceType.GetExtensionGroupingInfo().GetGroupingTypes()) { - continue; // https://github.com/dotnet/roslyn/issues/78963 - This is a temporary handling, we should get grouping and marker types processed instead. + int groupingIndex = builder.Count; + builder.Add(new Cci.ExportedType(groupingType, index, isForwarder: true)); + + foreach (var markerType in groupingType.GetNestedTypes(context)) + { + builder.Add(new Cci.ExportedType(markerType, groupingIndex, isForwarder: true)); + } } + } + break; + + case RetargetingNamedTypeSymbol retargetingType: + { + pushAndProcessNestedTypes(stack, context, index, nested, builder); + + foreach ((Cci.INestedTypeReference GroupingType, ImmutableArray MarkerTypes) item in retargetingType.GetExtensionGroupingAndMarkerTypesForTypeForwarding(context)) + { + int groupingIndex = builder.Count; + builder.Add(new Cci.ExportedType(item.GroupingType, index, isForwarder: true)); - stack.Push((nested[i], index)); + foreach (var markerType in item.MarkerTypes) + { + builder.Add(new Cci.ExportedType(markerType, groupingIndex, isForwarder: true)); + } + } } - } + break; + + default: + throw ExceptionUtilities.UnexpectedValue(type); } } + else + { + pushNestedTypes(stack, index, nested); + } + } - stack.Free(); + static void pushNestedTypes(ArrayBuilder<(NamedTypeSymbol type, int parentIndex)> stack, int index, ImmutableArray nested) + { + // Iterate backwards so they get popped in forward order. + for (int i = nested.Length - 1; i >= 0; i--) + { + if (nested[i].IsExtension) + { + continue; // Extension blocks are handled separately + } + + stack.Push((nested[i], index)); + } + } + + static void pushAndProcessNestedTypes(ArrayBuilder<(NamedTypeSymbol type, int parentIndex)> stack, EmitContext context, int index, ImmutableArray nested, ArrayBuilder builder) + { + int currentStackSize = stack.Count; + + pushNestedTypes(stack, index, nested); + + while (stack.Count > currentStackSize) + { + processTopItemFromStack(stack, context, builder); + } } } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs index 97c9290ffe443..e7aed58d9c2a7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs @@ -8,9 +8,14 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Threading; +using Microsoft.Cci; using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting @@ -40,6 +45,7 @@ internal sealed class RetargetingNamedTypeSymbol : WrappedNamedTypeSymbol private CachedUseSiteInfo _lazyCachedUseSiteInfo = CachedUseSiteInfo.Uninitialized; private StrongBox _lazyExtensionParameter; + private ImmutableArray<(Cci.INestedTypeReference GroupingType, ImmutableArray MarkerTypes)> _lazyExtensionGroupingAndMarkerTypesForTypeForwarding; public RetargetingNamedTypeSymbol(RetargetingModuleSymbol retargetingModule, NamedTypeSymbol underlyingType, TupleExtraData tupleData = null) : base(underlyingType, tupleData) @@ -477,5 +483,104 @@ internal override string? ExtensionGroupingName internal override string? ExtensionMarkerName => _underlyingType.ExtensionMarkerName; + + internal ImmutableArray<(Cci.INestedTypeReference GroupingType, ImmutableArray MarkerTypes)> GetExtensionGroupingAndMarkerTypesForTypeForwarding(EmitContext context) + { + if (_lazyExtensionGroupingAndMarkerTypesForTypeForwarding.IsDefault) + { + var builder = ArrayBuilder<(Cci.INestedTypeReference GroupingType, ImmutableArray MarkerTypes)>.GetInstance(); + var markerTypes = ArrayBuilder.GetInstance(); + + ImmutableArray groupingTypes = ((SourceMemberContainerTypeSymbol)_underlyingType).GetExtensionGroupingInfo().GetGroupingTypes(); + + foreach (var groupingType in groupingTypes) + { + var retargetedGroupingType = new ForwardedExtensionGroupingOrMarkerType(this.GetCciAdapter(), groupingType); + markerTypes.Clear(); + + foreach (var markerType in groupingType.GetNestedTypes(context)) + { + markerTypes.Add(new ForwardedExtensionGroupingOrMarkerType(retargetedGroupingType, markerType)); + } + + builder.Add((retargetedGroupingType, markerTypes.ToImmutable())); + } + + markerTypes.Free(); + + ImmutableInterlocked.InterlockedInitialize(ref _lazyExtensionGroupingAndMarkerTypesForTypeForwarding, builder.ToImmutableAndFree()); + } + + return _lazyExtensionGroupingAndMarkerTypesForTypeForwarding; + } + + /// + /// Used only to point to forwarded types and implements only API surface required to emit information about them. + /// + private sealed class ForwardedExtensionGroupingOrMarkerType : Cci.INestedTypeReference + { + private readonly Cci.ITypeReference _containingType; + private readonly Cci.INestedTypeReference _underlying; + + public ForwardedExtensionGroupingOrMarkerType(Cci.ITypeReference containingType, Cci.INestedTypeReference underlying) + { + _containingType = containingType; + _underlying = underlying; + } + + bool INestedTypeReference.InheritsEnclosingTypeTypeParameters => throw ExceptionUtilities.Unreachable(); + + ushort INamedTypeReference.GenericParameterCount => _underlying.GenericParameterCount; + + bool INamedTypeReference.MangleName => _underlying.MangleName; + + string? INamedTypeReference.AssociatedFileIdentifier => _underlying.AssociatedFileIdentifier; + + bool ITypeReference.IsEnum => throw ExceptionUtilities.Unreachable(); + + bool ITypeReference.IsValueType => throw ExceptionUtilities.Unreachable(); + + Cci.PrimitiveTypeCode ITypeReference.TypeCode => throw ExceptionUtilities.Unreachable(); + + TypeDefinitionHandle ITypeReference.TypeDef => throw ExceptionUtilities.Unreachable(); + + IGenericMethodParameterReference? ITypeReference.AsGenericMethodParameterReference => null; + + IGenericTypeInstanceReference? ITypeReference.AsGenericTypeInstanceReference => null; + + IGenericTypeParameterReference? ITypeReference.AsGenericTypeParameterReference => null; + + INamespaceTypeReference? ITypeReference.AsNamespaceTypeReference => null; + + INestedTypeReference? ITypeReference.AsNestedTypeReference => this; + + ISpecializedNestedTypeReference? ITypeReference.AsSpecializedNestedTypeReference => null; + + string? INamedEntity.Name => _underlying.Name; + + IDefinition? IReference.AsDefinition(EmitContext context) => null; + + INamespaceTypeDefinition? ITypeReference.AsNamespaceTypeDefinition(EmitContext context) => null; + + INestedTypeDefinition? ITypeReference.AsNestedTypeDefinition(EmitContext context) => null; + + ITypeDefinition? ITypeReference.AsTypeDefinition(EmitContext context) => null; + + void IReference.Dispatch(MetadataVisitor visitor) + { + throw ExceptionUtilities.Unreachable(); + } + + IEnumerable IReference.GetAttributes(EmitContext context) + { + throw ExceptionUtilities.Unreachable(); + } + + ITypeReference ITypeMemberReference.GetContainingType(EmitContext context) => _containingType; + + ISymbolInternal? IReference.GetInternalSymbol() => null; + + ITypeDefinition? ITypeReference.GetResolvedType(EmitContext context) => null; + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index 629b285bde5b9..c330ede461249 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -2937,7 +2937,7 @@ internal sealed override ObsoleteAttributeData? ObsoleteAttributeData internal override IEnumerable GetAllTopLevelForwardedTypes() { - return PEModuleBuilder.GetForwardedTypes(this, builder: null); + return PEModuleBuilder.GetForwardedTypes(this, builder: null, context: null); } public override AssemblyMetadata GetMetadata() => null; diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 4f7a84d35e189..a8404efc33685 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -29160,7 +29160,7 @@ public class C3; ".$34505F560D9EACF86A87F3ED1F85E448 0x27000001 (ExportedType) 0x0002", ".$69A44968D4F2B90D6BA4472A51F540A4 0x27000007 (ExportedType) 0x0002", ], actual); - }); + }).VerifyDiagnostics(); } [Fact] @@ -29200,7 +29200,7 @@ public class C3; ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0002", ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000004 (ExportedType) 0x0002", ], actual); - }); + }).VerifyDiagnostics(); } [Fact] @@ -29326,6 +29326,567 @@ public class C3; ); } + [Fact] + public void ExportedTypes_06() + { + string source = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var moduleComp = CreateCompilation(source, options: TestOptions.ReleaseModule); + var moduleRef = moduleComp.EmitToImageReference(); + + CompileAndVerify("", references: [moduleRef], assemblyValidator: (assembly) => + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x26000001 (AssemblyFile) 0x0001", + ".$B8D310208B4544F25EEBACB9990FC73B`1 0x27000001 (ExportedType) 0x0002", + ".$B39C5C386A2E3E9242B293D9323EC48A 0x27000002 (ExportedType) 0x0002", + ], actual); + }).VerifyDiagnostics(); + } + + [Fact] + public void ForwardedTypes_01() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } + + extension(string s) + { + } + + extension(ref int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll); + + var source2 = "[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C1))]"; + + CompileAndVerify(source2, references: [comp1.EmitToImageReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + CompileAndVerify(source2, references: [comp1.ToMetadataReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + CompileAndVerify(source2, references: [comp1.ToMetadataReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + static void validateAssembly(PEAssembly assembly) + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x23000002 (AssemblyReference) 0x200000", + ".C2 0x27000001 (ExportedType) 0x0000", + ".C3 0x27000002 (ExportedType) 0x0000", + ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0000", + ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000004 (ExportedType) 0x0000", + ".$56B5C634B2E52051C75D91F71BA8833A 0x27000004 (ExportedType) 0x0000", + ".$34505F560D9EACF86A87F3ED1F85E448 0x27000001 (ExportedType) 0x0000", + ".$69A44968D4F2B90D6BA4472A51F540A4 0x27000007 (ExportedType) 0x0000", + ], actual); + } + } + + [Fact] + public void ForwardedTypes_02() + { + string source1 = @" +namespace NS1.NS2; + +public static class C1 +{ + extension(int i) + { + } + + extension(string s) + { + } + + extension(ref int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll); + + var source2 = "[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(NS1.NS2.C1))]"; + + CompileAndVerify(source2, references: [comp1.EmitToImageReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + CompileAndVerify(source2, references: [comp1.ToMetadataReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + CompileAndVerify(source2, references: [comp1.ToMetadataReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + static void validateAssembly(PEAssembly assembly) + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + "NS1.NS2.C1 0x23000002 (AssemblyReference) 0x200000", + ".C2 0x27000001 (ExportedType) 0x0000", + ".C3 0x27000002 (ExportedType) 0x0000", + ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0000", + ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000004 (ExportedType) 0x0000", + ".$56B5C634B2E52051C75D91F71BA8833A 0x27000004 (ExportedType) 0x0000", + ".$34505F560D9EACF86A87F3ED1F85E448 0x27000001 (ExportedType) 0x0000", + ".$69A44968D4F2B90D6BA4472A51F540A4 0x27000007 (ExportedType) 0x0000", + ], actual); + } + } + + [Fact] + public void ForwardedTypes_03() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll); + + var source2 = "[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C1))]"; + + CompileAndVerify(source2, references: [comp1.EmitToImageReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + CompileAndVerify(source2, references: [comp1.ToMetadataReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + CompileAndVerify(source2, references: [comp1.ToMetadataReference()], assemblyValidator: validateAssembly).VerifyDiagnostics(); + + static void validateAssembly(PEAssembly assembly) + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x23000002 (AssemblyReference) 0x200000", + ".$B8D310208B4544F25EEBACB9990FC73B`1 0x27000001 (ExportedType) 0x0000", + ".$B39C5C386A2E3E9242B293D9323EC48A 0x27000002 (ExportedType) 0x0000", + ], actual); + } + } + + [Fact] + public void ForwardedTypes_04() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll); + + var source2 = @" +extern alias A; +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(A::C1))] + +static class C1 +{ + extension(int i) + { + } +} +"; + + var comp = CreateCompilation(source2, references: [comp1.EmitToImageReference().WithAliases(["A"])]); + comp.VerifyEmitDiagnostics( + // error CS8006: Forwarded type 'C1' conflicts with type declared in primary module of this assembly. + Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithDeclaration).WithArguments("C1").WithLocation(1, 1) + ); + + comp = CreateCompilation(source2, references: [comp1.ToMetadataReference().WithAliases(["A"])]); + comp.VerifyEmitDiagnostics( + // error CS8006: Forwarded type 'C1' conflicts with type declared in primary module of this assembly. + Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithDeclaration).WithArguments("C1").WithLocation(1, 1) + ); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + comp = CreateCompilation(source2, references: [comp1.ToMetadataReference().WithAliases(["A"])]); + comp.VerifyEmitDiagnostics( + // error CS8006: Forwarded type 'C1' conflicts with type declared in primary module of this assembly. + Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithDeclaration).WithArguments("C1").WithLocation(1, 1) + ); + } + + [Fact] + public void ForwardedTypes_05() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } + + extension(string s) + { + } + + extension(ref int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll); + var comp1ImageRef = comp1.EmitToImageReference(); + + var source2 = "[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C1))]"; + + var comp2 = CreateCompilation(source2, options: TestOptions.DebugModule, references: [comp1ImageRef]); + var comp2ImageRef = comp2.EmitToImageReference(); + + CompileAndVerify("", references: [comp1ImageRef, comp2ImageRef], assemblyValidator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + CompileAndVerify("", references: [comp1.ToMetadataReference(), comp2ImageRef], assemblyValidator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + CompileAndVerify("", references: [comp1.ToMetadataReference(), comp2ImageRef], assemblyValidator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + static void validateAssembly(PEAssembly assembly) + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x23000002 (AssemblyReference) 0x200000", + ".C2 0x27000001 (ExportedType) 0x0000", + ".C3 0x27000002 (ExportedType) 0x0000", + ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0000", + ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000004 (ExportedType) 0x0000", + ".$56B5C634B2E52051C75D91F71BA8833A 0x27000004 (ExportedType) 0x0000", + ".$34505F560D9EACF86A87F3ED1F85E448 0x27000001 (ExportedType) 0x0000", + ".$69A44968D4F2B90D6BA4472A51F540A4 0x27000007 (ExportedType) 0x0000", + ], actual); + } + } + + [Fact] + public void ForwardedTypes_06() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } + + extension(string s) + { + } + + extension(ref int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions"); + var comp1ImageRef = comp1.EmitToImageReference(); + + var source2 = "[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C1))]"; + + var comp2 = CreateCompilation(source2, options: TestOptions.DebugModule, references: [comp1ImageRef]); + var comp2ImageRef = comp2.EmitToImageReference(); + + CreateCompilation("", references: [comp2ImageRef]).VerifyEmitDiagnostics( + // error CS0012: The type 'C1' is defined in an assembly that is not referenced. You must add a reference to assembly 'Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_NoTypeDef).WithArguments("C1", "Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + + string source3 = @" +public static class C1 +{ +} +"; + comp1 = CreateCompilation(source3, options: TestOptions.ReleaseDll, assemblyName: "Extensions"); + + CompileAndVerify("", references: [comp1.ToMetadataReference(), comp2ImageRef], assemblyValidator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + static void validateAssembly(PEAssembly assembly) + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x23000002 (AssemblyReference) 0x200000", + ], actual); + } + } + + [Fact] + public void ForwardedTypes_07() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions"); + + var module = CreateCompilation(source1, options: TestOptions.ReleaseModule, assemblyName: "Module"); + var moduleRef = module.EmitToImageReference(); + + var source2 = @" +extern alias A; +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(A::C1))] +"; + + var comp = CreateCompilation(source2, references: [comp1.EmitToImageReference().WithAliases(["A"]), moduleRef]); + comp.VerifyEmitDiagnostics( + // error CS8008: Type 'C1' forwarded to assembly 'Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' exported from module 'Module.netmodule'. + Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithExportedType).WithArguments("C1", "Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Module.netmodule").WithLocation(1, 1) + ); + + comp = CreateCompilation(source2, references: [comp1.ToMetadataReference().WithAliases(["A"]), moduleRef]); + comp.VerifyEmitDiagnostics( + // error CS8008: Type 'C1' forwarded to assembly 'Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' exported from module 'Module.netmodule'. + Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithExportedType).WithArguments("C1", "Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Module.netmodule").WithLocation(1, 1) + ); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + comp = CreateCompilation(source2, references: [comp1.ToMetadataReference().WithAliases(["A"]), moduleRef]); + comp.VerifyEmitDiagnostics( + // error CS8008: Type 'C1' forwarded to assembly 'Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' exported from module 'Module.netmodule'. + Diagnostic(ErrorCode.ERR_ForwardedTypeConflictsWithExportedType).WithArguments("C1", "Extensions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Module.netmodule").WithLocation(1, 1) + ); + } + + [Fact] + public void ForwardedTypes_08() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions1"); + var comp2 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions2"); + + var source2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C1))] +"; + + var module1 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseModule, assemblyName: "Module1"); + var module1Ref = module1.EmitToImageReference(); + + var module2 = CreateCompilation(source2, references: [comp2.ToMetadataReference()], options: TestOptions.ReleaseModule, assemblyName: "Module2"); + var module2Ref = module2.EmitToImageReference(); + + var comp = CreateCompilation("", references: [comp1.EmitToImageReference(), comp2.EmitToImageReference(), module1Ref, module2Ref]); + comp.VerifyEmitDiagnostics( + // error CS8007: Type 'C1' forwarded to assembly 'Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' forwarded to assembly 'Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_ForwardedTypesConflict).WithArguments("C1", "Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + + comp = CreateCompilation("", references: [comp1.ToMetadataReference(), comp2.ToMetadataReference(), module1Ref, module2Ref]); + comp.VerifyEmitDiagnostics( + // error CS8007: Type 'C1' forwarded to assembly 'Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' forwarded to assembly 'Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_ForwardedTypesConflict).WithArguments("C1", "Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + comp2 = comp2.AddReferences(comp3Ref); + + comp = CreateCompilation("", references: [comp1.ToMetadataReference(), comp2.ToMetadataReference(), module1Ref, module2Ref]); + comp.VerifyEmitDiagnostics( + // error CS8007: Type 'C1' forwarded to assembly 'Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' forwarded to assembly 'Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_ForwardedTypesConflict).WithArguments("C1", "Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + } + + [Fact] + public void ForwardedTypes_09() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions1"); + var comp2 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions2"); + + var source2 = @" +extern alias A; +extern alias B; +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(A::C1))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(B::C1))] +"; + + var comp = CreateCompilation(source2, references: [comp1.EmitToImageReference().WithAliases(["A"]), comp2.EmitToImageReference().WithAliases(["B"])]); + comp.VerifyEmitDiagnostics( + // error CS8007: Type 'C1' forwarded to assembly 'Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' forwarded to assembly 'Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_ForwardedTypesConflict).WithArguments("C1", "Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + + comp = CreateCompilation(source2, references: [comp1.ToMetadataReference().WithAliases(["A"]), comp2.ToMetadataReference().WithAliases(["B"])]); + comp.VerifyEmitDiagnostics( + // error CS8007: Type 'C1' forwarded to assembly 'Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' forwarded to assembly 'Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_ForwardedTypesConflict).WithArguments("C1", "Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + comp2 = comp2.AddReferences(comp3Ref); + + comp = CreateCompilation(source2, references: [comp1.ToMetadataReference().WithAliases(["A"]), comp2.ToMetadataReference().WithAliases(["B"])]); + comp.VerifyEmitDiagnostics( + // error CS8007: Type 'C1' forwarded to assembly 'Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' conflicts with type 'C1' forwarded to assembly 'Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + Diagnostic(ErrorCode.ERR_ForwardedTypesConflict).WithArguments("C1", "Extensions2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", "C1", "Extensions1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1) + ); + } + + [Fact] + public void ForwardedTypes_10() + { + string source1 = @" +public static class C1 +{ + extension(int i) + { + } +} +"; + var comp1 = CreateCompilation(source1, options: TestOptions.ReleaseDll, assemblyName: "Extensions1"); + + var source2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(C1))] +"; + + var module1 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseModule, assemblyName: "Module1"); + var module1Ref = module1.EmitToImageReference(); + + var module2 = CreateCompilation(source2, references: [comp1.ToMetadataReference()], options: TestOptions.ReleaseModule, assemblyName: "Module2"); + var module2Ref = module2.EmitToImageReference(); + + var comp = CreateCompilation("", references: [comp1.EmitToImageReference(), module1Ref, module2Ref]); + CompileAndVerify(comp, validator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + comp = CreateCompilation("", references: [comp1.ToMetadataReference(), module1Ref, module2Ref]); + CompileAndVerify(comp, validator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + var comp3Ref = comp3.EmitToImageReference(); + + comp1 = comp1.AddReferences(comp3Ref); + + comp = CreateCompilation("", references: [comp1.ToMetadataReference(), module1Ref, module2Ref]); + CompileAndVerify(comp, validator: validateAssembly, verify: Verification.Skipped).VerifyDiagnostics(); + + static void validateAssembly(PEAssembly assembly) + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x23000002 (AssemblyReference) 0x200000", + ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0000", + ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000002 (ExportedType) 0x0000", + ], actual); + } + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79193")] public void OverloadResolution_01() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs index 54dc455d3aeb0..38272b8361604 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs @@ -1370,10 +1370,10 @@ public static class Extensions [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Extensions))] "; - // https://github.com/dotnet/roslyn/issues/78963 - The grouping and marker types should be among the forwarded types since they are public. - // They also should be among exported types for library built from source1. Type symbols - // representing extensions from the language perspective should not be in either set. - CheckForwarderEmit(source1, source2, "Extensions"); + // The grouping and marker types should be among the forwarded types since they are public. + // They also should be among exported types for library built from source1. Type symbols + // representing extensions from the language perspective should not be in either set. + CheckForwarderEmit(source1, source2, "Extensions", "Extensions+$BA41CFE2B5EDAEB8C1B9062F59ED4D69", "Extensions+$BA41CFE2B5EDAEB8C1B9062F59ED4D69+$BA41CFE2B5EDAEB8C1B9062F59ED4D69"); } [ClrOnlyFact] @@ -1525,19 +1525,33 @@ private void CheckForwarderEmit(string source1, string source2, params string[] Assert.Equal(topLevelTypes.OrderBy(s => s), GetNamesOfForwardedTypes(assembly)); }; - var verifier2 = CompileAndVerify(comp2, symbolValidator: metadataValidator, sourceSymbolValidator: metadataValidator); + checkForwarderEmit(comp2); - using (ModuleMetadata metadata = ModuleMetadata.CreateFromImage(verifier2.EmittedAssemblyData)) - { - var metadataReader = metadata.Module.GetMetadataReader(); + comp2 = CreateCompilation(source2, new[] { comp1.ToMetadataReference() }, options: TestOptions.ReleaseDll, assemblyName: "Asm2"); + checkForwarderEmit(comp2); - Assert.Equal(forwardedTypeFullNames.Length, metadataReader.GetTableRowCount(TableIndex.ExportedType)); + var comp3 = CreateCompilation("public class ForceRetargeting;", options: TestOptions.ReleaseDll); + comp1 = comp1.AddReferences(comp3.EmitToImageReference()); - int i = 0; - foreach (var exportedType in metadataReader.ExportedTypes) + comp2 = CreateCompilation(source2, new[] { comp1.ToMetadataReference() }, options: TestOptions.ReleaseDll, assemblyName: "Asm2"); + checkForwarderEmit(comp2); + + void checkForwarderEmit(CSharpCompilation comp2) + { + var verifier2 = CompileAndVerify(comp2, symbolValidator: metadataValidator, sourceSymbolValidator: metadataValidator); + + using (ModuleMetadata metadata = ModuleMetadata.CreateFromImage(verifier2.EmittedAssemblyData)) { - ValidateExportedTypeRow(exportedType, metadataReader, forwardedTypeFullNames[i]); - i++; + var metadataReader = metadata.Module.GetMetadataReader(); + + Assert.Equal(forwardedTypeFullNames.Length, metadataReader.GetTableRowCount(TableIndex.ExportedType)); + + int i = 0; + foreach (var exportedType in metadataReader.ExportedTypes) + { + ValidateExportedTypeRow(exportedType, metadataReader, forwardedTypeFullNames[i]); + i++; + } } } } diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index 8d3aea6d469d2..06cdc6dd61f55 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -188,7 +188,7 @@ public void CreateDeletedMemberDefinitions(DiagnosticBag diagnosticBag) /// Public types defined in other modules making up this assembly and to which other assemblies may refer to via this assembly /// followed by types forwarded to another assembly. /// - public abstract ImmutableArray GetExportedTypes(DiagnosticBag diagnostics); + public abstract ImmutableArray GetExportedTypes(EmitContext context); /// /// Used to distinguish which style to pick while writing native PDB information. diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 6ed421119a9c1..f19b24508880e 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -2221,7 +2221,7 @@ private void PopulateExportedTypeTableRows() return; } - var exportedTypes = module.GetExportedTypes(Context.Diagnostics); + var exportedTypes = module.GetExportedTypes(Context); if (exportedTypes.Length == 0) { return; diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs index de451e0395420..f165123bc08b8 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs @@ -34,7 +34,7 @@ public override void Visit(CommonPEModuleBuilder module) Visit(module.GetSourceModuleAttributes()); Visit(module.GetTopLevelTypeDefinitions(Context)); - foreach (var exportedType in module.GetExportedTypes(Context.Diagnostics)) + foreach (var exportedType in module.GetExportedTypes(Context)) { VisitExportedType(exportedType.Type); } diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index d07e661d83163..751db40438a91 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -378,14 +378,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Loop While stack.Count > 0 End Function - Public NotOverridable Overrides Function GetExportedTypes(diagnostics As DiagnosticBag) As ImmutableArray(Of Cci.ExportedType) + Public NotOverridable Overrides Function GetExportedTypes(context As EmitContext) As ImmutableArray(Of Cci.ExportedType) Debug.Assert(HaveDeterminedTopLevelTypes) If _lazyExportedTypes.IsDefault Then Dim initialized = ImmutableInterlocked.InterlockedInitialize(_lazyExportedTypes, CalculateExportedTypes()) If initialized AndAlso _lazyExportedTypes.Length > 0 Then - ReportExportedTypeNameCollisions(_lazyExportedTypes, diagnostics) + ReportExportedTypeNameCollisions(_lazyExportedTypes, context.Diagnostics) End If End If