diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs
index 36242b30704c0..b3b5048999726 100644
--- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs
+++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs
@@ -705,12 +705,14 @@ protected int GetOrCreateSlot(Symbol symbol, int containingSlot = 0)
return slot;
}
- // Descends through Rest fields of a tuple is "symbol" is an extended field
- // As a result the "symbol" will be adjusted to be the field of the innermost tuple
- // and a corresponding containingSlot is returned.
- // Return value -1 indicates a failure which could happen for the following reasons
- // a) Rest field does not exist, which could happen in rare error scenarios involving broken ValueTuple types
- // b) Rest is not tracked already and forceSlotsToExist is false (otherwise we create slots on demand)
+ ///
+ /// Descends through Rest fields of a tuple if "symbol" is an extended field
+ /// As a result the "symbol" will be adjusted to be the field of the innermost tuple
+ /// and a corresponding containingSlot is returned.
+ /// Return value -1 indicates a failure which could happen for the following reasons
+ /// a) Rest field does not exist, which could happen in rare error scenarios involving broken ValueTuple types
+ /// b) Rest is not tracked already and forceSlotsToExist is false (otherwise we create slots on demand)
+ ///
private int DescendThroughTupleRestFields(ref Symbol symbol, int containingSlot, bool forceContainingSlotsToExist)
{
var fieldSymbol = symbol as TupleFieldSymbol;
@@ -718,7 +720,7 @@ private int DescendThroughTupleRestFields(ref Symbol symbol, int containingSlot,
{
TypeSymbol containingType = ((TupleTypeSymbol)symbol.ContainingType).UnderlyingNamedType;
- // for tuple fields the varible indentifier represents the underlying field
+ // for tuple fields the variable identifier represents the underlying field
symbol = fieldSymbol.TupleUnderlyingField;
// descend through Rest fields
diff --git a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs
index 18039e0cd514e..ad45f65443a25 100644
--- a/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/FieldSymbol.cs
@@ -385,7 +385,7 @@ public virtual bool IsTupleField
}
///
- /// Returns Ture when field symbol is not mapped directly to a field in the underlying tuple struct.
+ /// Returns True when field symbol is not mapped directly to a field in the underlying tuple struct.
///
internal virtual bool IsVirtualTupleField
{
diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs
index 018b501a5deda..f59606a387f44 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleFieldSymbol.cs
@@ -15,7 +15,7 @@ internal class TupleFieldSymbol : WrappedFieldSymbol
protected readonly TupleTypeSymbol _containingTuple;
///
- /// If this field represents a tuple element (including the name match),
+ /// If this field represents a tuple element,
/// id is an index of the element (zero-based).
/// Otherwise, (-1 - [index in members array]);
///
@@ -40,7 +40,7 @@ public override bool IsTupleField
}
///
- /// If this field represents a tuple element (including the name match),
+ /// If this field represents a tuple element,
/// id is an index of the element (zero-based).
/// Otherwise, (-1 - [index in members array]);
/// i
@@ -103,22 +103,27 @@ internal override DiagnosticInfo GetUseSiteDiagnostic()
public override sealed int GetHashCode()
{
- return Hash.Combine(_containingTuple.GetHashCode(), _tupleFieldId.GetHashCode());
+ return Hash.Combine(
+ Hash.Combine(_containingTuple.GetHashCode(), _tupleFieldId.GetHashCode()),
+ this.Name.GetHashCode());
}
public override sealed bool Equals(object obj)
{
- return Equals(obj as TupleFieldSymbol);
- }
+ var other = obj as TupleFieldSymbol;
- public bool Equals(TupleFieldSymbol other)
- {
if ((object)other == this)
{
return true;
}
- return (object)other != null && _tupleFieldId == other._tupleFieldId && _containingTuple == other._containingTuple;
+ // note we have to compare both index and name because
+ // in nameless tuple there could be fields that differ only by index
+ // and in named tupoles there could be fields that differ only by name
+ return (object)other != null &&
+ _tupleFieldId == other.TupleElementIndex &&
+ _containingTuple == other._containingTuple &&
+ this.Name == other.Name;
}
}
diff --git a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs
index 5565f6496e01b..43af58b1f7fce 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Tuples/TupleTypeSymbol.cs
@@ -556,6 +556,15 @@ internal static int IsElementNameReserved(string name)
return 0;
}
+ return MatchesCanonicalElementName(name);
+ }
+
+ ///
+ /// Returns 3 for "Item3".
+ /// Returns -1 otherwise.
+ ///
+ private static int MatchesCanonicalElementName(string name)
+ {
if (name.StartsWith("Item", StringComparison.Ordinal))
{
string tail = name.Substring(4);
@@ -724,13 +733,13 @@ private ImmutableArray CollectTupleElementFields()
continue;
}
- int index = (member as TupleFieldSymbol)?.TupleElementIndex ??
- ((TupleErrorFieldSymbol)member).TupleElementIndex;
+ var field = (FieldSymbol)member;
+ var index = field.TupleElementIndex;
- if (index >= 0)
+ if (index >= 0 && index == MatchesCanonicalElementName(field.Name) - 1)
{
Debug.Assert((object)builder[index] == null);
- builder[index] = (FieldSymbol)member;
+ builder[index] = field;
}
}
@@ -815,7 +824,7 @@ private ImmutableArray CreateMembers()
{
// The name given doesn't match default name Item8, etc.
// Add a field with default name and make it virtual since we are not at the top level
- members.Add(new TupleVirtualElementFieldSymbol(this, fieldSymbol, defaultName, -members.Count - 1, null));
+ members.Add(new TupleVirtualElementFieldSymbol(this, fieldSymbol, defaultName, tupleFieldIndex, null));
}
// Add a field with the given name
@@ -832,7 +841,7 @@ private ImmutableArray CreateMembers()
else
{
// Add a field with default name
- members.Add(new TupleFieldSymbol(this, fieldSymbol, -members.Count - 1));
+ members.Add(new TupleFieldSymbol(this, fieldSymbol, tupleFieldIndex));
// Add a field with the given name
if (namesOfVirtualFields[tupleFieldIndex] != null)
diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs
index 1559605585590..02ed530302f8f 100644
--- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs
+++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs
@@ -8630,9 +8630,9 @@ static void Main()
var m2Item1 = (FieldSymbol)m2Tuple.GetMembers()[0];
var m2a2 = (FieldSymbol)m2Tuple.GetMembers()[1];
- Assert.IsType(m1Item1);
- Assert.IsType(m2Item1);
- Assert.IsType(m2a2);
+ AssertNonvirtualTupleElementField(m1Item1);
+ AssertNonvirtualTupleElementField(m2Item1);
+ AssertVirtualTupleElementField(m2a2);
Assert.True(m1Item1.IsTupleField);
Assert.Same(m1Item1, m1Item1.OriginalDefinition);
@@ -8879,7 +8879,7 @@ static void Main()
var m3Item8 = (FieldSymbol)m3Tuple.GetMembers("Item8").Single();
- Assert.IsType(m3Item8);
+ AssertVirtualTupleElementField(m3Item8);
Assert.True(m3Item8.IsTupleField);
Assert.Same(m3Item8, m3Item8.OriginalDefinition);
@@ -9073,7 +9073,7 @@ static void Main()
var m4Item8 = (FieldSymbol)m4Tuple.GetMembers("Item8").Single();
- Assert.IsType(m4Item8);
+ AssertVirtualTupleElementField(m4Item8);
Assert.True(m4Item8.IsTupleField);
Assert.Same(m4Item8, m4Item8.OriginalDefinition);
@@ -9092,7 +9092,7 @@ static void Main()
var m4h4 = (FieldSymbol)m4Tuple.GetMembers("h4").Single();
- Assert.IsType(m4h4);
+ AssertVirtualTupleElementField(m4h4);
Assert.True(m4h4.IsTupleField);
Assert.Same(m4h4, m4h4.OriginalDefinition);
@@ -9321,7 +9321,7 @@ static void Main()
var m5Item8 = (FieldSymbol)m5Tuple.GetMembers("Item8").Single();
- Assert.IsType(m5Item8);
+ AssertVirtualTupleElementField(m5Item8);
Assert.True(m5Item8.IsTupleField);
Assert.Same(m5Item8, m5Item8.OriginalDefinition);
@@ -9691,7 +9691,7 @@ static void Main()
var m8Item8 = (FieldSymbol)m8Tuple.GetMembers("Item8").Single();
- Assert.IsType(m8Item8);
+ AssertVirtualTupleElementField(m8Item8);
Assert.True(m8Item8.IsTupleField);
Assert.Same(m8Item8, m8Item8.OriginalDefinition);
@@ -9711,7 +9711,7 @@ static void Main()
var m8Item1 = (FieldSymbol)m8Tuple.GetMembers("Item1").Last();
- Assert.IsType(m8Item1);
+ AssertVirtualTupleElementField(m8Item1);
Assert.True(m8Item1.IsTupleField);
Assert.Same(m8Item1, m8Item1.OriginalDefinition);
@@ -9890,9 +9890,9 @@ static void Main()
var m2Item1 = (FieldSymbol)m2Tuple.GetMembers()[0];
var m2a2 = (FieldSymbol)m2Tuple.GetMembers()[1];
- Assert.IsType(m1Item1);
- Assert.IsType(m2Item1);
- Assert.IsType(m2a2);
+ AssertNonvirtualTupleElementField(m1Item1);
+ AssertNonvirtualTupleElementField(m2Item1);
+ AssertVirtualTupleElementField(m2a2);
Assert.True(m1Item1.IsTupleField);
Assert.Same(m1Item1, m1Item1.OriginalDefinition);
@@ -10226,9 +10226,9 @@ static void Test03()
var m102Item20 = (FieldSymbol)m102Tuple.GetMembers("Item20").Single();
var m102a = (FieldSymbol)m102Tuple.GetMembers("a").Single();
- Assert.IsType(m10Item1);
- Assert.IsType(m102Item20);
- Assert.IsType(m102a);
+ AssertNonvirtualTupleElementField(m10Item1);
+ AssertTupleNonElementField(m102Item20);
+ AssertVirtualTupleElementField(m102a);
Assert.Equal("System.ObsoleteAttribute", m10Item1.GetAttributes().Single().ToString());
Assert.Equal("System.ObsoleteAttribute", m102Item20.GetAttributes().Single().ToString());
@@ -10239,10 +10239,10 @@ static void Test03()
var m102Item2 = (FieldSymbol)m102Tuple.GetMembers("Item2").Single();
var m102b = (FieldSymbol)m102Tuple.GetMembers("b").Single();
- Assert.IsType(m10Item2);
- Assert.IsType(m102Item2);
- Assert.IsType(m102Item21);
- Assert.IsType(m102b);
+ AssertNonvirtualTupleElementField(m10Item2);
+ AssertNonvirtualTupleElementField(m102Item2);
+ AssertTupleNonElementField(m102Item21);
+ AssertVirtualTupleElementField(m102b);
Assert.Equal(20, m10Item2.TypeLayoutOffset);
Assert.Equal(20, m102Item2.TypeLayoutOffset);
@@ -10256,8 +10256,8 @@ static void Test03()
var m103Item2 = (FieldSymbol)m103Tuple.GetMembers("Item2").Last();
var m103Item9 = (FieldSymbol)m103Tuple.GetMembers("Item9").Single();
- Assert.IsType(m103Item2);
- Assert.IsType(m103Item9);
+ AssertVirtualTupleElementField(m103Item2);
+ AssertVirtualTupleElementField(m103Item9);
Assert.Null(m103Item2.TypeLayoutOffset);
Assert.Equal(20, m103Item2.TupleUnderlyingField.TypeLayoutOffset);
Assert.Null(m103Item9.TypeLayoutOffset);
@@ -10296,6 +10296,36 @@ static void Test03()
Assert.Equal("System.ObsoleteAttribute", m10E2.GetAttributes().Single().ToString());
}
+ private void AssertTupleNonElementField(FieldSymbol sym)
+ {
+ Assert.True(sym.IsTupleField);
+ Assert.False(sym.IsVirtualTupleField);
+
+ //it is not an element so index must be negative
+ Assert.True(sym.TupleElementIndex < 0);
+ }
+
+ private void AssertVirtualTupleElementField(FieldSymbol sym)
+ {
+ Assert.True(sym.IsTupleField);
+ Assert.True(sym.IsVirtualTupleField);
+
+ //it is an element so must have nonnegative index
+ Assert.True(sym.TupleElementIndex >= 0);
+ }
+
+ private void AssertNonvirtualTupleElementField(FieldSymbol sym)
+ {
+ Assert.True(sym.IsTupleField);
+ Assert.False(sym.IsVirtualTupleField);
+
+ //it is an element so must have nonnegative index
+ Assert.True(sym.TupleElementIndex >= 0);
+
+ //if it was 8th or after, it would be virtual
+ Assert.True(sym.TupleElementIndex < TupleTypeSymbol.RestPosition - 1);
+ }
+
[Fact]
public void CustomValueTupleWithGenericMethod()
{