-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Emit and roundtrip underlying instance field #73407
Changes from all commits
6aa7e12
9b00941
b892e3c
bcc08a4
3b18289
5fff14e
be50c30
cec3e26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Fields in structs use
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Could you point me to a documentation confirming that the first instance field will start at offset 0 then? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From ECMA CLI (II.10.1.2): sequential: The CLI shall lay out the fields in sequential order, based on the order of the fields in the logical metadata table (§II.22.15). [Rationale: [...] sequential layout is intended to instruct the CLI to match layout rules commonly followed by languages like C and C++ on an individual platform, where this is possible while still guaranteeing verifiable layout. [...] From the C99 standard section 6.7.2.1 bullet point 13 (unfortunately, gated): Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning. |
||
Interlocked.CompareExchange(ref _lazyUnderlyingInstanceField, field, comparand: null); | ||
} | ||
|
||
return _lazyUnderlyingInstanceField; | ||
} | ||
} | ||
|
||
internal override IEnumerable<FieldSymbol> GetFieldsToEmit() | ||
{ | ||
return !IsStatic && UnderlyingInstanceField is { } underlyingField | ||
? [underlyingField, .. base.GetFieldsToEmit()] | ||
: base.GetFieldsToEmit(); | ||
} | ||
} | ||
} |
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.
It is not obvious to me why do we care if the field is present and why do we need to perform any validation for it. #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.
There's two reasons to care about the presence of the field:
Unsafe.As
to be safe, we need the extension to be properly formed. We could do lighter validation (just the type) but I don't see the point to accept ill-formed extension typesthis.UnderlyingMember
in an instance extension type member will need to be rewritten to access this field, something likethis.<UnderlyingInstanceField>$.UnderlyingMember
(this will be in a later PR)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 don't think we will ever need to do something like this for an imported extension type. We will never be emitting code for its members
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're correct. That leaves reason 1