Skip to content
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

Don't wrap fields in ValueTuple definitions #44231

Merged
merged 34 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
72dc304
Don't wrap original tuple fields
jcouv May 13, 2020
881e92d
No need to unwrap real tuple fields
jcouv May 13, 2020
21f72af
Don't emit tuple error fields
jcouv May 13, 2020
348daea
More tests
jcouv May 14, 2020
4154f07
Address feedback
jcouv May 14, 2020
758a8be
Revert symbol display logic
jcouv May 15, 2020
f4b0b9e
Remove TupleFieldSymbol
jcouv May 15, 2020
df14519
Address feedback from Aleksey
jcouv May 18, 2020
f491231
Cache some information for TupleElementIndex
jcouv May 18, 2020
6400146
Address more feedback
jcouv May 18, 2020
2054720
Keep fields first in GetMembers()
jcouv May 18, 2020
fc85a89
Address more feedback
jcouv May 19, 2020
74cfed2
Add HasExplicitTupleElementName API
jcouv Jun 25, 2020
c422d49
Don't retarget tuple error fields
jcouv Jun 25, 2020
c57a02b
Put event backing field back in map
jcouv Jun 26, 2020
c18e709
Use original definitions for map to index
jcouv Jun 26, 2020
49199b8
Optimize map from field definition to index
jcouv Jun 26, 2020
5575125
Verify metadata emitted for fixed field
jcouv Jun 26, 2020
52dba4d
Verify IsDefinition on long tuples elements
jcouv Jul 5, 2020
92cf81d
Empty line
jcouv Jul 6, 2020
e40f8c3
Tweaks
jcouv Jul 6, 2020
ae512eb
Tweaks
jcouv Jul 6, 2020
612364d
Merge remote-tracking branch 'dotnet/master' into tuple-fields
jcouv Jul 6, 2020
a3706e7
Implement interface on IDE layer symbol
jcouv Jul 6, 2020
6b52689
Restore OriginalDefinition logic
jcouv Jul 7, 2020
9eeeecb
Merge remote-tracking branch 'dotnet/master' into tuple-fields
jcouv Dec 28, 2020
d260464
Add public API
jcouv Dec 28, 2020
59933df
order modifiers
jcouv Dec 29, 2020
465438c
Address feedback
jcouv Feb 26, 2021
77e86db
Use other retargeting option
jcouv Feb 26, 2021
ac45f85
Merge remote-tracking branch 'dotnet/master' into tuple-fields
jcouv Feb 26, 2021
1b2eb98
Remove if. Fix retargeting option
jcouv Feb 26, 2021
2484dc0
Address feedback from Chuck
jcouv Mar 25, 2021
f02ee32
sealed
jcouv Mar 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -554,24 +554,25 @@ private void CompileNamedType(NamedTypeSymbol containingType)

case SymbolKind.Field:
{
var field = (FieldSymbol)member;
var fieldSymbol = (field.TupleUnderlyingField ?? field) as SourceMemberFieldSymbol;
if ((object)fieldSymbol != null)
var fieldSymbol = (FieldSymbol)member;
if (member is TupleErrorFieldSymbol)
{
if (fieldSymbol.IsConst)
{
// We check specifically for constant fields with bad values because they never result
// in bound nodes being inserted into method bodies (in which case, they would be covered
// by the method-level check).
ConstantValue constantValue = fieldSymbol.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
SetGlobalErrorIfTrue(constantValue == null || constantValue.IsBad);
}
break;
}
Copy link
Contributor

@AlekseyTs AlekseyTs May 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} [](start = 28, length = 1)

It looks like there is an extra empty line above #Closed


if (fieldSymbol.IsFixedSizeBuffer && compilationState.Emitting)
{
// force the generation of implementation types for fixed-size buffers
TypeSymbol discarded = fieldSymbol.FixedImplementationType(compilationState.ModuleBuilderOpt);
}
if (fieldSymbol.IsConst)
{
// We check specifically for constant fields with bad values because they never result
// in bound nodes being inserted into method bodies (in which case, they would be covered
// by the method-level check).
ConstantValue constantValue = fieldSymbol.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
SetGlobalErrorIfTrue(constantValue == null || constantValue.IsBad);
}

if (fieldSymbol.IsFixedSizeBuffer && compilationState.Emitting)
{
// force the generation of implementation types for fixed-size buffers
TypeSymbol discarded = fieldSymbol.FixedImplementationType(compilationState.ModuleBuilderOpt);
}
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context)
foreach (var f in AdaptedNamedTypeSymbol.GetFieldsToEmit())
{
Debug.Assert((object)(f.TupleUnderlyingField ?? f) == f);
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert((object)(f.TupleUnderlyingField ?? f) == f); [](start = 16, length = 57)

I think we should keep the assert. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, I think we should make it even stronger and make sure we don't run into a TupleErrorFieldSymbol.


In reply to: 426203595 [](ancestors = 426203595)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like the thread is actually addressed and no other response provided.


In reply to: 426264743 [](ancestors = 426264743,426203595)

Debug.Assert(!(f is TupleErrorFieldSymbol));
if (isStruct || f.GetCciAdapter().ShouldInclude(context))
{
yield return f.GetCciAdapter();
Expand Down
16 changes: 10 additions & 6 deletions src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,14 @@ private void ValidateReferencedAssembly(AssemblySymbol assembly, AssemblyReferen
AddSymbolLocation(result, member);
break;
case SymbolKind.Field:
// NOTE: Dev11 does not add synthesized backing fields for properties,
// but adds backing fields for events, Roslyn adds both
if (member is TupleErrorFieldSymbol)
{
var field = (FieldSymbol)member;
AddSymbolLocation(result, field.TupleUnderlyingField ?? field);
break;
}

// NOTE: Dev11 does not add synthesized backing fields for properties,
// but adds backing fields for events, Roslyn adds both
AddSymbolLocation(result, member);
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AddSymbolLocation(result, member); [](start = 40, length = 34)

It feels like we should skip TupleErrorFieldSymbol here as well. #Closed

break;

case SymbolKind.Event:
Expand All @@ -291,7 +293,7 @@ private void ValidateReferencedAssembly(AssemblySymbol assembly, AssemblyReferen
FieldSymbol field = ((EventSymbol)member).AssociatedField;
if ((object)field != null)
{
AddSymbolLocation(result, field.TupleUnderlyingField ?? field);
AddSymbolLocation(result, field);
}
}
break;
Expand Down Expand Up @@ -1028,7 +1030,9 @@ internal Cci.IFieldReference Translate(
bool needDeclaration = false)
{
Debug.Assert(fieldSymbol.IsDefinitionOrDistinct());
Debug.Assert(!fieldSymbol.IsVirtualTupleField && (object)(fieldSymbol.TupleUnderlyingField ?? fieldSymbol) == fieldSymbol, "tuple fields should be rewritten to underlying by now");
Copy link
Contributor

@AlekseyTs AlekseyTs May 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&& (object)(fieldSymbol.TupleUnderlyingField ?? fieldSymbol) == fieldSymbol [](start = 57, length = 76)

I think we should keep this condition, it is still bad to violate it. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, I think we should make the assert even stronger and make sure we don't run into a TupleErrorFieldSymbol.


In reply to: 426203778 [](ancestors = 426203778)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like the thread is actually addressed completely and no other response provided.


In reply to: 426264817 [](ancestors = 426264817,426203778)

Debug.Assert(!fieldSymbol.IsVirtualTupleField &&
(object)(fieldSymbol.TupleUnderlyingField ?? fieldSymbol) == fieldSymbol &&
fieldSymbol is not TupleErrorFieldSymbol, "tuple fields should be rewritten to underlying by now");

if (!fieldSymbol.IsDefinition)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ protected virtual int GetOrCreateSlot(Symbol symbol, int containingSlot = 0, boo
/// </summary>
private int DescendThroughTupleRestFields(ref Symbol symbol, int containingSlot, bool forceContainingSlotsToExist)
{
if (symbol is TupleFieldSymbol fieldSymbol)
if (symbol is TupleElementFieldSymbol fieldSymbol)
{
TypeSymbol containingType = symbol.ContainingType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ public PossiblyConditionalState Clone()
var output = new BoundDagTemp(e.Syntax, type.Type, e);
int outputSlot = -1;
var originalTupleElement = e.Input.IsOriginalInput && !originalInputElementSlots.IsDefault
? field as TupleFieldSymbol
? field
: null;
if (originalTupleElement is not null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ private void AddTupleTypeName(INamedTypeSymbol symbol)
}

VisitFieldType(element);
if (!element.IsImplicitlyDeclared)
if (element.IsExplicitlyNamedTupleElement)
{
AddSpace();
builder.Add(CreatePart(SymbolDisplayPartKind.FieldName, symbol, element.Name));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ internal override ITypeSymbolInternal CommonGetWellKnownType(WellKnownType wellk
return GetWellKnownType(wellknownType);
}

internal static Symbol? GetRuntimeMember(NamedTypeSymbol declaringType, in MemberDescriptor descriptor, SignatureComparer<MethodSymbol, FieldSymbol, PropertySymbol, TypeSymbol, ParameterSymbol> comparer, AssemblySymbol accessWithinOpt)
internal static Symbol? GetRuntimeMember(NamedTypeSymbol declaringType, in MemberDescriptor descriptor, SignatureComparer<MethodSymbol, FieldSymbol, PropertySymbol, TypeSymbol, ParameterSymbol> comparer, AssemblySymbol? accessWithinOpt)
{
var members = declaringType.GetMembers(descriptor.Name);
return GetRuntimeMember(members, descriptor, comparer, accessWithinOpt);
Expand Down
23 changes: 22 additions & 1 deletion src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,16 @@ public virtual bool IsDefaultTupleElement
{
get
{
Debug.Assert(!(this is TupleElementFieldSymbol or TupleErrorFieldSymbol));
return TupleElementIndex >= 0;
}
}

public virtual bool IsExplicitlyNamedTupleElement
{
get
{
Debug.Assert(!(this is TupleElementFieldSymbol or TupleErrorFieldSymbol));
return false;
}
}
Expand All @@ -418,6 +428,7 @@ public virtual FieldSymbol TupleUnderlyingField
{
get
{
Debug.Assert(!(this is TupleElementFieldSymbol));
return ContainingType.IsTupleType ? this : null;
}
}
Expand All @@ -430,7 +441,8 @@ public virtual FieldSymbol CorrespondingTupleField
{
get
{
return null;
Debug.Assert(!(this is TupleElementFieldSymbol));
return TupleElementIndex >= 0 ? this : null;
}
}

Expand All @@ -451,6 +463,15 @@ public virtual int TupleElementIndex
{
get
{
// wrapped tuple fields already have this information and override this property
Debug.Assert(!(this is TupleElementFieldSymbol or TupleErrorFieldSymbol));

var map = ContainingType.TupleFieldDefinitionsToIndexMap;
if (map is object && map.TryGetValue(this.OriginalDefinition, out int index))
{
return index;
}

return -1;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ internal override IEnumerable<FieldSymbol> GetFieldsToEmit()
else
{
// If there are any non-event fields, they are at the very beginning.
IEnumerable<FieldSymbol> nonEventFields = GetMembers<FieldSymbol>(this.GetMembers(), SymbolKind.Field, offset: 0).Select(f => f.TupleUnderlyingField ?? f);
IEnumerable<FieldSymbol> nonEventFields = GetMembers<FieldSymbol>(this.GetMembers().WhereAsArray(m => !(m is TupleErrorFieldSymbol)), SymbolKind.Field, offset: 0);

// Event backing fields are not part of the set returned by GetMembers. Let's add them manually.
ArrayBuilder<FieldSymbol> eventFields = null;
Expand All @@ -900,9 +900,6 @@ internal override IEnumerable<FieldSymbol> GetFieldsToEmit()
if ((object)associatedField != null)
{
Debug.Assert((object)associatedField.AssociatedSymbol != null);

associatedField = associatedField.TupleUnderlyingField ?? associatedField;

Debug.Assert(!nonEventFields.Contains(associatedField));

if (eventFields == null)
Expand Down Expand Up @@ -1320,7 +1317,9 @@ private void LoadMembers()

if (IsTupleType)
{
int originalCount = members.Count;
members = AddOrWrapTupleMembers(members.ToImmutableAndFree());
membersCount += (members.Count - originalCount); // account for added tuple error fields
Debug.Assert(members is object);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ IFieldSymbol IFieldSymbol.CorrespondingTupleField
}
}

bool IFieldSymbol.IsExplicitlyNamedTupleElement
{
get
{
return _underlying.IsExplicitlyNamedTupleElement;
}
}

bool IFieldSymbol.IsConst => _underlying.IsConst;

bool IFieldSymbol.IsReadOnly => _underlying.IsReadOnly;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,20 @@ public override ImmutableArray<Symbol> GetMembers(string name)
return this.RetargetingTranslator.Retarget(_underlyingType.GetMembers(name));
}

public override void InitializeTupleFieldDefinitionsToIndexMap()
{
Debug.Assert(this.IsTupleType);
Debug.Assert(this.IsDefinition); // we only store a map for definitions

var retargetedMap = new SmallDictionary<FieldSymbol, int>(ReferenceEqualityComparer.Instance);
foreach ((FieldSymbol field, int index) in _underlyingType.TupleFieldDefinitionsToIndexMap)
{
retargetedMap.Add(this.RetargetingTranslator.Retarget(field), index);
}

this.TupleData!.SetFieldDefinitionsToIndexMap(retargetedMap);
}

internal override IEnumerable<FieldSymbol> GetFieldsToEmit()
{
foreach (FieldSymbol f in _underlyingType.GetFieldsToEmit())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ internal partial class RetargetingModuleSymbol
private readonly Func<Symbol, RetargetingNamespaceSymbol> _createRetargetingNamespace;
private readonly Func<Symbol, RetargetingTypeParameterSymbol> _createRetargetingTypeParameter;
private readonly Func<Symbol, RetargetingNamedTypeSymbol> _createRetargetingNamedType;
private readonly Func<Symbol, RetargetingFieldSymbol> _createRetargetingField;
private readonly Func<Symbol, FieldSymbol> _createRetargetingField;
private readonly Func<Symbol, RetargetingPropertySymbol> _createRetargetingProperty;
private readonly Func<Symbol, RetargetingEventSymbol> _createRetargetingEvent;

Expand All @@ -57,9 +57,29 @@ private RetargetingNamedTypeSymbol CreateRetargetingNamedType(Symbol symbol)
return new RetargetingNamedTypeSymbol(this, (NamedTypeSymbol)symbol);
}

private RetargetingFieldSymbol CreateRetargetingField(Symbol symbol)
private FieldSymbol CreateRetargetingField(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
if (symbol is TupleErrorFieldSymbol tupleErrorField)
{
var correspondingTupleField = tupleErrorField.CorrespondingTupleField;
Debug.Assert(correspondingTupleField is TupleErrorFieldSymbol);

var retargetedCorrespondingDefaultFieldOpt = (correspondingTupleField == (object)tupleErrorField)
? null
: (TupleErrorFieldSymbol)RetargetingTranslator.Retarget(correspondingTupleField);

return new TupleErrorFieldSymbol(
RetargetingTranslator.Retarget(tupleErrorField.ContainingType, RetargetOptions.RetargetPrimitiveTypesByName),
tupleErrorField.Name,
tupleErrorField.TupleElementIndex,
tupleErrorField.Locations.IsEmpty ? null : tupleErrorField.Locations[0],
this.RetargetingTranslator.Retarget(tupleErrorField.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode),
tupleErrorField.GetUseSiteInfo().DiagnosticInfo,
tupleErrorField.IsImplicitlyDeclared,
retargetedCorrespondingDefaultFieldOpt);
}

return new RetargetingFieldSymbol(this, (FieldSymbol)symbol);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1350,14 +1350,18 @@ internal override IEnumerable<FieldSymbol> GetFieldsToEmit()
switch (m.Kind)
{
case SymbolKind.Field:
var field = (FieldSymbol)m;
yield return field.TupleUnderlyingField ?? field;
if (m is TupleErrorFieldSymbol)
Copy link
Member

@cston cston May 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (m is TupleErrorFieldSymbol) [](start = 24, length = 31)

When do we try to emit with error fields? #Resolved

Copy link
Member Author

@jcouv jcouv May 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct ValueTuple<T1, T2> { }

This is recognized as a 2-tuple, and thus has Item1 and Item2


In reply to: 425348604 [](ancestors = 425348604)

{
break;
}

yield return (FieldSymbol)m;
break;
case SymbolKind.Event:
FieldSymbol? associatedField = ((EventSymbol)m).AssociatedField;
if ((object?)associatedField != null)
{
yield return associatedField.TupleUnderlyingField ?? associatedField;
yield return associatedField;
}
break;
}
Expand Down Expand Up @@ -1499,13 +1503,6 @@ internal void AssertMemberExposure(Symbol member, bool forDiagnostics = false)
return;
}

if (member is FieldSymbol && this.IsTupleType)
{
Debug.Assert(forDiagnostics);
return; // There are dangling tuple elements, probably related to https://github.com/dotnet/roslyn/issues/43597
// and will be addressed by a pending PR https://github.com/dotnet/roslyn/pull/44231.
}

Debug.Assert(false, "Premature symbol exposure.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1290,24 +1290,12 @@ public sealed override bool AreLocalsZeroed

private bool HasInstanceFields()
{
var members = this.GetMembersUnordered();
for (var i = 0; i < members.Length; i++)
var fields = this.GetFieldsToEmit();
foreach (var field in fields)
{
var m = members[i];
if (!m.IsStatic)
if (!field.IsStatic)
{
switch (m.Kind)
{
case SymbolKind.Field:
return true;

case SymbolKind.Event:
if (((EventSymbol)m).AssociatedField != null)
{
return true;
}
break;
}
return true;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ public override bool IsDefaultTupleElement
}
}

public override bool IsExplicitlyNamedTupleElement
{
get
{
return _tupleElementIndex >= 0 && !_isImplicitlyDeclared;
}
}

public override FieldSymbol TupleUnderlyingField
{
get
Expand Down
Loading