public BoundDeconstructValuePlaceholder SetInferredType(TypeSymbol type, bool success)
        {
            Debug.Assert((object)Placeholder == null);

            Placeholder = new BoundDeconstructValuePlaceholder(this.Syntax, type, hasErrors: this.HasErrors || !success);
            return Placeholder;
        }
Example #2
0
        /// <summary>
        /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple.
        ///
        /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps.
        /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do.
        /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables.
        /// </summary>
        private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
            CSharpSyntaxNode node,
            ExpressionSyntax right,
            ArrayBuilder <DeconstructionVariable> checkedVariables,
            DiagnosticBag diagnostics,
            BoundDeconstructValuePlaceholder rhsPlaceholder = null)
        {
            // receiver for first Deconstruct step
            var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue);

            boundRHS = FixTupleLiteral(checkedVariables, boundRHS, node, diagnostics);

            if ((object)boundRHS.Type == null || boundRHS.Type.IsErrorType())
            {
                // we could still not infer a type for the RHS
                FailRemainingInferences(checkedVariables, diagnostics);

                return(new BoundDeconstructionAssignmentOperator(
                           node, FlattenDeconstructVariables(checkedVariables), boundRHS,
                           ImmutableArray <BoundDeconstructionDeconstructStep> .Empty,
                           ImmutableArray <BoundDeconstructionAssignmentStep> .Empty,
                           ImmutableArray <BoundDeconstructionAssignmentStep> .Empty,
                           ImmutableArray <BoundDeconstructionConstructionStep> .Empty,
                           GetSpecialType(SpecialType.System_Void, diagnostics, node),
                           hasErrors: true));
            }

            var deconstructionSteps = ArrayBuilder <BoundDeconstructionDeconstructStep> .GetInstance(1);

            var conversionSteps = ArrayBuilder <BoundDeconstructionAssignmentStep> .GetInstance(1);

            var assignmentSteps = ArrayBuilder <BoundDeconstructionAssignmentStep> .GetInstance(1);

            var constructionStepsOpt = ArrayBuilder <BoundDeconstructionConstructionStep> .GetInstance(1);

            bool hasErrors = !DeconstructIntoSteps(
                new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type),
                node,
                diagnostics,
                checkedVariables,
                deconstructionSteps,
                conversionSteps,
                assignmentSteps,
                constructionStepsOpt);

            TypeSymbol returnType = hasErrors ?
                                    CreateErrorType() :
                                    constructionStepsOpt.Last().OutputPlaceholder.Type;

            var deconstructions = deconstructionSteps.ToImmutableAndFree();
            var conversions     = conversionSteps.ToImmutableAndFree();
            var assignments     = assignmentSteps.ToImmutableAndFree();
            var constructions   = constructionStepsOpt.ToImmutableAndFree();

            FailRemainingInferences(checkedVariables, diagnostics);

            return(new BoundDeconstructionAssignmentOperator(
                       node, FlattenDeconstructVariables(checkedVariables), boundRHS,
                       deconstructions, conversions, assignments, constructions, returnType, hasErrors: hasErrors));
        }
Example #3
0
        /// <summary>
        /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple.
        ///
        /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps.
        /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do.
        /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables.
        /// </summary>
        private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(
                                                        CSharpSyntaxNode node,
                                                        ExpressionSyntax right,
                                                        ArrayBuilder<DeconstructionVariable> checkedVariables,
                                                        DiagnosticBag diagnostics,
                                                        bool isDeclaration,
                                                        BoundDeconstructValuePlaceholder rhsPlaceholder = null)
        {
            // receiver for first Deconstruct step
            var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue);

            boundRHS = FixTupleLiteral(checkedVariables, boundRHS, node, diagnostics);

            if ((object)boundRHS.Type == null)
            {
                // we could still not infer a type for the RHS
                FailRemainingInferences(checkedVariables, diagnostics);

                return new BoundDeconstructionAssignmentOperator(
                            node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS,
                            ImmutableArray<BoundDeconstructionDeconstructStep>.Empty,
                            ImmutableArray<BoundDeconstructionAssignmentStep>.Empty,
                            ImmutableArray<BoundDeconstructionAssignmentStep>.Empty,
                            ImmutableArray<BoundDeconstructionConstructionStep>.Empty,
                            GetSpecialType(SpecialType.System_Void, diagnostics, node),
                            hasErrors: true);
            }

            var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1);
            var conversionSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1);
            var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1);
            var constructionStepsOpt = isDeclaration ? null : ArrayBuilder<BoundDeconstructionConstructionStep>.GetInstance(1);

            bool hasErrors = !DeconstructIntoSteps(
                                    new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type),
                                    node,
                                    diagnostics,
                                    checkedVariables,
                                    deconstructionSteps,
                                    conversionSteps,
                                    assignmentSteps,
                                    constructionStepsOpt);

            TypeSymbol returnType = isDeclaration ?
                                            GetSpecialType(SpecialType.System_Void, diagnostics, node) :
                                            hasErrors ?
                                                CreateErrorType() :
                                                constructionStepsOpt.Last().OutputPlaceholder.Type;

            var deconstructions = deconstructionSteps.ToImmutableAndFree();
            var conversions = conversionSteps.ToImmutableAndFree();
            var assignments = assignmentSteps.ToImmutableAndFree();
            var constructions = isDeclaration ? default(ImmutableArray<BoundDeconstructionConstructionStep>) : constructionStepsOpt.ToImmutableAndFree();

            FailRemainingInferences(checkedVariables, diagnostics);

            return new BoundDeconstructionAssignmentOperator(
                            node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS,
                            deconstructions, conversions, assignments, constructions, returnType, hasErrors: hasErrors);
        }
Example #4
0
        /// <summary>
        /// Whether the target is a tuple or a type that requires Deconstruction, this will generate and stack appropriate deconstruction and assignment steps.
        /// Note that the variables may either be plain or nested variables.
        /// The variables may be updated with inferred types if they didn't have types initially.
        /// Returns false if there was an error.
        /// </summary>
        private bool DeconstructIntoSteps(
            BoundDeconstructValuePlaceholder targetPlaceholder,
            CSharpSyntaxNode syntax,
            DiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables,
            ArrayBuilder <BoundDeconstructionDeconstructStep> deconstructionSteps,
            ArrayBuilder <BoundDeconstructionAssignmentStep> assignmentSteps)
        {
            Debug.Assert(targetPlaceholder.Type != null);

            BoundDeconstructionDeconstructStep step;

            if (targetPlaceholder.Type.IsTupleType)
            {
                // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())`
                step = MakeTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables, deconstructionSteps, assignmentSteps);
            }
            else
            {
                step = MakeNonTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables, deconstructionSteps, assignmentSteps);
            }

            if (step == null)
            {
                return(false);
            }

            deconstructionSteps.Add(step);

            // outputs will either need a conversion step and assignment step, or if they are nested variables, they will need further deconstruction
            return(DeconstructOrAssignOutputs(step, variables, syntax, diagnostics, deconstructionSteps, assignmentSteps));
        }
        public BoundDeconstructValuePlaceholder SetInferredType(TypeSymbol type, bool success)
        {
            Debug.Assert((object)Placeholder == null);

            Placeholder = new BoundDeconstructValuePlaceholder(this.Syntax, type, hasErrors: this.HasErrors || !success);
            return(Placeholder);
        }
Example #6
0
        /// <summary>
        /// Bind a deconstruction assignment.
        /// </summary>
        /// <param name="deconstruction">The deconstruction operation</param>
        /// <param name="left">The left (tuple) operand</param>
        /// <param name="right">The right (deconstructable) operand</param>
        /// <param name="diagnostics">Where to report diagnostics</param>
        /// <param name="declaration">A variable set to the first variable declaration found in the left</param>
        /// <param name="expression">A variable set to the first expression in the left that isn't a declaration or discard</param>
        /// <param name="resultIsUsedOverride">The expression evaluator needs to bind deconstructions (both assignments and declarations) as expression-statements
        ///     and still access the returned value</param>
        /// <param name="rightPlaceholder"></param>
        /// <returns></returns>
        internal BoundDeconstructionAssignmentOperator BindDeconstruction(
            CSharpSyntaxNode deconstruction,
            ExpressionSyntax left,
            ExpressionSyntax right,
            DiagnosticBag diagnostics,
            ref DeclarationExpressionSyntax declaration,
            ref ExpressionSyntax expression,
            bool resultIsUsedOverride = false,
            BoundDeconstructValuePlaceholder rightPlaceholder = null)
        {
            DeconstructionVariable locals = BindDeconstructionVariables(left, diagnostics, ref declaration, ref expression);

            Debug.Assert(locals.HasNestedVariables);

            var             deconstructionDiagnostics = new DiagnosticBag();
            BoundExpression boundRight = rightPlaceholder ?? BindValue(right, deconstructionDiagnostics, BindValueKind.RValue);

            boundRight = FixTupleLiteral(locals.NestedVariables, boundRight, deconstruction, deconstructionDiagnostics);

            bool resultIsUsed = resultIsUsedOverride || IsDeconstructionResultUsed(left);
            var  assignment   = BindDeconstructionAssignment(deconstruction, left, boundRight, locals.NestedVariables, resultIsUsed, deconstructionDiagnostics);

            DeconstructionVariable.FreeDeconstructionVariables(locals.NestedVariables);

            diagnostics.AddRange(deconstructionDiagnostics);
            return(assignment);
        }
        public BoundDeconstructValuePlaceholder SetInferredType(TypeSymbolWithAnnotations type, Binder binder, bool success)
        {
            Debug.Assert(Placeholder is null);

            // The val escape scope for this placeholder won't be used, so defaulting to narrowest scope
            Placeholder = new BoundDeconstructValuePlaceholder(this.Syntax, binder.LocalScopeDepth, type.TypeSymbol, hasErrors: this.HasErrors || !success);
            return(Placeholder);
        }
Example #8
0
 internal DeconstructMethodInfo(
     BoundExpression invocation,
     BoundDeconstructValuePlaceholder inputPlaceholder,
     ImmutableArray <BoundDeconstructValuePlaceholder> outputPlaceholders
     )
 {
     (Invocation, InputPlaceholder, OutputPlaceholders) = (
         invocation,
         inputPlaceholder,
         outputPlaceholders
         );
 }
Example #9
0
 internal BoundDeconstructionAssignmentOperator BindDeconstruction(
                                                     CSharpSyntaxNode node,
                                                     ExpressionSyntax left,
                                                     ExpressionSyntax right,
                                                     DiagnosticBag diagnostics,
                                                     bool isDeclaration,
                                                     BoundDeconstructValuePlaceholder rightPlaceholder = null)
 {
     DeconstructionVariable locals = BindDeconstructionVariables(left, isDeclaration, diagnostics);
     Debug.Assert(locals.HasNestedVariables);
     var result = BindDeconstructionAssignment(node, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder);
     FreeDeconstructionVariables(locals.NestedVariables);
     return result;
 }
Example #10
0
        /// <summary>
        /// Figures out how to assign from sourceType into receivingVariable and bundles the information (leaving holes for the actual source and receiver) into an AssignmentInfo.
        /// </summary>
        private BoundDeconstructionAssignmentStep MakeDeconstructionAssignmentStep(
            BoundExpression receivingVariable, TypeSymbol sourceType, BoundDeconstructValuePlaceholder inputPlaceholder,
            CSharpSyntaxNode node, DiagnosticBag diagnostics)
        {
            var outputPlaceholder = new BoundDeconstructValuePlaceholder(receivingVariable.Syntax, receivingVariable.Type)
            {
                WasCompilerGenerated = true
            };

            // each assignment has a placeholder for a receiver and another for the source
            BoundAssignmentOperator op = BindAssignment(receivingVariable.Syntax, outputPlaceholder, inputPlaceholder, diagnostics);

            return(new BoundDeconstructionAssignmentStep(node, op, inputPlaceholder, outputPlaceholder));
        }
Example #11
0
        internal BoundDeconstructionAssignmentOperator BindDeconstruction(
            CSharpSyntaxNode node,
            ExpressionSyntax left,
            ExpressionSyntax right,
            DiagnosticBag diagnostics,
            bool isDeclaration,
            BoundDeconstructValuePlaceholder rightPlaceholder = null)
        {
            DeconstructionVariable locals = BindDeconstructionVariables(left, isDeclaration, diagnostics);

            Debug.Assert(locals.HasNestedVariables);
            var result = BindDeconstructionAssignment(node, right, locals.NestedVariables, diagnostics, rhsPlaceholder: rightPlaceholder);

            FreeDeconstructionVariables(locals.NestedVariables);
            return(result);
        }
Example #12
0
        /// <summary>
        /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple.
        ///
        /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps.
        /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do.
        /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables.
        /// </summary>
        private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder<DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null)
        {
            TypeSymbol voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node);

            // receiver for first Deconstruct step
            var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue);

            if ((object)boundRHS.Type == null)
            {
                if (boundRHS.Kind == BoundKind.TupleLiteral)
                {
                    // Let's fix the literal up by figuring out its type
                    // For declarations, that means merging type information from the LHS and RHS
                    // For assignments, only the LHS side matters since it is necessarily typed
                    TypeSymbol lhsAsTuple = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, node, Compilation, diagnostics);
                    if ((object)lhsAsTuple != null)
                    {
                        boundRHS = GenerateConversionForAssignment(lhsAsTuple, boundRHS, diagnostics);
                    }
                }
                else
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, right);
                }

                if ((object)boundRHS.Type == null)
                {
                    // we could still not infer a type for the RHS
                    FailRemainingInferences(checkedVariables, diagnostics);

                    return new BoundDeconstructionAssignmentOperator(
                                node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS,
                                ImmutableArray<BoundDeconstructionDeconstructStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty,
                                voidType, hasErrors: true);
                }
            }
            var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1);
            var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1);
            bool hasErrors = !DeconstructIntoSteps(new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, assignmentSteps);

            var deconstructions = deconstructionSteps.ToImmutableAndFree();
            var assignments = assignmentSteps.ToImmutableAndFree();

            FailRemainingInferences(checkedVariables, diagnostics);
            return new BoundDeconstructionAssignmentOperator(node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, assignments, voidType, hasErrors: hasErrors);
        }
Example #13
0
        /// <summary>
        /// This will generate and stack appropriate deconstruction and assignment steps for a non-tuple type.
        /// Returns null if there was an error (if a suitable Deconstruct method was not found).
        /// </summary>
        private BoundDeconstructionDeconstructStep MakeNonTupleDeconstructStep(
            BoundDeconstructValuePlaceholder targetPlaceholder,
            CSharpSyntaxNode syntax,
            DiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables)
        {
            // symbol and parameters for Deconstruct
            ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders;
            var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, targetPlaceholder, syntax, diagnostics, out outPlaceholders);

            if (deconstructInvocation.HasAnyErrors)
            {
                return(null);
            }

            SetInferredTypes(variables, outPlaceholders.SelectAsArray(p => p.Type), diagnostics);

            return(new BoundDeconstructionDeconstructStep(syntax, deconstructInvocation, targetPlaceholder, outPlaceholders));
        }
Example #14
0
        /// <summary>
        /// The produces a deconstruction step with no Deconstruct method since the tuple already has distinct elements.
        /// </summary>
        private BoundDeconstructionDeconstructStep MakeTupleDeconstructStep(
            BoundDeconstructValuePlaceholder targetPlaceholder,
            CSharpSyntaxNode syntax,
            DiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables)
        {
            Debug.Assert(targetPlaceholder.Type.IsTupleType);

            var tupleTypes = targetPlaceholder.Type.TupleElementTypes;

            SetInferredTypes(variables, tupleTypes, diagnostics);

            if (variables.Count != tupleTypes.Length)
            {
                Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleTypes.Length, variables.Count);
                return(null);
            }

            return(new BoundDeconstructionDeconstructStep(syntax, null, targetPlaceholder, tupleTypes.SelectAsArray((t, i, v) => new BoundDeconstructValuePlaceholder(v[i].Syntax, t), variables)));
        }
Example #15
0
        private BoundDeconstructionConstructionStep MakeDeconstructionConstructionStep(CSharpSyntaxNode node, DiagnosticBag diagnostics,
                                                                                       ImmutableArray <BoundDeconstructValuePlaceholder> constructionInputs)
        {
            var tuple = TupleTypeSymbol.Create(locationOpt: null,
                                               elementTypes: constructionInputs.SelectAsArray(e => e.Type),
                                               elementLocations: default(ImmutableArray <Location>),
                                               elementNames: default(ImmutableArray <string>),
                                               compilation: Compilation,
                                               diagnostics: diagnostics,
                                               syntax: node);

            var outputPlaceholder = new BoundDeconstructValuePlaceholder(node, tuple)
            {
                WasCompilerGenerated = true
            };

            BoundExpression construction = new BoundTupleLiteral(node, default(ImmutableArray <string>), constructionInputs.CastArray <BoundExpression>(), tuple);

            return(new BoundDeconstructionConstructionStep(node, construction, outputPlaceholder));
        }
Example #16
0
        /// <summary>
        /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple.
        ///
        /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps.
        /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do.
        /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables.
        /// </summary>
        private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder<DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null)
        {
            TypeSymbol voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node);

            // receiver for first Deconstruct step
            var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue);

            if ((object)boundRHS.Type == null)
            {
                if (boundRHS.Kind == BoundKind.TupleLiteral && !isDeclaration)
                {
                    // tuple literal without type such as `(null, null)`, let's fix it up by peeking at the LHS
                    TypeSymbol lhsAsTuple = MakeTupleTypeFromDeconstructionLHS(checkedVariables, diagnostics, Compilation);
                    boundRHS = GenerateConversionForAssignment(lhsAsTuple, boundRHS, diagnostics);
                }
                else
                {
                    // expression without type such as `null`
                    Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, right);
                    FailRemainingInferences(checkedVariables, diagnostics);

                    return new BoundDeconstructionAssignmentOperator(
                                node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS,
                                ImmutableArray<BoundDeconstructionDeconstructStep>.Empty, ImmutableArray<BoundDeconstructionAssignmentStep>.Empty,
                                voidType, hasErrors: true);
                }
            }

            var deconstructionSteps = ArrayBuilder<BoundDeconstructionDeconstructStep>.GetInstance(1);
            var assignmentSteps = ArrayBuilder<BoundDeconstructionAssignmentStep>.GetInstance(1);
            bool hasErrors = !DeconstructIntoSteps(new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, assignmentSteps);

            var deconstructions = deconstructionSteps.ToImmutableAndFree();
            var assignments = assignmentSteps.ToImmutableAndFree();

            FailRemainingInferences(checkedVariables, diagnostics);
            return new BoundDeconstructionAssignmentOperator(node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, assignments, voidType, hasErrors: hasErrors);
        }
Example #17
0
        /// <summary>
        /// This will generate and stack appropriate deconstruction and assignment steps for a non-tuple type.
        /// Returns null if there was an error (if a suitable Deconstruct method was not found).
        /// </summary>
        private BoundDeconstructionDeconstructStep MakeNonTupleDeconstructStep(
                                                            BoundDeconstructValuePlaceholder targetPlaceholder,
                                                            CSharpSyntaxNode syntax,
                                                            DiagnosticBag diagnostics,
                                                            ArrayBuilder<DeconstructionVariable> variables)
        {
            // symbol and parameters for Deconstruct
            ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders;
            var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count, targetPlaceholder, syntax, diagnostics, out outPlaceholders);
            if (deconstructInvocation.HasAnyErrors)
            {
                return null;
            }

            SetInferredTypes(variables, outPlaceholders.SelectAsArray(p => p.Type), diagnostics);

            return new BoundDeconstructionDeconstructStep(syntax, deconstructInvocation, targetPlaceholder, outPlaceholders);
        }
Example #18
0
        /// <summary>
        /// The produces a deconstruction step with no Deconstruct method since the tuple already has distinct elements.
        /// </summary>
        private BoundDeconstructionDeconstructStep MakeTupleDeconstructStep(
                                                        BoundDeconstructValuePlaceholder targetPlaceholder,
                                                        CSharpSyntaxNode syntax,
                                                        DiagnosticBag diagnostics,
                                                        ArrayBuilder<DeconstructionVariable> variables)
        {
            Debug.Assert(targetPlaceholder.Type.IsTupleType);

            var tupleTypes = targetPlaceholder.Type.TupleElementTypes;
            SetInferredTypes(variables, tupleTypes, diagnostics);

            if (variables.Count != tupleTypes.Length)
            {
                Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleTypes.Length, variables.Count);
                return null;
            }

            return new BoundDeconstructionDeconstructStep(syntax, null, targetPlaceholder, tupleTypes.SelectAsArray((t, i, v) => new BoundDeconstructValuePlaceholder(v[i].Syntax, t), variables));
        }
Example #19
0
        /// <summary>
        /// Whether the target is a tuple or a type that requires Deconstruction, this will generate and stack appropriate deconstruction and assignment steps.
        /// Note that the variables may either be plain or nested variables.
        /// The variables may be updated with inferred types if they didn't have types initially.
        /// Returns false if there was an error.
        /// Pass in constructionStepsOpt as null if construction steps should not be computed.
        /// </summary>
        private bool DeconstructIntoSteps(
                        BoundDeconstructValuePlaceholder targetPlaceholder,
                        CSharpSyntaxNode syntax,
                        DiagnosticBag diagnostics,
                        ArrayBuilder<DeconstructionVariable> variables,
                        ArrayBuilder<BoundDeconstructionDeconstructStep> deconstructionSteps,
                        ArrayBuilder<BoundDeconstructionAssignmentStep> conversionSteps,
                        ArrayBuilder<BoundDeconstructionAssignmentStep> assignmentSteps,
                        ArrayBuilder<BoundDeconstructionConstructionStep> constructionStepsOpt)
        {
            Debug.Assert(targetPlaceholder.Type != null);

            BoundDeconstructionDeconstructStep step;

            if (targetPlaceholder.Type.IsTupleType)
            {
                // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())`
                step = MakeTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables);
            }
            else
            {
                step = MakeNonTupleDeconstructStep(targetPlaceholder, syntax, diagnostics, variables);
            }

            if (step == null)
            {
                return false;
            }

            deconstructionSteps.Add(step);

            // outputs will either need a conversion step and assignment step, or if they are nested variables, they will need further deconstruction
            return DeconstructOrAssignOutputs(step, variables, syntax, diagnostics, deconstructionSteps, conversionSteps, assignmentSteps, constructionStepsOpt);
        }
Example #20
0
        internal BoundDeconstructionAssignmentOperator BindDeconstructionDeclaration(CSharpSyntaxNode node, VariableDeclarationSyntax declaration, ExpressionSyntax right, DiagnosticBag diagnostics, BoundDeconstructValuePlaceholder rightPlaceholder = null)
        {
            ArrayBuilder <DeconstructionVariable> locals = BindDeconstructionDeclarationLocals(declaration, declaration.Type, diagnostics);

            var result = BindDeconstructionAssignment(node, right, locals, diagnostics, isDeclaration: true, rhsPlaceholder: rightPlaceholder);

            FreeDeconstructionVariables(locals);

            return(result);
        }
Example #21
0
        /// <summary>
        /// There are two kinds of deconstruction-assignments which this binding handles: tuple and non-tuple.
        ///
        /// Returns a BoundDeconstructionAssignmentOperator with a list of deconstruction steps and assignment steps.
        /// Deconstruct steps for tuples have no invocation to Deconstruct, but steps for non-tuples do.
        /// The caller is responsible for releasing all the ArrayBuilders in checkedVariables.
        /// </summary>
        private BoundDeconstructionAssignmentOperator BindDeconstructionAssignment(CSharpSyntaxNode node, ExpressionSyntax right, ArrayBuilder <DeconstructionVariable> checkedVariables, DiagnosticBag diagnostics, bool isDeclaration, BoundDeconstructValuePlaceholder rhsPlaceholder = null)
        {
            TypeSymbol voidType = GetSpecialType(SpecialType.System_Void, diagnostics, node);

            // receiver for first Deconstruct step
            var boundRHS = rhsPlaceholder ?? BindValue(right, diagnostics, BindValueKind.RValue);

            if ((object)boundRHS.Type == null)
            {
                if (boundRHS.Kind == BoundKind.TupleLiteral)
                {
                    // Let's fix the literal up by figuring out its type
                    // For declarations, that means merging type information from the LHS and RHS
                    // For assignments, only the LHS side matters since it is necessarily typed
                    TypeSymbol lhsAsTuple = MakeMergedTupleType(checkedVariables, (BoundTupleLiteral)boundRHS, node, Compilation, diagnostics);
                    if ((object)lhsAsTuple != null)
                    {
                        boundRHS = GenerateConversionForAssignment(lhsAsTuple, boundRHS, diagnostics);
                    }
                }
                else
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructRequiresExpression, right);
                }

                if ((object)boundRHS.Type == null)
                {
                    // we could still not infer a type for the RHS
                    FailRemainingInferences(checkedVariables, diagnostics);

                    return(new BoundDeconstructionAssignmentOperator(
                               node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS,
                               ImmutableArray <BoundDeconstructionDeconstructStep> .Empty, ImmutableArray <BoundDeconstructionAssignmentStep> .Empty,
                               voidType, hasErrors: true));
                }
            }
            var deconstructionSteps = ArrayBuilder <BoundDeconstructionDeconstructStep> .GetInstance(1);

            var assignmentSteps = ArrayBuilder <BoundDeconstructionAssignmentStep> .GetInstance(1);

            bool hasErrors = !DeconstructIntoSteps(new BoundDeconstructValuePlaceholder(boundRHS.Syntax, boundRHS.Type), node, diagnostics, checkedVariables, deconstructionSteps, assignmentSteps);

            var deconstructions = deconstructionSteps.ToImmutableAndFree();
            var assignments     = assignmentSteps.ToImmutableAndFree();

            FailRemainingInferences(checkedVariables, diagnostics);
            return(new BoundDeconstructionAssignmentOperator(node, isDeclaration, FlattenDeconstructVariables(checkedVariables), boundRHS, deconstructions, assignments, voidType, hasErrors: hasErrors));
        }
Example #22
0
        /// <summary>
        /// Like BindForEachParts, but only bind the deconstruction part of the foreach, for purpose of inferring the types of the declared locals.
        /// </summary>
        internal override BoundStatement BindForEachDeconstruction(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);

            VariableComponentSyntax variables = ((ForEachComponentStatementSyntax)_syntax).VariableComponent;
            var valuePlaceholder = new BoundDeconstructValuePlaceholder(_syntax.Expression, inferredType ?? CreateErrorType("var"));
            BoundDeconstructionAssignmentOperator deconstruction = BindDeconstructionDeclaration(
                                                                    variables,
                                                                    variables,
                                                                    right: null,
                                                                    diagnostics: diagnostics,
                                                                    rightPlaceholder: valuePlaceholder);
            return new BoundExpressionStatement(_syntax, deconstruction);
        }
Example #23
0
        /// <summary>
        /// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary
        /// Deconstruct method and any element-wise conversion.
        ///
        /// Note that the variables may either be plain or nested variables.
        /// The variables may be updated with inferred types if they didn't have types initially.
        /// Returns false if there was an error.
        /// </summary>
        private bool MakeDeconstructionConversion(
            TypeSymbol type,
            SyntaxNode syntax,
            SyntaxNode rightSyntax,
            DiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables,
            out Conversion conversion)
        {
            Debug.Assert(type != null);
            ImmutableArray <TypeSymbol> tupleOrDeconstructedTypes;

            conversion = Conversion.Deconstruction;

            // Figure out the deconstruct method (if one is required) and determine the types we get from the RHS at this level
            var deconstructMethod = default(DeconstructMethodInfo);

            if (type.IsTupleType)
            {
                // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())`
                tupleOrDeconstructedTypes = type.TupleElementTypes;
                SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);

                if (variables.Count != tupleOrDeconstructedTypes.Length)
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleOrDeconstructedTypes.Length, variables.Count);
                    return(false);
                }
            }
            else
            {
                ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders;
                var inputPlaceholder      = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type);
                var deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count,
                                                                                inputPlaceholder, rightSyntax, diagnostics, out outPlaceholders);

                if (deconstructInvocation.HasAnyErrors)
                {
                    return(false);
                }

                deconstructMethod = new DeconstructMethodInfo(deconstructInvocation, inputPlaceholder, outPlaceholders);

                tupleOrDeconstructedTypes = outPlaceholders.SelectAsArray(p => p.Type);
                SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);
            }

            // Figure out whether those types will need conversions, including further deconstructions
            bool hasErrors = false;

            int count             = variables.Count;
            var nestedConversions = ArrayBuilder <Conversion> .GetInstance(count);

            for (int i = 0; i < count; i++)
            {
                var variable = variables[i];

                Conversion nestedConversion;
                if (variable.HasNestedVariables)
                {
                    var elementSyntax = syntax.Kind() == SyntaxKind.TupleExpression ? ((TupleExpressionSyntax)syntax).Arguments[i] : syntax;

                    hasErrors |= !MakeDeconstructionConversion(tupleOrDeconstructedTypes[i], syntax, rightSyntax, diagnostics,
                                                               variable.NestedVariables, out nestedConversion);
                }
                else
                {
                    var single = variable.Single;
                    HashSet <DiagnosticInfo> useSiteDiagnostics = null;
                    nestedConversion = this.Conversions.ClassifyConversionFromType(tupleOrDeconstructedTypes[i], single.Type, ref useSiteDiagnostics);
                    diagnostics.Add(single.Syntax, useSiteDiagnostics);

                    if (!nestedConversion.IsImplicit)
                    {
                        hasErrors = true;
                        GenerateImplicitConversionError(diagnostics, Compilation, single.Syntax, nestedConversion, tupleOrDeconstructedTypes[i], single.Type);
                    }
                }
                nestedConversions.Add(nestedConversion);
            }

            conversion = new Conversion(ConversionKind.Deconstruction, deconstructMethod, nestedConversions.ToImmutableAndFree());

            return(!hasErrors);
        }
Example #24
0
        private BoundDeconstructionConstructionStep MakeDeconstructionConstructionStep(CSharpSyntaxNode node, DiagnosticBag diagnostics,
                                                        ImmutableArray<BoundDeconstructValuePlaceholder> constructionInputs)
        {
            var tuple = TupleTypeSymbol.Create(locationOpt: null,
                           elementTypes: constructionInputs.SelectAsArray(e => e.Type),
                           elementLocations: default(ImmutableArray<Location>),
                           elementNames: default(ImmutableArray<string>),
                           compilation: Compilation,
                           diagnostics: diagnostics,
                           syntax: node);

            var outputPlaceholder = new BoundDeconstructValuePlaceholder(node, tuple) { WasCompilerGenerated = true };

            BoundExpression construction = new BoundTupleLiteral(node, default(ImmutableArray<string>), constructionInputs.CastArray<BoundExpression>(), tuple);
            return new BoundDeconstructionConstructionStep(node, construction, outputPlaceholder);
        }
Example #25
0
 public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node)
 {
     Fail(node);
     return(null);
 }
Example #26
0
        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);
        }
Example #27
0
        /// <summary>
        /// Figures out how to assign from inputPlaceholder into receivingVariable and bundles the information (leaving holes for the actual source and receiver) into an AssignmentInfo.
        /// </summary>
        private BoundDeconstructionAssignmentStep MakeDeconstructionAssignmentStep(
                                                    BoundExpression receivingVariable, BoundDeconstructValuePlaceholder inputPlaceholder,
                                                    CSharpSyntaxNode node, DiagnosticBag diagnostics)
        {
            var outputPlaceholder = new BoundDeconstructValuePlaceholder(receivingVariable.Syntax, receivingVariable.Type) { WasCompilerGenerated = true };

            // each assignment has a placeholder for a receiver and another for the source
            BoundAssignmentOperator op = BindAssignment(receivingVariable.Syntax, outputPlaceholder, inputPlaceholder, diagnostics);

            return new BoundDeconstructionAssignmentStep(node, op, outputPlaceholder);
        }
Example #28
0
 public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node)
 {
     return(PlaceholderReplacement(node));
 }
Example #29
0
        internal BoundDeconstructionAssignmentOperator BindDeconstructionDeclaration(CSharpSyntaxNode node, VariableDeclarationSyntax declaration, ExpressionSyntax right, DiagnosticBag diagnostics, BoundDeconstructValuePlaceholder rightPlaceholder = null)
        {
            ArrayBuilder<DeconstructionVariable> locals = BindDeconstructionDeclarationLocals(declaration, declaration.Type, diagnostics);

            var result = BindDeconstructionAssignment(node, right, locals, diagnostics, isDeclaration: true, rhsPlaceholder: rightPlaceholder);
            FreeDeconstructionVariables(locals);

            return result;
        }
Example #30
0
        /// <summary>
        /// Recursively builds a Conversion object with Kind=Deconstruction including information about any necessary
        /// Deconstruct method and any element-wise conversion.
        ///
        /// Note that the variables may either be plain or nested variables.
        /// The variables may be updated with inferred types if they didn't have types initially.
        /// Returns false if there was an error.
        /// </summary>
        private bool MakeDeconstructionConversion(
            TypeSymbol type,
            SyntaxNode syntax,
            SyntaxNode rightSyntax,
            BindingDiagnosticBag diagnostics,
            ArrayBuilder <DeconstructionVariable> variables,
            out Conversion conversion)
        {
            Debug.Assert((object)type != null);
            ImmutableArray <TypeSymbol> tupleOrDeconstructedTypes;

            conversion = Conversion.Deconstruction;

            // Figure out the deconstruct method (if one is required) and determine the types we get from the RHS at this level
            var deconstructMethod = default(DeconstructMethodInfo);

            if (type.IsTupleType)
            {
                // tuple literal such as `(1, 2)`, `(null, null)`, `(x.P, y.M())`
                tupleOrDeconstructedTypes = type.TupleElementTypesWithAnnotations.SelectAsArray(TypeMap.AsTypeSymbol);
                SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);

                if (variables.Count != tupleOrDeconstructedTypes.Length)
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructWrongCardinality, syntax, tupleOrDeconstructedTypes.Length, variables.Count);
                    return(false);
                }
            }
            else
            {
                if (variables.Count < 2)
                {
                    Error(diagnostics, ErrorCode.ERR_DeconstructTooFewElements, syntax);
                    return(false);
                }

                var             inputPlaceholder      = new BoundDeconstructValuePlaceholder(syntax, this.LocalScopeDepth, type);
                BoundExpression deconstructInvocation = MakeDeconstructInvocationExpression(variables.Count,
                                                                                            inputPlaceholder, rightSyntax, diagnostics, outPlaceholders: out ImmutableArray <BoundDeconstructValuePlaceholder> outPlaceholders, out _);

                if (deconstructInvocation.HasAnyErrors)
                {
                    return(false);
                }

                deconstructMethod = new DeconstructMethodInfo(deconstructInvocation, inputPlaceholder, outPlaceholders);

                tupleOrDeconstructedTypes = outPlaceholders.SelectAsArray(p => p.Type);
                SetInferredTypes(variables, tupleOrDeconstructedTypes, diagnostics);
            }

            // Figure out whether those types will need conversions, including further deconstructions
            bool hasErrors = false;

            int count             = variables.Count;
            var nestedConversions = ArrayBuilder <(BoundValuePlaceholder?, BoundExpression?)> .GetInstance(count);

            for (int i = 0; i < count; i++)
            {
                var variable = variables[i];

                Conversion nestedConversion;
                if (variable.NestedVariables is object)
                {
                    var elementSyntax = syntax.Kind() == SyntaxKind.TupleExpression ? ((TupleExpressionSyntax)syntax).Arguments[i] : syntax;

                    hasErrors |= !MakeDeconstructionConversion(tupleOrDeconstructedTypes[i], elementSyntax, rightSyntax, diagnostics,
                                                               variable.NestedVariables, out nestedConversion);

                    Debug.Assert(nestedConversion.Kind == ConversionKind.Deconstruction);
                    var operandPlaceholder = new BoundValuePlaceholder(syntax, ErrorTypeSymbol.UnknownResultType).MakeCompilerGenerated();
                    nestedConversions.Add((operandPlaceholder, new BoundConversion(syntax, operandPlaceholder, nestedConversion,
                                                                                   @checked: false, explicitCastInCode: false,
                                                                                   conversionGroupOpt: null, constantValueOpt: null,
#pragma warning disable format
                                                                                   type: ErrorTypeSymbol.UnknownResultType)
                    {
                        WasCompilerGenerated = true
                    }));