提交 2c6e8fa2 编写于 作者: V VSadov

Foreach deconstruction should mark iteration variables as assigned.

Fixes:#16106
上级 ea309794
......@@ -277,6 +277,16 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics);
// NOTE: in error cases, binder may collect all kind of variables, not just formally declared iteration variables.
// As a matter of error recovery, we will treat such variables the same as the iteration variables.
// I.E. - they will be considered declared and assigned in each iteration step.
ImmutableArray<LocalSymbol> iterationVariables = this.Locals;
Debug.Assert(hasErrors ||
_syntax.HasErrors ||
iterationVariables.All(local => local.DeclarationKind == LocalDeclarationKind.ForEachIterationVariable),
"Should not have iteration variables that are not ForEachIterationVariable in valid code");
hasErrors = hasErrors || boundIterationVariableType.HasErrors || iterationVariableType.IsErrorType();
// Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts).
......@@ -287,7 +297,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
null, // can't be sure that it's complete
default(Conversion),
boundIterationVariableType,
this.IterationVariable,
iterationVariables,
collectionExpr,
deconstructStep,
body,
......@@ -379,7 +389,7 @@ private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics,
builder.Build(this.Flags),
elementConversion,
boundIterationVariableType,
this.IterationVariable,
iterationVariables,
convertedCollectionExpression,
deconstructStep,
body,
......
......@@ -875,7 +875,7 @@
<!-- This is so the binding API can find produce semantic info if the type is "var". -->
<!-- If there is a deconstruction, there will be no iteration variable (but we'll still have a type for it). -->
<Field Name="IterationVariableType" Type="BoundTypeExpression"/>
<Field Name="IterationVariableOpt" Type="LocalSymbol" Null="allow"/>
<Field Name="IterationVariables" Type="ImmutableArray&lt;LocalSymbol&gt;"/>
<!-- If this node does not have errors, then this is the foreach expression wrapped
in a conversion to the collection type used by the foreach loop. The conversion
is here so that the binding API can return the correct ConvertedType in semantic
......
......@@ -262,7 +262,9 @@ public override void Accept(OperationVisitor visitor)
internal partial class BoundForEachStatement : IForEachLoopStatement
{
ILocalSymbol IForEachLoopStatement.IterationVariable => this.IterationVariableOpt;
ILocalSymbol IForEachLoopStatement.IterationVariable => this.DeconstructionOpt == null?
this.IterationVariables.FirstOrDefault():
null;
IOperation IForEachLoopStatement.Collection => this.Expression;
......
......@@ -1222,16 +1222,6 @@ protected virtual void AssignImpl(BoundNode node, BoundExpression value, RefKind
AssignImpl(((BoundRangeVariable)node).Value, value, refKind, written, read);
break;
case BoundKind.ForEachStatement:
{
var iterationVariable = ((BoundForEachStatement)node).IterationVariableOpt;
Debug.Assert((object)iterationVariable != null);
int slot = GetOrCreateSlot(iterationVariable);
if (slot > 0) SetSlotState(slot, written);
if (written) NoteWrite(iterationVariable, value, read);
break;
}
case BoundKind.BadExpression:
{
// Sometimes a bad node is not so bad that we cannot analyze it at all.
......@@ -1582,6 +1572,16 @@ public override BoundNode VisitForStatement(BoundForStatement node)
return result;
}
public override BoundNode VisitForEachStatement(BoundForEachStatement node)
{
// NOTE: iteration variables are not declared or assigned
// before the collection expression is evaluated
var result = base.VisitForEachStatement(node);
// NOTE: do not report unused iteration variables. They are always considered used.
return result;
}
public override BoundNode VisitDoStatement(BoundDoStatement node)
{
DeclareVariables(node.Locals);
......@@ -1598,12 +1598,6 @@ public override BoundNode VisitWhileStatement(BoundWhileStatement node)
return result;
}
public override BoundNode VisitForEachStatement(BoundForEachStatement node)
{
var result = base.VisitForEachStatement(node);
return result;
}
public override BoundNode VisitIfStatement(BoundIfStatement node)
{
var result = base.VisitIfStatement(node);
......@@ -2186,14 +2180,15 @@ public override BoundNode VisitEventAccess(BoundEventAccess node)
return result;
}
public override void VisitForEachIterationVariable(BoundForEachStatement node)
public override void VisitForEachIterationVariables(BoundForEachStatement node)
{
var local = node.IterationVariableOpt;
if ((object)local != null)
// declare and assign all iteration variables
foreach (var iterationVariable in node.IterationVariables)
{
GetOrCreateSlot(local);
Assign(node, value: null);
// TODO: node needed? NoteRead(local); // Never warn about unused foreach variables.
Debug.Assert((object)iterationVariable != null);
int slot = GetOrCreateSlot(iterationVariable);
if (slot > 0) SetSlotAssigned(slot);
NoteWrite(iterationVariable, null, read: true);
}
}
......
......@@ -147,11 +147,6 @@ private Symbol GetNodeSymbol(BoundNode node)
return local?.DeclarationKind == LocalDeclarationKind.CatchVariable ? local : null;
}
case BoundKind.ForEachStatement:
{
return ((BoundForEachStatement)node).IterationVariableOpt;
}
case BoundKind.RangeVariable:
{
return ((BoundRangeVariable)node).RangeVariableSymbol;
......
......@@ -2118,7 +2118,7 @@ public override BoundNode VisitForEachStatement(BoundForEachStatement node)
VisitRvalue(node.Expression);
var breakState = this.State.Clone();
LoopHead(node);
VisitForEachIterationVariable(node);
VisitForEachIterationVariables(node);
VisitStatement(node.Body);
ResolveContinues(node.ContinueLabel);
LoopTail(node);
......@@ -2126,7 +2126,7 @@ public override BoundNode VisitForEachStatement(BoundForEachStatement node)
return null;
}
public virtual void VisitForEachIterationVariable(BoundForEachStatement node)
public virtual void VisitForEachIterationVariables(BoundForEachStatement node)
{
}
......
......@@ -213,16 +213,6 @@ public override BoundNode VisitUnboundLambda(UnboundLambda node)
return VisitLambda(node.BindForErrorRecovery());
}
public override void VisitForEachIterationVariable(BoundForEachStatement node)
{
var local = node.IterationVariableOpt;
if ((object)local != null)
{
GetOrCreateSlot(local);
Assign(node, value: null);
}
}
public override BoundNode VisitRangeVariable(BoundRangeVariable node)
{
// Compute the "underlying symbol" for a read of the range variable
......
......@@ -122,16 +122,22 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen
return base.VisitLocalFunctionStatement(node);
}
public override void VisitForEachIterationVariable(BoundForEachStatement node)
public override void VisitForEachIterationVariables(BoundForEachStatement node)
{
if (IsInside)
{
if (node.IterationVariableOpt != null)
var deconstructionAssignment = node.DeconstructionOpt?.DeconstructionAssignment;
if (deconstructionAssignment == null)
{
_variablesDeclared.Add(node.IterationVariableOpt);
// regular, not deconstructing, foreach declares exactly one iteration variable
_variablesDeclared.Add(node.IterationVariables.Single());
}
else
{
// deconstruction foreach declares multiple variables
deconstructionAssignment.Left.VisitAllElements((x, self) => self.Visit(x), this);
}
node.DeconstructionOpt?.DeconstructionAssignment.Left.VisitAllElements((x, self) => self.Visit(x), this);
}
}
......
......@@ -96,9 +96,6 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
InstrumentForEachStatementCollectionVarDeclaration(node, ref enumeratorVarDecl);
// V v
LocalSymbol iterationVar = node.IterationVariableOpt;
//(V)(T)e.Current
BoundExpression iterationVarAssignValue = MakeConversionNode(
syntax: forEachSyntax,
......@@ -117,8 +114,8 @@ private BoundStatement RewriteEnumeratorForEachStatement(BoundForEachStatement n
// V v = (V)(T)e.Current; -OR- (D1 d1, ...) = (V)(T)e.Current;
ImmutableArray<LocalSymbol> iterationVariables;
BoundStatement iterationVarDecl = LocalOrDeconstructionDeclaration(node, iterationVar, iterationVarAssignValue, out iterationVariables);
ImmutableArray<LocalSymbol> iterationVariables = node.IterationVariables;
BoundStatement iterationVarDecl = LocalOrDeconstructionDeclaration(node, iterationVariables, iterationVarAssignValue);
InstrumentForEachStatementIterationVarDeclaration(node, ref iterationVarDecl);
......@@ -436,7 +433,6 @@ private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node)
// p = p + 1;
BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar, intType);
LocalSymbol iterationVar = node.IterationVariableOpt;
Debug.Assert(node.ElementConversion.IsValid);
// (V)s.Chars[p]
......@@ -453,8 +449,8 @@ private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node)
@checked: node.Checked);
// V v = (V)s.Chars[p]; /* OR */ (D1 d1, ...) = (V)s.Chars[p];
ImmutableArray<LocalSymbol> iterationVariables;
BoundStatement iterationVarDecl = LocalOrDeconstructionDeclaration(node, iterationVar, iterationVarInitValue, out iterationVariables);
ImmutableArray<LocalSymbol> iterationVariables = node.IterationVariables;
BoundStatement iterationVarDecl = LocalOrDeconstructionDeclaration(node, iterationVariables, iterationVarInitValue);
InstrumentForEachStatementIterationVarDeclaration(node, ref iterationVarDecl);
......@@ -494,9 +490,8 @@ private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node)
/// </summary>
private BoundStatement LocalOrDeconstructionDeclaration(
BoundForEachStatement forEachBound,
LocalSymbol iterationVar,
BoundExpression iterationVarValue,
out ImmutableArray<LocalSymbol> iterationVariables)
ImmutableArray<LocalSymbol> iterationVariables,
BoundExpression iterationVarValue)
{
var forEachSyntax = (CommonForEachStatementSyntax)forEachBound.Syntax;
......@@ -506,8 +501,8 @@ private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node)
if (deconstruction == null)
{
// V v = /* expression */
iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarValue);
iterationVariables = ImmutableArray.Create(iterationVar);
Debug.Assert(iterationVariables.Length == 1);
iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVariables[0], iterationVarValue);
}
else
{
......@@ -518,8 +513,6 @@ private BoundStatement RewriteStringForEachStatement(BoundForEachStatement node)
BoundExpression loweredAssignment = VisitExpression(assignment);
iterationVarDecl = new BoundExpressionStatement(assignment.Syntax, loweredAssignment);
RemovePlaceholderReplacement(deconstruction.TargetPlaceholder);
iterationVariables = GetLocalSymbols(assignment.Left);
}
return iterationVarDecl;
......@@ -613,9 +606,6 @@ private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEac
BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar,
MakeLiteral(forEachSyntax, ConstantValue.Default(SpecialType.System_Int32), intType));
// V v
LocalSymbol iterationVar = node.IterationVariableOpt;
// (V)a[p]
BoundExpression iterationVarInitValue = MakeConversionNode(
syntax: forEachSyntax,
......@@ -629,8 +619,8 @@ private BoundStatement RewriteSingleDimensionalArrayForEachStatement(BoundForEac
@checked: node.Checked);
// V v = (V)a[p]; /* OR */ (D1 d1, ...) = (V)a[p];
ImmutableArray<LocalSymbol> iterationVariables;
BoundStatement iterationVariableDecl = LocalOrDeconstructionDeclaration(node, iterationVar, iterationVarInitValue, out iterationVariables);
ImmutableArray<LocalSymbol> iterationVariables = node.IterationVariables;
BoundStatement iterationVariableDecl = LocalOrDeconstructionDeclaration(node, iterationVariables, iterationVarInitValue);
InstrumentForEachStatementIterationVarDeclaration(node, ref iterationVariableDecl);
......@@ -771,9 +761,6 @@ private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEach
boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType);
}
// V v
LocalSymbol iterationVar = node.IterationVariableOpt;
// (V)a[p_0, p_1, ...]
BoundExpression iterationVarInitValue = MakeConversionNode(
syntax: forEachSyntax,
......@@ -787,8 +774,8 @@ private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEach
// V v = (V)a[p_0, p_1, ...]; /* OR */ (D1 d1, ...) = (V)a[p_0, p_1, ...];
ImmutableArray<LocalSymbol> iterationVariables;
BoundStatement iterationVarDecl = LocalOrDeconstructionDeclaration(node, iterationVar, iterationVarInitValue, out iterationVariables);
ImmutableArray<LocalSymbol> iterationVariables = node.IterationVariables;
BoundStatement iterationVarDecl = LocalOrDeconstructionDeclaration(node, iterationVariables, iterationVarInitValue);
InstrumentForEachStatementIterationVarDeclaration(node, ref iterationVarDecl);
......
......@@ -6151,5 +6151,46 @@ public static void Main()
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "10");
}
[Fact]
[WorkItem(16106, "https://github.com/dotnet/roslyn/issues/16106")]
public void DefAssignmentsStruct001()
{
string source = @"
using System.Collections.Generic;
public class MyClass
{
public static void Main()
{
((int, int), string)[] arr = new((int, int), string)[1];
Test5(arr);
}
public static void Test4(IEnumerable<(KeyValuePair<int, int>, string)> en)
{
foreach ((KeyValuePair<int, int> kv, string s) in en)
{
var a = kv.Key; // false error CS0170: Use of possibly unassigned field
}
}
public static void Test5(IEnumerable<((int, int), string)> en)
{
foreach (((int, int k) t, string s) in en)
{
var a = t.k; // false error CS0170: Use of possibly unassigned field
System.Console.WriteLine(a);
}
}
}";
var compilation = CreateCompilationWithMscorlib(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe);
compilation.VerifyDiagnostics();
CompileAndVerify(compilation, expectedOutput: "0");
}
}
}
......@@ -1204,7 +1204,7 @@ void Foo(int[] a)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int32 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int32 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal(SymbolKind.ArrayType, ((BoundConversion)boundNode.Expression).Operand.Type.Kind);
}
......@@ -1236,7 +1236,7 @@ void Foo(string s)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal("System.Char c", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Char c", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_String, boundNode.Expression.Type.SpecialType);
Assert.Equal(SpecialType.System_String, ((BoundConversion)boundNode.Expression).Operand.Type.SpecialType);
}
......@@ -1279,7 +1279,7 @@ class Enumerator
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1322,7 +1322,7 @@ struct Enumerator
Assert.Equal(ConversionKind.Boxing, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1354,7 +1354,7 @@ void Foo(System.Collections.IEnumerable e)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Unboxing, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("System.Collections.IEnumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("System.Collections.IEnumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1393,7 +1393,7 @@ class Enumerable : System.Collections.Generic.IEnumerable<int>
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("System.Collections.Generic.IEnumerable<System.Int32>", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1434,7 +1434,7 @@ private class Hidden { }
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal("System.Object x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Object x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1472,7 +1472,7 @@ class Enumerable : System.Collections.IEnumerable
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Unboxing, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1504,7 +1504,7 @@ void Foo(int[] a)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal(SpecialType.System_Int32, boundNode.IterationVariableOpt.Type.SpecialType);
Assert.Equal(SpecialType.System_Int32, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
......@@ -1534,7 +1534,7 @@ void Foo(string s)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal(SpecialType.System_Char, boundNode.IterationVariableOpt.Type.SpecialType);
Assert.Equal(SpecialType.System_Char, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
......@@ -1563,7 +1563,7 @@ class Enumerator
var boundNode = GetBoundForEachStatement(text);
Assert.NotNull(boundNode.EnumeratorInfoOpt);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal(SpecialType.System_Int32, boundNode.IterationVariableOpt.Type.SpecialType);
Assert.Equal(SpecialType.System_Int32, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
......@@ -1587,7 +1587,7 @@ class Enumerable : System.Collections.IEnumerable
var boundNode = GetBoundForEachStatement(text);
Assert.NotNull(boundNode.EnumeratorInfoOpt);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal(SpecialType.System_Object, boundNode.IterationVariableOpt.Type.SpecialType);
Assert.Equal(SpecialType.System_Object, boundNode.IterationVariables.Single().Type.SpecialType);
}
[Fact]
......@@ -1619,7 +1619,7 @@ class var { }
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal("C.var", boundNode.IterationVariableOpt.Type.ToTestDisplayString());
Assert.Equal("C.var", boundNode.IterationVariables.Single().Type.ToTestDisplayString());
}
[Fact]
......@@ -1649,7 +1649,7 @@ void Foo(dynamic d)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.ExplicitDynamic, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int32 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int32 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal(TypeKind.Dynamic, ((BoundConversion)boundNode.Expression).Operand.Type.TypeKind);
}
......@@ -1681,7 +1681,7 @@ void Foo(dynamic d)
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind); //NB: differs from explicit case
Assert.Equal("dynamic x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("dynamic x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal(SpecialType.System_Collections_IEnumerable, boundNode.Expression.Type.SpecialType);
Assert.Equal(SymbolKind.DynamicType, ((BoundConversion)boundNode.Expression).Operand.Type.Kind);
}
......@@ -1721,7 +1721,7 @@ public class Enumerable<T>
Assert.Equal(ConversionKind.Boxing, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal("System.Object x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Object x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable<T>", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable<T>", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1804,7 +1804,7 @@ interface MyEnumerator
Assert.Equal(ConversionKind.Boxing, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal("System.Object x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Object x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable<T>", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable<T>", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -1850,7 +1850,7 @@ struct Enumerator
Assert.Equal(ConversionKind.Boxing, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, boundNode.ElementConversion.Kind);
Assert.Equal("System.Int64 x", boundNode.IterationVariableOpt.ToTestDisplayString());
Assert.Equal("System.Int64 x", boundNode.IterationVariables.Single().ToTestDisplayString());
Assert.Equal("Enumerable", boundNode.Expression.Type.ToTestDisplayString());
Assert.Equal("Enumerable", ((BoundConversion)boundNode.Expression).Operand.Type.ToTestDisplayString());
}
......@@ -3004,7 +3004,7 @@ .maxstack 2
Assert.Equal(ConversionKind.ImplicitReference, info.EnumeratorConversion.Kind);
Assert.Equal(ConversionKind.Identity, boundNode.ElementConversion.Kind);
Assert.Equal(SpecialType.System_Char, boundNode.IterationVariableOpt.Type.SpecialType);
Assert.Equal(SpecialType.System_Char, boundNode.IterationVariables.Single().Type.SpecialType);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册