-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Changes from 1 commit
72dc304
881e92d
21f72af
348daea
4154f07
758a8be
f4b0b9e
df14519
f491231
6400146
2054720
fc85a89
74cfed2
c422d49
c57a02b
c18e709
49199b8
5575125
52dba4d
92cf81d
e40f8c3
ae512eb
612364d
a3706e7
6b52689
9eeeecb
d260464
59933df
465438c
77e86db
ac45f85
1b2eb98
2484dc0
f02ee32
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 |
---|---|---|
|
@@ -520,7 +520,8 @@ private void AddTupleTypeName(INamedTypeSymbol symbol) | |
} | ||
|
||
VisitFieldType(element); | ||
if (!element.IsImplicitlyDeclared) | ||
if (!element.IsImplicitlyDeclared && | ||
!(symbol.IsGenericType && symbol.IsDefinition)) // C# tuples are generic | ||
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.
This logic looks suspicious. Is this going to do the right thing for VB symbols? Is this PR affecting behavior of 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. In this PR, we're not changing the behavior of I'll look for VB tests to make sure the scenario you raised is covered. In reply to: 426205983 [](ancestors = 426205983) 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. It feels to me that relying on IsImplicitlyDeclared to decide whether to include names is fragile and probably a wrong thing to do. It feels like that should be decided on whether explicit names were specified in the tuple type syntax used to "create" the tuple type. That is not the same as IsImplicitlyDeclared, or at least it is no longer the same. We probably should base the logic on a more specific API/information. In reply to: 426687365 [](ancestors = 426687365,426205983) 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. It might be worth discussing this issue in more depth offline before we decide the course of action In reply to: 426774418 [](ancestors = 426774418,426687365,426205983) |
||
{ | ||
AddSpace(); | ||
builder.Add(CreatePart(SymbolDisplayPartKind.FieldName, symbol, element.Name)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
// The .NET Foundation licenses this file to you under the MIT license. | ||
// See the LICENSE file in the project root for more information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
|
@@ -399,7 +400,7 @@ public virtual bool IsDefaultTupleElement | |
{ | ||
get | ||
{ | ||
return false; | ||
return ContainingType.IsDefinition && TupleElementIndex >= 0; | ||
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.
|
||
} | ||
} | ||
|
||
|
@@ -424,7 +425,7 @@ public virtual FieldSymbol CorrespondingTupleField | |
{ | ||
get | ||
{ | ||
return null; | ||
return (ContainingType.IsDefinition && TupleElementIndex >= 0) ? this : null; | ||
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. That property is |
||
} | ||
} | ||
|
||
|
@@ -445,6 +446,25 @@ public virtual int TupleElementIndex | |
{ | ||
get | ||
{ | ||
if (ContainingType.IsTupleType && ContainingType.IsDefinition) | ||
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.
Why is it important that the containing type is the definition? #Resolved 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. In thinking about this question, I realized it's not. This implementation should handle the underlying fields in a constructed tuple. Will add test and fix In reply to: 425344464 [](ancestors = 425344464) |
||
{ | ||
var i = NamedTypeSymbol.MatchesCanonicalElementName(Name); | ||
int itemsPerType = Math.Min(ContainingType.Arity, NamedTypeSymbol.ValueTupleRestPosition - 1); | ||
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.
Consider inlining this to only calculate when |
||
if (i > 0 && i <= itemsPerType) | ||
{ | ||
WellKnownMember wellKnownMember = NamedTypeSymbol.GetTupleTypeMember(ContainingType.Arity, i); | ||
|
||
RuntimeMembers.MemberDescriptor relativeDescriptor = WellKnownMembers.GetDescriptor(wellKnownMember); | ||
var found = CSharpCompilation.GetRuntimeMember(ImmutableArray.Create<Symbol>(this), relativeDescriptor, CSharpCompilation.SpecialMembersSignatureComparer.Instance, | ||
accessWithinOpt: null); // force lookup of public members only | ||
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. This looks expensive. Can we calculate 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. I didn't want to add state to every source or PE symbol just because it might be a tuple element. In reply to: 425346752 [](ancestors = 425346752) |
||
|
||
if (found is object) | ||
{ | ||
return i - 1; | ||
} | ||
} | ||
} | ||
|
||
return -1; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -445,7 +445,7 @@ internal static int IsTupleElementNameReserved(string name) | |
return 0; | ||
} | ||
|
||
return matchesCanonicalElementName(name); | ||
return MatchesCanonicalElementName(name); | ||
|
||
static bool isElementNameForbidden(string name) | ||
{ | ||
|
@@ -463,26 +463,27 @@ static bool isElementNameForbidden(string name) | |
return false; | ||
} | ||
} | ||
} | ||
|
||
// Returns 3 for "Item3". | ||
// Returns -1 otherwise. | ||
static int matchesCanonicalElementName(string name) | ||
/// <summary> | ||
/// Returns 3 for "Item3". | ||
/// Returns -1 otherwise. | ||
/// </summary> | ||
internal static int MatchesCanonicalElementName(string name) | ||
{ | ||
if (name.StartsWith("Item", StringComparison.Ordinal)) | ||
{ | ||
if (name.StartsWith("Item", StringComparison.Ordinal)) | ||
string tail = name.Substring(4); | ||
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. The
Unfortunately there are no overloads of #WontFix 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. Looked around but didn't find something better. Not sure I want to add one for this PR, especially since it's a pre-existing usage of In reply to: 426860653 [](ancestors = 426860653) |
||
if (int.TryParse(tail, out int number)) | ||
{ | ||
string tail = name.Substring(4); | ||
int number; | ||
if (int.TryParse(tail, out number)) | ||
if (number > 0 && string.Equals(name, TupleMemberName(number), StringComparison.Ordinal)) | ||
{ | ||
if (number > 0 && String.Equals(name, TupleMemberName(number), StringComparison.Ordinal)) | ||
{ | ||
return number; | ||
} | ||
return number; | ||
} | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
/// <summary> | ||
|
@@ -601,6 +602,19 @@ public sealed override ImmutableArray<FieldSymbol> TupleElements | |
continue; | ||
} | ||
|
||
if (this.IsDefinition) | ||
{ | ||
// No need to wrap fields on System.ValueTuple definitions | ||
members.Add(field); | ||
int tupleFieldIndex2 = currentFieldsForElements.IndexOf(field, ReferenceEqualityComparer.Instance); | ||
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.
Consider using a 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. Never mind. This is the same lookup we were performing previously, and there are at most 8 items in the collection. In reply to: 425355278 [](ancestors = 425355278) |
||
if (tupleFieldIndex2 >= 0) | ||
{ | ||
elementsMatchedByFields[tupleFieldIndex2] = true; // mark as handled | ||
} | ||
|
||
continue; | ||
} | ||
|
||
var underlyingField = field is TupleElementFieldSymbol tupleElement ? tupleElement.UnderlyingField.OriginalDefinition : field.OriginalDefinition; | ||
int tupleFieldIndex = currentFieldsForElements.IndexOf(underlyingField, ReferenceEqualityComparer.Instance); | ||
if (underlyingField is TupleErrorFieldSymbol) | ||
|
@@ -621,11 +635,7 @@ public sealed override ImmutableArray<FieldSymbol> TupleElements | |
var providedName = elementNames.IsDefault ? null : elementNames[tupleFieldIndex]; | ||
|
||
ImmutableArray<Location> locations; | ||
if (this.IsDefinition) | ||
{ | ||
locations = member.Locations; | ||
} | ||
else if (elementLocations.IsDefault) | ||
if (elementLocations.IsDefault) | ||
{ | ||
locations = ImmutableArray<Location>.Empty; | ||
} | ||
|
@@ -1051,57 +1061,47 @@ internal SmallDictionary<Symbol, Symbol> UnderlyingDefinitionToMemberMap | |
{ | ||
get | ||
{ | ||
return _lazyUnderlyingDefinitionToMemberMap ?? | ||
(_lazyUnderlyingDefinitionToMemberMap = ComputeDefinitionToMemberMap()); | ||
} | ||
} | ||
return _lazyUnderlyingDefinitionToMemberMap ??= computeDefinitionToMemberMap(); | ||
|
||
private SmallDictionary<Symbol, Symbol> ComputeDefinitionToMemberMap() | ||
{ | ||
var map = new SmallDictionary<Symbol, Symbol>(ReferenceEqualityComparer.Instance); | ||
var members = TupleUnderlyingType.GetMembers(); | ||
|
||
// Go in reverse because we want members with default name, which precede the ones with | ||
// friendly names, to be in the map. | ||
for (int i = members.Length - 1; i >= 0; i--) | ||
{ | ||
var member = members[i]; | ||
switch (member.Kind) | ||
SmallDictionary<Symbol, Symbol> computeDefinitionToMemberMap() | ||
{ | ||
case SymbolKind.Method: | ||
case SymbolKind.Property: | ||
case SymbolKind.NamedType: | ||
map.Add(member.OriginalDefinition, member); | ||
break; | ||
|
||
case SymbolKind.Field: | ||
var tupleUnderlyingField = ((FieldSymbol)member).TupleUnderlyingField; | ||
if (tupleUnderlyingField is object) | ||
{ | ||
map[tupleUnderlyingField.OriginalDefinition] = member; | ||
} | ||
break; | ||
var map = new SmallDictionary<Symbol, Symbol>(ReferenceEqualityComparer.Instance); | ||
var members = TupleUnderlyingType.GetMembers(); | ||
|
||
case SymbolKind.Event: | ||
var underlyingEvent = (EventSymbol)member; | ||
var underlyingAssociatedField = underlyingEvent.AssociatedField; | ||
// The field is not part of the members list | ||
if (underlyingAssociatedField is object) | ||
// Go in reverse because we want members with default name, which precede the ones with | ||
// friendly names, to be in the map. | ||
for (int i = members.Length - 1; i >= 0; i--) | ||
{ | ||
var member = members[i]; | ||
switch (member.Kind) | ||
{ | ||
Debug.Assert((object)underlyingAssociatedField.ContainingSymbol == TupleUnderlyingType); | ||
Debug.Assert(TupleUnderlyingType.GetMembers(underlyingAssociatedField.Name).IndexOf(underlyingAssociatedField) < 0); | ||
map.Add(underlyingAssociatedField.OriginalDefinition, new TupleFieldSymbol(TupleUnderlyingType, underlyingAssociatedField, -i - 1)); | ||
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.
It is not clear why this is no longer needed. We don't need/want to create an instance of a TupleFieldSymbol, but it feel like we still want an entry in the map if we wanted to have it in the map before this PR. #Closed 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. I'll put the logic back for completeness, but the only place we use this is in In reply to: 426810809 [](ancestors = 426810809) |
||
case SymbolKind.Method: | ||
case SymbolKind.Property: | ||
case SymbolKind.NamedType: | ||
map.Add(member.OriginalDefinition, member); | ||
break; | ||
|
||
case SymbolKind.Field: | ||
var tupleUnderlyingField = ((FieldSymbol)member).TupleUnderlyingField; | ||
if (tupleUnderlyingField is object) | ||
{ | ||
map[tupleUnderlyingField.OriginalDefinition] = member; | ||
} | ||
break; | ||
|
||
case SymbolKind.Event: | ||
var underlyingEvent = (EventSymbol)member; | ||
map.Add(underlyingEvent.OriginalDefinition, member); | ||
break; | ||
|
||
default: | ||
throw ExceptionUtilities.UnexpectedValue(member.Kind); | ||
} | ||
} | ||
|
||
map.Add(underlyingEvent.OriginalDefinition, member); | ||
break; | ||
|
||
default: | ||
throw ExceptionUtilities.UnexpectedValue(member.Kind); | ||
return map; | ||
} | ||
} | ||
|
||
return map; | ||
} | ||
|
||
public TMember? GetTupleMemberSymbolForUnderlyingMember<TMember>(TMember underlyingMemberOpt) where TMember : Symbol | ||
|
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.
Are we checking if this is a default field rather than a named field? If so, is there a more explicit way to do that check that is language-independent? Perhaps
element.CorrespondingTupleField != null
. #ResolvedThere 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've tried to think of different approaches, and tried a few, but this is really tricky.
We don't want to display the
Item1
inVT'2
(which is!IsImplicitlyDeclared
, but has aCorrespondingTupleField
).Also checks are whether fields are default are not don't work, as
(int Item1, int).Item1
is a default field even though explicitly named.So ended up sticking with this check. #ByDesign