-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Move to new metadata format for extensions #79553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move to new metadata format for extensions #79553
Conversation
| } | ||
| } | ||
|
|
||
| internal static void ReverseEndianness(Span<short> shortSpan) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can define this in MS.CA.Contracts project as a static extension polyfill API. #WontFix
| System_Collections_Generic_List_T__AddRange, | ||
|
|
||
| System_Runtime_CompilerServices_ParamCollectionAttribute__ctor, | ||
| System_Runtime_CompilerServices_ExtensionMarkerNameAttribute__ctor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I added two follow-up items to #78963
- add ExtensionMarkerNameAttribute to BCL
- disallow ExtensionMarkerName attribute in source #Closed
| yield break; | ||
| } | ||
|
|
||
| bool checkForExtensonGroup = this.IsStatic && this.ContainingType is null && this.TypeKind == TypeKind.Class && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| yield break; | ||
| } | ||
|
|
||
| bool checkForExtensonGroup = this.IsStatic && this.ContainingType is null && this.TypeKind == TypeKind.Class && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also check that this.Arity == 0? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also check that this.Arity == 0?
Good idea
| private class ExtensionInfo(TypeDefinitionHandle groupingType, MethodDefinitionHandle markerMethod) | ||
| { | ||
| public readonly MethodDefinitionHandle MarkerMethod = markerMethod; | ||
| public readonly TypeDefinitionHandle GroupingType = groupingType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| internal override string ExtensionName | ||
| => Name; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Confirm implementation | ||
| internal override string ExtensionName // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Confirm implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was changed in main branch to a specific link:
internal override string ExtensionName
=> Name; // Tracked by [https://github.com/dotnet/roslyn/issues/78963](https://github.com/dotnet/roslyn/issues/78963) : Revisit when adopting new metadata design with content-based type names
Consider using a PROTOTYPE comment or removing the link entirely since there's a PROTOTYPE in implementation. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider using a PROTOTYPE comment or removing the link entirely since there's a PROTOTYPE in implementation.
Removing
|
|
||
| if (IsExtension) | ||
| { | ||
| // We do not recognize any attributes on extension blocks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider leaving PROTOTYPE comments to cover the changes in GetAttributes, CreateEvents, CreateFields and CreateNestedTypes showing that attributes/events/fields/types in metadata are ignored. #Closed
| public override IEnumerable<TypeReferenceWithAttributes> GetConstraints(EmitContext context) | ||
| { | ||
| // Drop all attributes from constraints, they are about C# specific semantics. | ||
| foreach (var constrint in base.GetConstraints(context)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the extension case still reachable in Refers to: src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs:293 in 221fc17. [](commit_id = 221fc17, deletion_comment = False) |
|
|
||
| if (_lazyExtensionInfo.LazyExtensionMarkerName is null) | ||
| { | ||
| _lazyExtensionInfo.LazyExtensionMarkerName = "<Marker>$" + RawNameToHashString(ComputeExtensionMarkerRawName()); // PROTOTYPE: Add a constant for this prefix. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are any PROTOTYPE comments in this PR that you don't consider blocking for merging the feature branch back to main, it would be helpful to use linked issues instead or tagging them somehow. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are any PROTOTYPE comments in this PR that you don't consider blocking for merging the feature branch back to main, it would be helpful to use linked issues instead or tagging them somehow.
The things I would consider truly blocking:
- dock IDs and doc comments on merged extension blocks
- Detecting and reporting collisions during merge into grouping and marker types.
- Finalizing anything that affects metadata names. Prefixes, etc.
- Test that the right things are dropped from grouping types (we can follow up offline)
- Test that the right things are preserved on marker types (we can follow up offline)
However, I'd rather not make this decision on my own, hence leaving the comments. The comments are also more preferable for me from the context and time consumption perspective.
| { | ||
| return ((SourceMemberContainerTypeSymbol)containingType.ContainingType).GetExtensionGroupingInfo().GetCorrespondingMarkerType((SourceNamedTypeSymbol)containingType); | ||
| } | ||
| else if (containingType.IsExtension) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| { | ||
| internal sealed class ExtensionGroupingInfo | ||
| { | ||
| private readonly Dictionary<string, MultiDictionary<string, SourceNamedTypeSymbol>> _groupingMap; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Consider documenting. If I understood correctly, the outer string key is a grouping name, the inner string key is a marker name, and the named types are the extension declarations
nit: it may be good to extract the computation logic that creates the dictionary and ExtensionGroupingInfo from GetExtensionGroupingInfo and move it into ExtensionGroupingInfo so the logic constructing this dictionary is closer to the dictionary's definition. #Closed
| } | ||
| } | ||
|
|
||
| private class ExtensionGroupingTypeParameter : InheritedTypeParameter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| int IComparable<ExtensionGroupingType>.CompareTo(ExtensionGroupingType? other) | ||
| { | ||
| Debug.Assert(other is { }); | ||
| return ExtensionMarkerTypes[0].CompareTo(other.ExtensionMarkerTypes[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we simply compare names? Question also applies to ExtensionMarkerType comparison #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we simply compare names?
I think we should stick to the lexical declaration order for emit purposes.
| builder.Sort(); | ||
| ExtensionMarkerTypes = builder.ToImmutableAndFree(); | ||
|
|
||
| _typeParameters = ExtensionMarkerTypes[0].UnderlyingExtensions[0].Arity != 0 ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be useful to check/assert that the merged signatures are identical (ignoring C#-isms)? That could help catch issues as new features are added in the future and the content-hashes were not updated.
The question also applies to ExtensionMarkerType (that one would not ignore C#-isms)
#Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be useful to check/assert that the merged signatures are identical (ignoring C#-isms)?
This check should be performed not as an assert. It is not implemented in this PR and a PROTOTYPE comment is left in SourceMemberContainerTypeSymbol.
|
|
||
| protected override bool IsSealed => true; | ||
|
|
||
| protected override ITypeDefinition ContainingTypeDefinition => ExtensionMarkerTypes[0].UnderlyingExtensions[0].ContainingType!.GetCciAdapter(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be an assertion (here or possibly in the constructor)
I do not think its worth the ceremony. The fact is obvious by construction, i.e. these are nested types symbols.
| { | ||
| Debug.Assert((object)method != null); | ||
|
|
||
| // PROTOTYPE: SynthesizedExtensionMarker should not be added as a member of the extension type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear what you intend for SynthesizedExtensionMarker. Consider leaving more details if this is something I'll need to handle #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear what you intend for SynthesizedExtensionMarker. Consider leaving more details if this is something I'll need to handle
I went ahead and made the change in this PR
|
|
||
| var builder = ArrayBuilder<TResult>.GetInstance(); | ||
|
|
||
| int index = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The index seems unused #Closed
Refers to: src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs:55 in 221fc17. [](commit_id = 221fc17, deletion_comment = False) |
jcouv
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done with review pass (commit 1)
| y.OriginalDefinition.AsMember(normalize(yExtension))); | ||
| } | ||
|
|
||
| private static NamedTypeSymbol normalize(NamedTypeSymbol extension) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| private static NamedTypeSymbol normalize(NamedTypeSymbol extension) | |
| private static NamedTypeSymbol Normalize(NamedTypeSymbol extension) | |
| ``` #Resolved |
| } | ||
| } | ||
|
|
||
| private class ExtensionGroupingTypeParameter : InheritedTypeParameter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| private class ExtensionGroupingTypeParameter : InheritedTypeParameter | |
| private sealed class ExtensionGroupingTypeParameter : InheritedTypeParameter | |
| ``` #Resolved |
| yield return synthesized; | ||
| break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we don't need an iterator method but can simply return:
| yield return synthesized; | |
| break; | |
| return [synthesized]; | |
| ``` #Resolved |
| if (marker.TryGetExtensionMarkerMethod() is { IsNil: false } markerHandle) | ||
| { | ||
| marker = PENamedTypeSymbol.Create(moduleSymbol, this, markerRid, type.Handle, markerHandle); | ||
| yield return marker; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't marker types nested in the grouping type? But we seem to be returning them as nested for the extension enclosing static class. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't marker types nested in the grouping type? But we seem to be returning them as nested for the extension enclosing static class.
That is correct, we are transforming what we have in metadata to how the language sees things, i.e. extension blocks directly in the top level static class.
For multiple markers in a group and attribute values adding some extra checks to For merged blocks under one marker adding some extra checks to In reply to: 3111874120 Refers to: src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs:55 in 221fc17. [](commit_id = 221fc17, deletion_comment = False) |
I do not think so, but I will leave this clean-up for a follow-up. In reply to: 3111750451 Refers to: src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs:293 in 221fc17. [](commit_id = 221fc17, deletion_comment = False) |
jcouv
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM Thanks (commit 3)
The marker method symbol still has the extension block as containing type (and when we produce metadata, we re-parent it). Should we attach it directly to the right marker type instead? Refers to: src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs:1106 in faf186a. [](commit_id = faf186a, deletion_comment = False) |
Addresses part of #78963 ("switch away from ordinals in skeleton type names")
Relates to test plan #76130