public BoundForEachStatement(SyntaxNode syntax, ForEachEnumeratorInfo enumeratorInfoOpt, Conversion elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray <LocalSymbol> iterationVariables, BoundExpression expression, BoundForEachDeconstructStep deconstructionOpt, BoundStatement body, bool @checked, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) : this(syntax, enumeratorInfoOpt, elementConversion, iterationVariableType, iterationVariables, iterationErrorExpressionOpt : null, expression, deconstructionOpt, body, @checked, breakLabel, continueLabel, hasErrors) { }
private BoundForEachStatement BindForEachPartsWorker(DiagnosticBag diagnostics, Binder originalBinder) { // Use the right binder to avoid seeing iteration variable BoundExpression collectionExpr = originalBinder.GetBinder(_syntax.Expression).BindValue(_syntax.Expression, diagnostics, BindValueKind.RValue); ForEachEnumeratorInfo.Builder builder = new ForEachEnumeratorInfo.Builder(); TypeSymbol inferredType; bool hasErrors = !GetEnumeratorInfoAndInferCollectionElementType(ref builder, ref collectionExpr, diagnostics, out inferredType); // These should only occur when special types are missing or malformed. hasErrors = hasErrors || (object)builder.GetEnumeratorMethod == null || (object)builder.MoveNextMethod == null || (object)builder.CurrentPropertyGetter == null; TypeSymbol iterationVariableType; BoundTypeExpression boundIterationVariableType; bool hasNameConflicts = false; BoundForEachDeconstructStep deconstructStep = null; switch (_syntax.Kind()) { case SyntaxKind.ForEachStatement: { var node = (ForEachStatementSyntax)_syntax; // Check for local variable conflicts in the *enclosing* binder; obviously the *current* // binder has a local that matches! hasNameConflicts = originalBinder.ValidateDeclarationNameConflictsInScope(IterationVariable, diagnostics); // If the type in syntax is "var", then the type should be set explicitly so that the // Type property doesn't fail. TypeSyntax typeSyntax = node.Type; bool isVar; AliasSymbol alias; TypeSymbol declType = BindType(typeSyntax, diagnostics, out isVar, out alias); if (isVar) { iterationVariableType = inferredType ?? CreateErrorType("var"); } else { Debug.Assert((object)declType != null); iterationVariableType = declType; } boundIterationVariableType = new BoundTypeExpression(typeSyntax, alias, iterationVariableType); this.IterationVariable.SetTypeSymbol(iterationVariableType); break; } case SyntaxKind.ForEachComponentStatement: { var node = (ForEachComponentStatementSyntax)_syntax; iterationVariableType = inferredType ?? CreateErrorType("var"); var variables = node.VariableComponent; var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, iterationVariableType); BoundDeconstructionAssignmentOperator deconstruction = BindDeconstructionDeclaration( variables, variables, right: null, diagnostics: diagnostics, rightPlaceholder: valuePlaceholder); deconstructStep = new BoundForEachDeconstructStep(variables, deconstruction, valuePlaceholder); boundIterationVariableType = new BoundTypeExpression(variables, aliasOpt: null, type: iterationVariableType); break; } default: throw ExceptionUtilities.UnexpectedValue(_syntax.Kind()); } BoundStatement body = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); hasErrors = hasErrors || iterationVariableType.IsErrorType(); // Skip the conversion checks and array/enumerator differentiation if we know we have an error (except local name conflicts). if (hasErrors) { return new BoundForEachStatement( _syntax, null, // can't be sure that it's complete default(Conversion), boundIterationVariableType, this.IterationVariable, collectionExpr, deconstructStep, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); } hasErrors |= hasNameConflicts; var foreachKeyword = _syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, builder.GetEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.MoveNextMethod, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter, foreachKeyword, hasBaseReceiver: false); ReportDiagnosticsIfObsolete(diagnostics, builder.CurrentPropertyGetter.AssociatedSymbol, foreachKeyword, hasBaseReceiver: false); // We want to convert from inferredType in the array/string case and builder.ElementType in the enumerator case, // but it turns out that these are equivalent (when both are available). HashSet<DiagnosticInfo> useSiteDiagnostics = null; Conversion elementConversion = this.Conversions.ClassifyConversionFromType(inferredType, iterationVariableType, ref useSiteDiagnostics, forCast: true); if (!elementConversion.IsValid) { ImmutableArray<MethodSymbol> originalUserDefinedConversions = elementConversion.OriginalUserDefinedConversions; if (originalUserDefinedConversions.Length > 1) { diagnostics.Add(ErrorCode.ERR_AmbigUDConv, _syntax.ForEachKeyword.GetLocation(), originalUserDefinedConversions[0], originalUserDefinedConversions[1], inferredType, iterationVariableType); } else { SymbolDistinguisher distinguisher = new SymbolDistinguisher(this.Compilation, inferredType, iterationVariableType); diagnostics.Add(ErrorCode.ERR_NoExplicitConv, _syntax.ForEachKeyword.GetLocation(), distinguisher.First, distinguisher.Second); } hasErrors = true; } else { ReportDiagnosticsIfObsolete(diagnostics, elementConversion, _syntax.ForEachKeyword, hasBaseReceiver: false); } // Spec (§8.8.4): // If the type X of expression is dynamic then there is an implicit conversion from >>expression<< (not the type of the expression) // to the System.Collections.IEnumerable interface (§6.1.8). builder.CollectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, builder.CollectionType, ref useSiteDiagnostics); builder.CurrentConversion = this.Conversions.ClassifyConversionFromType(builder.CurrentPropertyGetter.ReturnType, builder.ElementType, ref useSiteDiagnostics); builder.EnumeratorConversion = this.Conversions.ClassifyConversionFromType(builder.GetEnumeratorMethod.ReturnType, GetSpecialType(SpecialType.System_Object, diagnostics, _syntax), ref useSiteDiagnostics); diagnostics.Add(_syntax.ForEachKeyword.GetLocation(), useSiteDiagnostics); // Due to the way we extracted the various types, these conversions should always be possible. // CAVEAT: if we're iterating over an array of pointers, the current conversion will fail since we // can't convert from object to a pointer type. Similarly, if we're iterating over an array of // Nullable<Error>, the current conversion will fail because we don't know if an ErrorType is a // value type. This doesn't matter in practice, since we won't actually use the enumerator pattern // when we lower the loop. Debug.Assert(builder.CollectionConversion.IsValid); Debug.Assert(builder.CurrentConversion.IsValid || (builder.ElementType.IsPointerType() && collectionExpr.Type.IsArray()) || (builder.ElementType.IsNullableType() && builder.ElementType.GetMemberTypeArgumentsNoUseSiteDiagnostics().Single().IsErrorType() && collectionExpr.Type.IsArray())); Debug.Assert(builder.EnumeratorConversion.IsValid || this.Compilation.GetSpecialType(SpecialType.System_Object).TypeKind == TypeKind.Error || !useSiteDiagnostics.IsNullOrEmpty(), "Conversions to object succeed unless there's a problem with the object type or the source type"); // If user-defined conversions could occur here, we would need to check for ObsoleteAttribute. Debug.Assert((object)builder.CollectionConversion.Method == null, "Conversion from collection expression to collection type should not be user-defined"); Debug.Assert((object)builder.CurrentConversion.Method == null, "Conversion from Current property type to element type should not be user-defined"); Debug.Assert((object)builder.EnumeratorConversion.Method == null, "Conversion from GetEnumerator return type to System.Object should not be user-defined"); // We're wrapping the collection expression in a (non-synthesized) conversion so that its converted // type (i.e. builder.CollectionType) will be available in the binding API. BoundConversion convertedCollectionExpression = new BoundConversion( collectionExpr.Syntax, collectionExpr, builder.CollectionConversion, CheckOverflowAtRuntime, false, ConstantValue.NotAvailable, builder.CollectionType); return new BoundForEachStatement( _syntax, builder.Build(this.Flags), elementConversion, boundIterationVariableType, this.IterationVariable, convertedCollectionExpression, deconstructStep, body, CheckOverflowAtRuntime, this.BreakLabel, this.ContinueLabel, hasErrors); }