diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index e1e7eb35eb7a6..6d4c7c5655851 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1871,7 +1871,7 @@ public override TypeKind TypeKind } // PROTOTYPE consider caching/optimizing this computation - if (!TryGetExtensionMarkerMethod().IsNil) + if (!TryGetExtensionInfo().MarkerMethod.IsNil) { // Extension result = TypeKind.Extension; @@ -1895,10 +1895,12 @@ public override TypeKind TypeKind /// /// Superficially checks whether this is a valid extension type - /// and returns the extension marker method (to be validated later) + /// and returns the extension marker method and underlying instance field if applicable /// if it is. + /// Both will be validated later. /// - private MethodDefinitionHandle TryGetExtensionMarkerMethod() + private (MethodDefinitionHandle MarkerMethod, FieldDefinitionHandle UnderlyingInstanceField) + TryGetExtensionInfo() { var moduleSymbol = this.ContainingPEModule; var module = moduleSymbol.Module; @@ -1913,15 +1915,39 @@ private MethodDefinitionHandle TryGetExtensionMarkerMethod() try { - // They must not contain any instance state + // The only expected instance state is the underlying instance field + FieldDefinitionHandle foundUnderlyingInstanceField = default; foreach (var field in module.GetFieldsOfTypeOrThrow(_handle)) { - if ((module.GetFieldDefFlagsOrThrow(field) & FieldAttributes.Static) == 0) + if (module.GetFieldDefNameOrThrow(field) == WellKnownMemberNames.ExtensionFieldName) + { + if ((module.GetFieldDefFlagsOrThrow(field) & FieldAttributes.Static) != 0) + { + // It must be an instance field + return default; + } + + if (this.IsStatic) + { + // It's only allowed in non-static extension types + return default; + } + + Debug.Assert(foundUnderlyingInstanceField.IsNil); + foundUnderlyingInstanceField = field; + } + else if ((module.GetFieldDefFlagsOrThrow(field) & FieldAttributes.Static) == 0) { return default; } } + if (!this.IsStatic && foundUnderlyingInstanceField.IsNil) + { + // Non-static extensions must have an underlying instance field (to be validated later) + return default; + } + // They must have a single marker method (to be validated later) MethodDefinitionHandle foundMarkerMethod = default; foreach (var methodHandle in module.GetMethodsOfTypeOrThrow(_handle)) @@ -1940,7 +1966,7 @@ private MethodDefinitionHandle TryGetExtensionMarkerMethod() } } - return foundMarkerMethod; + return (foundMarkerMethod, foundUnderlyingInstanceField); } catch (BadImageFormatException) { @@ -1968,10 +1994,11 @@ private void DecodeExtensionType(out bool isExplicit, out TypeSymbol underlyingT bool tryDecodeExtensionType(out bool isExplicit, [NotNullWhen(true)] out TypeSymbol? underlyingType) { - var markerMethod = TryGetExtensionMarkerMethod(); + var (markerMethod, underlyingInstanceField) = TryGetExtensionInfo(); Debug.Assert(!markerMethod.IsNil); var moduleSymbol = this.ContainingPEModule; + // Decode and validate marker method isExplicit = false; underlyingType = null; @@ -1987,7 +2014,7 @@ bool tryDecodeExtensionType(out bool isExplicit, [NotNullWhen(true)] out TypeSym } // PROTOTYPE do we want to tighten the flags check further? (require that type be sealed?) - if ((localFlags & MethodAttributes.Private) == 0 || + if ((localFlags & MethodAttributes.MemberAccessMask) != MethodAttributes.Private || (localFlags & MethodAttributes.Static) == 0) { return false; @@ -2010,7 +2037,9 @@ bool tryDecodeExtensionType(out bool isExplicit, [NotNullWhen(true)] out TypeSym // PROTOTYPE need to decode extension type references (may be some cycle issues) type = ApplyTransforms(type, paramInfo.Handle, moduleSymbol); + ImmutableArray customModifiers = CSharpCustomModifier.Convert(paramInfo.CustomModifiers); + // PROTOTYPE consider checking top-level nullability annotation if (paramInfo.IsByRef || !paramInfo.CustomModifiers.IsDefault) { var info = new CSDiagnosticInfo(ErrorCode.ERR_MalformedExtensionInMetadata, this); // PROTOTYPE need to report use-site diagnostic @@ -2031,10 +2060,22 @@ bool tryDecodeExtensionType(out bool isExplicit, [NotNullWhen(true)] out TypeSym { var info = new CSDiagnosticInfo(ErrorCode.ERR_MalformedExtensionInMetadata, this); // PROTOTYPE need to report use-site diagnostic underlyingType = new ExtendedErrorTypeSymbol(type, LookupResultKind.NotReferencable, info, unreported: true); + return false; } else { - underlyingType = type; + // Validate instance field + if (!underlyingInstanceField.IsNil + && !validateUnderlyingInstanceField(underlyingInstanceField, moduleSymbol, type)) + { + var info = new CSDiagnosticInfo(ErrorCode.ERR_MalformedExtensionInMetadata, this); // PROTOTYPE need to report use-site diagnostic + underlyingType = new ExtendedErrorTypeSymbol(type, LookupResultKind.NotReferencable, info, unreported: true); + return false; + } + else + { + underlyingType = type; + } } } else @@ -2046,6 +2087,27 @@ bool tryDecodeExtensionType(out bool isExplicit, [NotNullWhen(true)] out TypeSym Debug.Assert(underlyingType is not null); return true; } + + bool validateUnderlyingInstanceField(FieldDefinitionHandle underlyingInstanceFieldHandle, PEModuleSymbol moduleSymbol, TypeSymbol underlyingType) + { + var fieldSymbol = new PEFieldSymbol(moduleSymbol, this, underlyingInstanceFieldHandle); + + if (fieldSymbol.DeclaredAccessibility != Accessibility.Private + || fieldSymbol.IsStatic + || fieldSymbol.RefKind != RefKind.None + || fieldSymbol.IsReadOnly) + { + return false; + } + + if (!fieldSymbol.TypeWithAnnotations.Equals(TypeWithAnnotations.Create(underlyingType), TypeCompareKind.CLRSignatureCompareOptions)) + { + return false; + } + + // PROTOTYPE do we want to tighten the checks further? (required) + return true; + } } #nullable disable @@ -2186,12 +2248,18 @@ private IEnumerable CreateNestedTypes() } } + var underlyingInstanceField = TryGetExtensionInfo().UnderlyingInstanceField; try { foreach (var fieldRid in module.GetFieldsOfTypeOrThrow(_handle)) { try { + if (!underlyingInstanceField.IsNil && fieldRid == underlyingInstanceField) + { + continue; + } + if (!(isOrdinaryEmbeddableStruct || (isOrdinaryStruct && (module.GetFieldDefFlagsOrThrow(fieldRid) & FieldAttributes.Static) == 0) || module.ShouldImportField(fieldRid, moduleSymbol.ImportOptions))) @@ -2231,7 +2299,7 @@ private IEnumerable CreateNestedTypes() // PROTOTYPE are extensions embeddable? // for ordinary embeddable struct types we import private members so that we can report appropriate errors if the structure is used var isOrdinaryEmbeddableStruct = (this.TypeKind == TypeKind.Struct) && (this.SpecialType == Microsoft.CodeAnalysis.SpecialType.None) && this.ContainingAssembly.IsLinked; - var extensionMarkerMethod = TryGetExtensionMarkerMethod(); + var extensionMarkerMethod = TryGetExtensionInfo().MarkerMethod; try { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceExtensionTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceExtensionTypeSymbol.cs index 5c43d08fd5c14..f69fcb9368b14 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceExtensionTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceExtensionTypeSymbol.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -18,6 +19,8 @@ internal sealed class SourceExtensionTypeSymbol : SourceNamedTypeSymbol private ExtensionInfo _lazyDeclaredExtensionInfo = ExtensionInfo.Sentinel; // PROTOTYPE consider renaming ExtensionUnderlyingType->ExtendedType (here and elsewhere) private TypeSymbol? _lazyExtensionUnderlyingType = ErrorTypeSymbol.UnknownResultType; + // For non-static extensions, we emit a field of the underlying type + private FieldSymbol? _lazyUnderlyingInstanceField = null; internal SourceExtensionTypeSymbol(NamespaceOrTypeSymbol containingSymbol, MergedTypeDeclaration declaration, BindingDiagnosticBag diagnostics) : base(containingSymbol, declaration, diagnostics) @@ -395,5 +398,37 @@ internal static bool IsRestrictedExtensionUnderlyingType(TypeSymbol type) return false; } + + private FieldSymbol? UnderlyingInstanceField + { + get + { + if (IsStatic) + { + throw ExceptionUtilities.Unreachable(); + } + + var extendedType = GetExtendedTypeNoUseSiteDiagnostics(null); + if (extendedType is null) + { + return null; + } + + if (_lazyUnderlyingInstanceField is null) + { + var field = new SynthesizedFieldSymbol(this, extendedType, WellKnownMemberNames.ExtensionFieldName); + Interlocked.CompareExchange(ref _lazyUnderlyingInstanceField, field, comparand: null); + } + + return _lazyUnderlyingInstanceField; + } + } + + internal override IEnumerable GetFieldsToEmit() + { + return !IsStatic && UnderlyingInstanceField is { } underlyingField + ? [underlyingField, .. base.GetFieldsToEmit()] + : base.GetFieldsToEmit(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/ExtensionTypeTests.cs b/src/Compilers/CSharp/Test/Emit3/ExtensionTypeTests.cs index 45d2b51c592bc..3d29c76b46f29 100644 --- a/src/Compilers/CSharp/Test/Emit3/ExtensionTypeTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/ExtensionTypeTests.cs @@ -8,6 +8,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; @@ -18,6 +20,7 @@ using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; +using static Roslyn.Test.Utilities.MetadataReaderUtils; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; @@ -54,7 +57,7 @@ private static void AssertSetStrictlyEqual(string[] expected, string[] actual) } // Verify things that are common for all extension types - private static void VerifyExtension(TypeSymbol type, bool? isExplicit, SpecialType specialType = SpecialType.None) where T : TypeSymbol + private static void VerifyExtension(TypeSymbol type, bool? isExplicit, SpecialType specialType = SpecialType.None, string fieldType = null) where T : TypeSymbol { var namedType = (NamedTypeSymbol)type; Assert.True(namedType is T); @@ -127,6 +130,28 @@ private static void AssertSetStrictlyEqual(string[] expected, string[] actual) Assert.False(sourceNamedType.IsImplicitlyDeclared); } + if (type is PENamedTypeSymbol peType) + { + var module = (PEModuleSymbol)type.ContainingModule; + var reader = module.Module.GetMetadataReader(); + var fieldDefHandle = reader.GetTypeDefinition(peType.Handle).GetFields() + .Where(f => reader.GetString(reader.GetFieldDefinition(f).Name) == WellKnownMemberNames.ExtensionFieldName).SingleOrDefault(); + + // Static extensions don't have this field, but non-static extensions have it + Assert.Equal(fieldDefHandle.IsNil, type.IsStatic); + + if (!type.IsStatic) + { + var fieldDef = reader.GetFieldDefinition(fieldDefHandle); + var blob = reader.GetBlobReader(fieldDef.Signature); + var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, reader, genericContext: null); + var fieldTypeDisplay = decoder.DecodeFieldSignature(ref blob); + + // The instance value field has the expected type + Assert.Equal(fieldType, fieldTypeDisplay); + } + } + static void checkBaseExtension(NamedTypeSymbol baseExtension) { if (baseExtension.IsExtension) @@ -243,7 +268,7 @@ void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: isExplicit); + VerifyExtension(r, isExplicit: isExplicit, fieldType: "UnderlyingClass"); } Assert.Equal("UnderlyingClass", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -398,6 +423,7 @@ public void ForClass_Metadata_RefStruct(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -644,7 +670,7 @@ static void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "UnderlyingClass"); } AssertEx.Equal(new[] @@ -2032,7 +2058,7 @@ static void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "UnderlyingStruct"); } Assert.Equal("UnderlyingStruct", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -2074,7 +2100,7 @@ void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: isExplicit); + VerifyExtension(r, isExplicit: isExplicit, fieldType: "!0"); } Assert.Equal("T", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -2116,7 +2142,7 @@ void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: isExplicit); + VerifyExtension(r, isExplicit: isExplicit, fieldType: "E"); } Assert.Equal("E", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -2184,7 +2210,7 @@ static void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "C`1{!0}"); } Assert.Equal("C", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -2225,7 +2251,7 @@ static void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "System.ValueTuple`2{Int32, Int32}"); } Assert.Equal("(System.Int32, System.Int32)", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -2266,7 +2292,7 @@ static void validate(ModuleSymbol module) } else { - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "Int32[]"); } Assert.Equal("System.Int32[]", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); @@ -3095,7 +3121,7 @@ public void UnderlyingType_NativeInt(bool useImageReference) static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "IntPtr"); Assert.Equal("nint", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -3137,7 +3163,7 @@ public void UnderlyingType_NativeInt_OlderFramework(bool useImageReference) static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "IntPtr"); Assert.Equal("nint", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -3181,7 +3207,7 @@ public class C { } static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "C`1{IntPtr}"); Assert.Equal("C", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -3198,7 +3224,45 @@ public class C { } var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); - CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: Verification.FailsPEVerify); + var verifier = CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate, verify: Verification.FailsPEVerify); + // Note: we don't emit a DynamicAttribute on synthesized field + verifier.VerifyTypeIL("R", """ +.class public sequential ansi sealed beforefieldinit R + extends [System.Runtime]System.ValueType +{ + .custom instance void [System.Runtime]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 01 00 00 + ) + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = ( + 01 00 0e 45 78 74 65 6e 73 69 6f 6e 54 79 70 65 + 73 00 00 + ) + // Fields + .field private class C`1 '$' + .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Methods + .method private hidebysig static + void '$' ( + class C`1 '' + ) cil managed + { + .param [1] + .custom instance void [System.Linq.Expressions]System.Runtime.CompilerServices.DynamicAttribute::.ctor(bool[]) = ( + 01 00 02 00 00 00 00 01 00 00 + ) + // Method begins at RVA 0x206f + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method R::'$' +} // end of class R +"""); if (new NoBaseExtensions().ShouldSkip) return; @@ -3223,7 +3287,7 @@ public class C { } static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "C`1{Object}"); Assert.Equal("C", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -3280,7 +3344,7 @@ public class C { } static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "C`1{Object}"); Assert.Equal("C", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString(includeNonNullable: true)); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -3337,7 +3401,7 @@ public class C { } static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "C`1{Object}"); Assert.Equal("C", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString(includeNonNullable: true)); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -3397,7 +3461,7 @@ public void UnderlyingType_TupleWithElementNames() static void validate(ModuleSymbol module) { var r = module.GlobalNamespace.GetTypeMember("R"); - VerifyExtension(r, isExplicit: true); + VerifyExtension(r, isExplicit: true, fieldType: "System.ValueTuple`2{Int32, Int32}"); Assert.Equal("(System.Int32 a, System.Int32 b)", r.GetExtendedTypeNoUseSiteDiagnostics(null).ToTestDisplayString()); Assert.Empty(r.BaseExtensionsNoUseSiteDiagnostics); Assert.Empty(r.AllBaseExtensionsNoUseSiteDiagnostics); @@ -4467,7 +4531,7 @@ public struct R2 { } comp5.VerifyDiagnostics(); var r1 = comp5.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: true); + VerifyExtension(r1, isExplicit: true, fieldType: "R2"); var r2FromR1 = (ExtendedErrorTypeSymbol)r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("R2", r2FromR1.ToTestDisplayString()); AssertEx.Equal("error CS9322: Extension marker method on type 'R1' is malformed.", r2FromR1.ErrorInfo.ToString()); @@ -6719,8 +6783,8 @@ public void IsExtension_NestedNamedTypeSymbol(bool isExplicit, bool isExplicit2) void validate(ModuleSymbol module) { var e2 = module.GlobalNamespace.GetTypeMember("E1").GetTypeMember("E2"); - VerifyExtension(e2.ContainingType, isExplicit: isExplicit); - VerifyExtension(e2, isExplicit: isExplicit2); + VerifyExtension(e2.ContainingType, isExplicit: isExplicit, fieldType: "Int32"); + VerifyExtension(e2, isExplicit: isExplicit2, fieldType: "Int32"); } } @@ -7114,6 +7178,7 @@ public void ExtensionMarkerMethod_Baseline(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7129,12 +7194,57 @@ public void ExtensionMarkerMethod_Baseline(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); var r2 = comp.GlobalNamespace.GetTypeMember("R2"); VerifyExtension(r2, isExplicit: true); } + [Theory] + [InlineData("public")] + [InlineData("family")] + [InlineData("assembly")] + public void ExtensionMarkerMethod_NotPrivateMethod(string methodAccessibility) + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit R1 + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method {{methodAccessibility}} hidebysig static void '{{ExtensionMarkerName(false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M () cil managed + { + IL_0000: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var r1 = comp.GlobalNamespace.GetTypeMember("R1"); + VerifyExtension(r1, isExplicit: false, fieldType: "Object"); + Assert.True(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); + } + [Theory, CombinatorialData] public void ExtensionMarkerMethod_NotByValueParameter(bool isExplicit) { @@ -7146,6 +7256,7 @@ public void ExtensionMarkerMethod_NotByValueParameter(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7161,7 +7272,7 @@ public void ExtensionMarkerMethod_NotByValueParameter(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); var r1ExtendedType = r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); Assert.True(r1ExtendedType.IsErrorType()); @@ -7191,6 +7302,7 @@ public void ExtensionMarkerMethod_WithModoptOnReturn(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7206,7 +7318,7 @@ public void ExtensionMarkerMethod_WithModoptOnReturn(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); if (new NoBaseExtensions().ShouldSkip) return; @@ -7232,6 +7344,7 @@ public void ExtensionMarkerMethod_WithModreqOnReturn(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7247,7 +7360,7 @@ public void ExtensionMarkerMethod_WithModreqOnReturn(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); if (new NoBaseExtensions().ShouldSkip) return; @@ -7271,6 +7384,7 @@ public void ExtensionMarkerMethod_WithModoptOnFirstParameter(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7286,7 +7400,7 @@ public void ExtensionMarkerMethod_WithModoptOnFirstParameter(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); var r1ExtendedType = r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); Assert.True(r1ExtendedType.IsErrorType()); @@ -7304,6 +7418,46 @@ public void ExtensionMarkerMethod_WithModoptOnFirstParameter(bool isExplicit) VerifyExtension(r2, isExplicit: true); } + [Theory, CombinatorialData] + public void ExtensionMarkerMethod_WithModoptOnFirstParameter_FieldHasModoptToo(bool isExplicit) + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit R1 + extends [mscorlib]System.ValueType +{ + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit)}}'(object modopt(object) '') cil managed + { + IL_0000: ret + } + .field private object modopt(object) '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8) + ); + + var r1 = comp.GlobalNamespace.GetTypeMember("R1"); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "modopt(Object) Object"); + var r1ExtendedType = r1.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.True(r1ExtendedType.IsErrorType()); + } + [Theory, CombinatorialData] public void ExtensionMarkerMethod_WithModreqOnFirstParameter(bool isExplicit) { @@ -7316,6 +7470,7 @@ public void ExtensionMarkerMethod_WithModreqOnFirstParameter(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7331,7 +7486,7 @@ public void ExtensionMarkerMethod_WithModreqOnFirstParameter(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); var r1ExtendedType = (ExtendedErrorTypeSymbol)r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); Assert.True(r1ExtendedType.IsErrorType()); @@ -7362,6 +7517,7 @@ public void ExtensionMarkerMethod_WithModoptOnSecondParameter(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } .class public sequential ansi sealed beforefieldinit R2 @@ -7387,7 +7543,7 @@ public void ExtensionMarkerMethod_WithModoptOnSecondParameter(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); if (new NoBaseExtensions().ShouldSkip) return; @@ -7469,6 +7625,7 @@ public void ExtensionMarkerMethod_Overloaded(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7506,6 +7663,7 @@ public void ExtensionMarkerMethod_Overloaded(bool isExplicit) Assert.Equal(new[] { + $$"""System.Object R1.{{WellKnownMemberNames.ExtensionFieldName}}""", "R1..ctor()", $$"""void R1.{{ExtensionMarkerName(isExplicit)}}(System.Object A_0)""", $$"""void R1.{{ExtensionMarkerName(isExplicit)}}(System.String A_0)""" @@ -7537,6 +7695,7 @@ public void ExtensionMarkerMethod_Overloaded_DifferentExplicitness(bool isExplic { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7574,6 +7733,7 @@ public void ExtensionMarkerMethod_Overloaded_DifferentExplicitness(bool isExplic Assert.Equal(new[] { + $$"""System.Object R1.{{WellKnownMemberNames.ExtensionFieldName}}""", "R1..ctor()", $$"""void R1.{{ExtensionMarkerName(isExplicit)}}(System.Object A_0)""", $$"""void R1.{{ExtensionMarkerName(!isExplicit)}}(System.String A_0)""" @@ -7594,6 +7754,7 @@ public void ExtensionMarkerMethod_ExtensionMethod(bool isExplicit) .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7613,7 +7774,7 @@ static class OtherExtension ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); var r2 = comp.GlobalNamespace.GetTypeMember("R2"); VerifyExtension(r2, isExplicit: true); @@ -7631,6 +7792,7 @@ public void ExtensionMarkerMethod_WithThisParameter(bool isExplicit) .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 01 ) IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7645,7 +7807,7 @@ public void ExtensionMarkerMethod_WithThisParameter(bool isExplicit) Diagnostic(ErrorCode.ERR_NotYetImplementedInRoslyn, ": R1").WithArguments("base extensions").WithLocation(1, 41) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); } [Theory, CombinatorialData] @@ -7661,6 +7823,7 @@ public void ExtensionMarkerMethod_WithDynamicFirstParameter(bool isExplicit) .custom instance void [mscorlib]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( 01 00 00 00 ) IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7676,7 +7839,7 @@ public void ExtensionMarkerMethod_WithDynamicFirstParameter(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); var r1Underyling = r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("dynamic", r1Underyling.ToTestDisplayString()); Assert.True(r1Underyling.IsErrorType()); @@ -7706,6 +7869,7 @@ public void ExtensionMarkerMethod_WithExtensionFirstParameter(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } .class public sequential ansi sealed beforefieldinit R1 @@ -7715,6 +7879,7 @@ public void ExtensionMarkerMethod_WithExtensionFirstParameter(bool isExplicit) { IL_0000: ret } + .field private valuetype R0 '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7731,7 +7896,7 @@ public void ExtensionMarkerMethod_WithExtensionFirstParameter(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "R0"); var r1Underyling = (ExtendedErrorTypeSymbol)r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("R0", r1Underyling.ToTestDisplayString()); Assert.True(r1Underyling.IsErrorType()); @@ -7762,6 +7927,7 @@ public void ExtensionMarkerMethod_WithSelfExtensionFirstParameter() { IL_0000: ret } + .field private valuetype R1 '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7779,7 +7945,7 @@ public void ExtensionMarkerMethod_WithSelfExtensionFirstParameter() ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: true); + VerifyExtension(r1, isExplicit: true, fieldType: "R1"); var r1Underyling = r1.GetExtendedTypeNoUseSiteDiagnostics(null); Assert.Equal("R1", r1Underyling.ToTestDisplayString()); Assert.True(r1Underyling.IsErrorType()); @@ -7809,6 +7975,7 @@ public void ExtensionMarkerMethod_WithNonExtensionSecondParameter() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7848,6 +8015,7 @@ public void ExtensionMarkerMethod_WithSelfReferentialSecondParameter() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7888,6 +8056,7 @@ public void ExtensionMarkerMethod_WithDuplicateSecondAndThirdParameters() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } .class public sequential ansi sealed beforefieldinit R2 @@ -7897,6 +8066,7 @@ public void ExtensionMarkerMethod_WithDuplicateSecondAndThirdParameters() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7914,11 +8084,11 @@ public void ExtensionMarkerMethod_WithDuplicateSecondAndThirdParameters() ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: true); + VerifyExtension(r1, isExplicit: true, fieldType: "Object"); Assert.Empty(r1.BaseExtensionsNoUseSiteDiagnostics); var r2 = comp.GlobalNamespace.GetTypeMember("R2"); - VerifyExtension(r2, isExplicit: true); + VerifyExtension(r2, isExplicit: true, fieldType: "Object"); if (new NoBaseExtensions().ShouldSkip) return; @@ -7951,6 +8121,7 @@ public void ExtensionMarkerMethod_MissingIsByRefLikeAttribute() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -7966,7 +8137,7 @@ public void ExtensionMarkerMethod_MissingIsByRefLikeAttribute() ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: true); + VerifyExtension(r1, isExplicit: true, fieldType: "Object"); if (new NoBaseExtensions().ShouldSkip) return; @@ -8219,6 +8390,7 @@ public void ObsoleteExtensionMarker_WrongString() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8251,6 +8423,7 @@ public void ExtensionMarkerMethod_NotPrivate(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8266,7 +8439,7 @@ public void ExtensionMarkerMethod_NotPrivate(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); Assert.True(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); if (new NoBaseExtensions().ShouldSkip) return; @@ -8293,6 +8466,7 @@ public void ExtensionMarkerMethod_NotStatic(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8308,7 +8482,7 @@ public void ExtensionMarkerMethod_NotStatic(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); Assert.True(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); if (new NoBaseExtensions().ShouldSkip) return; @@ -8335,6 +8509,7 @@ public void ExtensionMarkerMethod_NotHideBySig() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8350,7 +8525,7 @@ public void ExtensionMarkerMethod_NotHideBySig() ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: true); + VerifyExtension(r1, isExplicit: true, fieldType: "Object"); Assert.False(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); var r2 = comp.GlobalNamespace.GetTypeMember("R2"); @@ -8368,6 +8543,7 @@ public void ExtensionMarkerMethod_NoParameters(bool isExplicit) { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8383,7 +8559,7 @@ public void ExtensionMarkerMethod_NoParameters(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); Assert.True(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); var r2 = comp.GlobalNamespace.GetTypeMember("R2"); @@ -8411,6 +8587,7 @@ public void ExtensionMarkerMethod_NotVoidReturn(bool isExplicit) IL_0000: ldnull IL_0001: throw } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8426,7 +8603,7 @@ public void ExtensionMarkerMethod_NotVoidReturn(bool isExplicit) ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: isExplicit); + VerifyExtension(r1, isExplicit: isExplicit, fieldType: "Object"); Assert.True(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); if (new NoBaseExtensions().ShouldSkip) return; @@ -8452,6 +8629,8 @@ public void ExtensionMarkerMethod_GenericMethod() { IL_0000: ret } + + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8467,7 +8646,8 @@ public void ExtensionMarkerMethod_GenericMethod() ); var r1 = comp.GlobalNamespace.GetTypeMember("R1"); - VerifyExtension(r1, isExplicit: true); + VerifyExtension(r1, isExplicit: true, fieldType: "Object"); + Assert.True(r1.GetExtendedTypeNoUseSiteDiagnostics(null).IsErrorType()); if (new NoBaseExtensions().ShouldSkip) return; @@ -8479,7 +8659,7 @@ public void ExtensionMarkerMethod_GenericMethod() ); var r2 = comp.GlobalNamespace.GetTypeMember("R2"); - VerifyExtension(r2, isExplicit: true); + VerifyExtension(r2, isExplicit: true, fieldType: "Object"); } [ConditionalFact(typeof(NoBaseExtensions))] @@ -8692,6 +8872,7 @@ public void BadMember_ExtensionMethod() .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8736,6 +8917,7 @@ public void BadMember_InstanceField() IL_0000: ret } .field public int32 'field' + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' } """; @@ -8766,7 +8948,7 @@ public void BadMember_InstanceField() } [Fact] - public void ExtensionMarkerMethodHiddenInMetadata() + public void ExtensionMarkerMethodAndInstanceValueFieldHiddenInMetadata() { var src = """ public explicit extension R for object { } @@ -12315,6 +12497,7 @@ public void ExtensionMemberLookup_MatchingExtendedType_GenericMember_TypeOnlyCon { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' .class nested public auto ansi beforefieldinit Nested`1 extends [mscorlib]System.Object @@ -12358,7 +12541,7 @@ public static void M() Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInAgg, "Nested").WithArguments("Nested<>", "object").WithLocation(5, 36)); var e = comp.GlobalNamespace.GetTypeMember("E"); - VerifyExtension(e, isExplicit: false); + VerifyExtension(e, isExplicit: false, fieldType: "Object"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); @@ -35400,6 +35583,7 @@ public void Lookup_ExtensionTypeMembers_MustCallMethodsDirectly() { IL_0000: ret } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' .method public hidebysig specialname newslot static int32 get_Item ( int32 x ) cil managed { @@ -39508,4 +39692,547 @@ internal interface INested : Interface Assert.Equal("IBase.Val", model.GetSymbolInfo(qualifiedName[0]).Symbol.ToTestDisplayString()); Assert.Equal("IBase.Val", model.GetSymbolInfo(qualifiedName[1]).Symbol.ToTestDisplayString()); } + + [Theory, CombinatorialData] + public void InstanceField_StaticExtension(bool isExplicit) + { + var source = $$""" +E.M(); + +public class C { } + +static {{(isExplicit ? "explicit" : "implicit")}} extension E for C +{ + public static void M() { System.Console.Write("ran"); } +} +"""; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("ran"), + sourceSymbolValidator: validate, symbolValidator: validate, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + bool inSource = module is SourceModuleSymbol; + var e = module.GlobalNamespace.GetTypeMember("E"); + if (inSource) + { + VerifyExtension(e, isExplicit: isExplicit); + } + else + { + VerifyExtension(e, isExplicit: isExplicit, fieldType: "C"); + } + } + } + + [Fact] + public void InstanceField_Baseline() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("ran"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "Object"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.False(r1ExtendedType.IsErrorType()); + } + + [Theory] + [InlineData("public")] + [InlineData("family")] + [InlineData("assembly")] + public void InstanceField_FieldNotPrivate(string fieldAccessibility) + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field {{fieldAccessibility}} object '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M () cil managed + { + IL_0000: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "Object"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.True(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_Static() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private static object '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyNotExtension(e); + } + + [Fact] + public void InstanceField_Missing() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + // no instance field + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyNotExtension(e); + } + + [Fact] + public void InstanceField_WrongType() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private string '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "String"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.True(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_RefType() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private object& '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "Object&"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.True(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_DynamicVsObject() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' + .custom instance void [mscorlib]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( 01 00 00 00 ) + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("ran"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "Object"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.False(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_NullabilityDifference_TopLevel() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 ) + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + + .field private object '{{WellKnownMemberNames.ExtensionFieldName}}' + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = ( 01 00 02 00 00 ) + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("ran"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "Object"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.False(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_NullabilityDifference_Nested() + { + var ilSource = $$""" +.class public auto ansi beforefieldinit C`1 + extends [mscorlib]System.Object +{ + .param type T + .method public hidebysig specialname rtspecialname instance void .ctor () cil managed + { + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } +} + +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00) + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(class C`1 '') cil managed + { + .param [1] + .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8[]) = ( 01 00 02 00 00 00 01 02 00 00) + IL_0000: ret + } + + .field private class C`1 '{{WellKnownMemberNames.ExtensionFieldName}}' // C! + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +C.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("ran"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); + + // PROTOTYPE what should we do about the top-level annotation in marker method? + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "C`1{Object}"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("C", r1ExtendedType.ToTestDisplayString()); + Assert.False(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_Modopt() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private object modopt(object) '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "modopt(Object) Object"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.True(r1ExtendedType.IsErrorType()); + } + + [Fact] + public void InstanceField_Readonly() + { + var ilSource = $$""" +.class public sequential ansi sealed beforefieldinit E + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string) = ( + 01 00 43 45 78 74 65 6e 73 69 6f 6e 20 74 79 70 + 65 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f + 72 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 + 73 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d + 70 69 6c 65 72 2e 00 00 + ) + .method private hidebysig static void '{{ExtensionMarkerName(isExplicit: false)}}'(object '') cil managed + { + IL_0000: ret + } + .field private initonly object '{{WellKnownMemberNames.ExtensionFieldName}}' + + .method public hidebysig static void M() cil managed + { + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + + var src = """ +object.M(); +"""; + + var comp = CreateCompilationWithIL(src, ilSource, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + VerifyExtension(e, isExplicit: false, fieldType: "Object"); + var r1ExtendedType = e.GetExtendedTypeNoUseSiteDiagnostics(null); + Assert.Equal("System.Object", r1ExtendedType.ToTestDisplayString()); + Assert.True(r1ExtendedType.IsErrorType()); + } } diff --git a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs index 6350c93411093..4787e6dab5667 100644 --- a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs +++ b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs @@ -406,11 +406,16 @@ public static class WellKnownMemberNames /// /// The name of marker method for an implicit extension type. /// - internal const string ImplicitExtensionMarkerMethodName = "$"; // PROTOTYPE confirm name with WG + internal const string ImplicitExtensionMarkerMethodName = "$"; // PROTOTYPE(static) confirm name with WG /// /// The name of marker method for an explicit extension type. /// - internal const string ExplicitExtensionMarkerMethodName = "$"; // PROTOTYPE confirm name with WG + internal const string ExplicitExtensionMarkerMethodName = "$"; // PROTOTYPE(static) confirm name with WG + + /// + /// The name of field to underlying instance for an extension type. + /// + internal const string ExtensionFieldName = "$"; // PROTOTYPE(static) confirm name with WG } } diff --git a/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs b/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs index 8cd21b15c022b..f11fb2b5dc6a7 100644 --- a/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs +++ b/src/Compilers/Test/Core/Metadata/MetadataReaderUtils.cs @@ -402,7 +402,7 @@ string getQualifiedName(StringHandle leftHandle, StringHandle rightHandle) } } - private sealed class ConstantSignatureVisualizer : ISignatureTypeProvider + internal sealed class ConstantSignatureVisualizer : ISignatureTypeProvider { public static readonly ConstantSignatureVisualizer Instance = new ConstantSignatureVisualizer();