/// <summary>
        /// Converts the expression for creating a tuple instance into an expression creating a ValueTuple (if short) or nested ValueTuples (if longer).
        ///
        /// For instance, for a long tuple we'll generate:
        /// creationExpression(ctor=largestCtor, args=firstArgs+(nested creationExpression for remainder, with smaller ctor and next few args))
        /// </summary>
        private BoundNode RewriteTupleCreationExpression(BoundTupleExpression node, ImmutableArray<BoundExpression> rewrittenArguments)
        {
            NamedTypeSymbol underlyingTupleType = node.Type.TupleUnderlyingType;

            ArrayBuilder<NamedTypeSymbol> underlyingTupleTypeChain = ArrayBuilder<NamedTypeSymbol>.GetInstance();
            TupleTypeSymbol.GetUnderlyingTypeChain(underlyingTupleType, underlyingTupleTypeChain);

            try
            {
                // make a creation expression for the smallest type
                NamedTypeSymbol smallestType = underlyingTupleTypeChain.Pop();
                ImmutableArray<BoundExpression> smallestCtorArguments = ImmutableArray.Create(rewrittenArguments,
                                                                                              underlyingTupleTypeChain.Count * (TupleTypeSymbol.RestPosition - 1),
                                                                                              smallestType.Arity);
                var smallestCtor = (MethodSymbol)TupleTypeSymbol.GetWellKnownMemberInType(smallestType.OriginalDefinition,
                                                                                            TupleTypeSymbol.GetTupleCtor(smallestType.Arity),
                                                                                            _diagnostics,
                                                                                            node.Syntax);
                if ((object)smallestCtor == null)
                {
                    return node;
                }

                MethodSymbol smallestConstructor = smallestCtor.AsMember(smallestType);
                BoundObjectCreationExpression currentCreation = new BoundObjectCreationExpression(node.Syntax, smallestConstructor, smallestCtorArguments);

                if (underlyingTupleTypeChain.Count > 0)
                {
                    NamedTypeSymbol tuple8Type = underlyingTupleTypeChain.Peek();
                    var tuple8Ctor = (MethodSymbol)TupleTypeSymbol.GetWellKnownMemberInType(tuple8Type.OriginalDefinition,
                                                                                            TupleTypeSymbol.GetTupleCtor(TupleTypeSymbol.RestPosition),
                                                                                            _diagnostics,
                                                                                            node.Syntax);
                    if ((object)tuple8Ctor == null)
                    {
                        return node;
                    }

                    // make successively larger creation expressions containing the previous one
                    do
                    {
                        ImmutableArray<BoundExpression> ctorArguments = ImmutableArray.Create(rewrittenArguments,
                                                                                              (underlyingTupleTypeChain.Count - 1) * (TupleTypeSymbol.RestPosition - 1),
                                                                                              TupleTypeSymbol.RestPosition - 1)
                                                                                      .Add(currentCreation);

                        MethodSymbol constructor = tuple8Ctor.AsMember(underlyingTupleTypeChain.Pop());
                        currentCreation = new BoundObjectCreationExpression(node.Syntax, constructor, ctorArguments);
                    }
                    while (underlyingTupleTypeChain.Count > 0);
                }

                return currentCreation;
            }
            finally
            {
                underlyingTupleTypeChain.Free();
            }
        }
        /// <summary>
        /// Converts the expression for creating a tuple instance into an expression creating a ValueTuple (if short) or nested ValueTuples (if longer).
        ///
        /// For instance, for a long tuple we'll generate:
        /// creationExpression(ctor=largestCtor, args=firstArgs+(nested creationExpression for remainder, with smaller ctor and next few args))
        /// </summary>
        private BoundNode RewriteTupleCreationExpression(BoundTupleExpression node, ImmutableArray <BoundExpression> rewrittenArguments)
        {
            NamedTypeSymbol underlyingTupleType = node.Type.TupleUnderlyingType;

            ArrayBuilder <NamedTypeSymbol> underlyingTupleTypeChain = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            TupleTypeSymbol.GetUnderlyingTypeChain(underlyingTupleType, underlyingTupleTypeChain);

            try
            {
                // make a creation expression for the smallest type
                NamedTypeSymbol smallestType = underlyingTupleTypeChain.Pop();
                ImmutableArray <BoundExpression> smallestCtorArguments = ImmutableArray.Create(rewrittenArguments,
                                                                                               underlyingTupleTypeChain.Count * (TupleTypeSymbol.RestPosition - 1),
                                                                                               smallestType.Arity);
                var smallestCtor = (MethodSymbol)TupleTypeSymbol.GetWellKnownMemberInType(smallestType.OriginalDefinition,
                                                                                          TupleTypeSymbol.GetTupleCtor(smallestType.Arity),
                                                                                          _diagnostics,
                                                                                          node.Syntax);
                if ((object)smallestCtor == null)
                {
                    return(node);
                }

                MethodSymbol smallestConstructor = smallestCtor.AsMember(smallestType);
                BoundObjectCreationExpression currentCreation = new BoundObjectCreationExpression(node.Syntax, smallestConstructor, smallestCtorArguments);

                if (underlyingTupleTypeChain.Count > 0)
                {
                    NamedTypeSymbol tuple8Type = underlyingTupleTypeChain.Peek();
                    var             tuple8Ctor = (MethodSymbol)TupleTypeSymbol.GetWellKnownMemberInType(tuple8Type.OriginalDefinition,
                                                                                                        TupleTypeSymbol.GetTupleCtor(TupleTypeSymbol.RestPosition),
                                                                                                        _diagnostics,
                                                                                                        node.Syntax);
                    if ((object)tuple8Ctor == null)
                    {
                        return(node);
                    }

                    // make successively larger creation expressions containing the previous one
                    do
                    {
                        ImmutableArray <BoundExpression> ctorArguments = ImmutableArray.Create(rewrittenArguments,
                                                                                               (underlyingTupleTypeChain.Count - 1) * (TupleTypeSymbol.RestPosition - 1),
                                                                                               TupleTypeSymbol.RestPosition - 1)
                                                                         .Add(currentCreation);

                        MethodSymbol constructor = tuple8Ctor.AsMember(underlyingTupleTypeChain.Pop());
                        currentCreation = new BoundObjectCreationExpression(node.Syntax, constructor, ctorArguments);
                    }while (underlyingTupleTypeChain.Count > 0);
                }

                return(currentCreation);
            }
            finally
            {
                underlyingTupleTypeChain.Free();
            }
        }
        /// <summary>
        /// The left represents a tree of L-values. The structure of right can be missing parts of the tree on the left.
        /// The conversion holds nested conversions and deconstruction information, which matches the tree from the left,
        /// and it provides the information to fill in the missing parts of the tree from the right and convert it to
        /// the tree from the left.
        ///
        /// A bound sequence is returned which has different phases of side-effects:
        /// - the initialization phase includes side-effects from the left, followed by evaluations of the right
        /// - the deconstruction phase includes all the invocations of Deconstruct methods and tuple element accesses below a Deconstruct call
        /// - the conversion phase
        /// - the assignment phase
        /// </summary>
        private BoundExpression?RewriteDeconstruction(BoundTupleExpression left, Conversion conversion, BoundExpression right, bool isUsed)
        {
            var lhsTemps = ArrayBuilder <LocalSymbol> .GetInstance();

            var lhsEffects = ArrayBuilder <BoundExpression> .GetInstance();

            ArrayBuilder <Binder.DeconstructionVariable> lhsTargets = GetAssignmentTargetsAndSideEffects(left, lhsTemps, lhsEffects);

            Debug.Assert(left.Type is { });
Example #4
0
        private bool IsLikeTupleExpression(BoundExpression expr, out BoundTupleExpression tuple)
        {
            switch (expr)
            {
            case BoundTupleExpression t:
                tuple = t;
                return(true);

            case BoundConversion {
                    Conversion: { Kind : ConversionKind.Identity }, Operand : var o
            } :
Example #5
0
        private void CheckForDeconstructionAssignmentToSelf(
            BoundTupleExpression leftTuple,
            BoundExpression right
            )
        {
            while (right.Kind == BoundKind.Conversion)
            {
                var conversion = (BoundConversion)right;
                switch (conversion.ConversionKind)
                {
                case ConversionKind.Deconstruction:
                case ConversionKind.ImplicitTupleLiteral:
                case ConversionKind.Identity:
                    right = conversion.Operand;
                    break;

                default:
                    return;
                }
            }

            if (
                right.Kind != BoundKind.ConvertedTupleLiteral &&
                right.Kind != BoundKind.TupleLiteral
                )
            {
                return;
            }

            var rightTuple    = (BoundTupleExpression)right;
            var leftArguments = leftTuple.Arguments;
            int length        = leftArguments.Length;

            Debug.Assert(length == rightTuple.Arguments.Length);

            for (int i = 0; i < length; i++)
            {
                var leftArgument  = leftArguments[i];
                var rightArgument = rightTuple.Arguments[i];

                if (leftArgument is BoundTupleExpression tupleExpression)
                {
                    CheckForDeconstructionAssignmentToSelf(tupleExpression, rightArgument);
                }
                else if (IsSameLocalOrField(leftArgument, rightArgument))
                {
                    Error(ErrorCode.WRN_AssignmentToSelf, leftArgument);
                }
            }
        }
Example #6
0
        /// <summary>
        /// The left represents a tree of L-values. The structure of right can be missing parts of the tree on the left.
        /// The conversion holds nested conversisons and deconstruction information, which matches the tree from the left,
        /// and it provides the information to fill in the missing parts of the tree from the right and convert it to
        /// the tree from the left.
        ///
        /// A bound sequence is returned which has different phases of side-effects:
        /// - the initialization phase includes side-effects from the left, followed by evaluations of the right
        /// - the deconstruction phase includes all the invocations of Deconstruct methods and tuple element accesses below a Deconstruct call
        /// - the conversion phase
        /// - the assignment phase
        /// </summary>
        private BoundExpression RewriteDeconstruction(BoundTupleExpression left, Conversion conversion, BoundExpression right)
        {
            var temps = ArrayBuilder <LocalSymbol> .GetInstance();

            var effects = DeconstructionSideEffects.GetInstance();
            ArrayBuilder <Binder.DeconstructionVariable> lhsTargets = GetAssignmentTargetsAndSideEffects(left, temps, effects.init);

            BoundExpression returnTuple = ApplyDeconstructionConversion(lhsTargets, right, conversion, temps, effects, inInit: true);

            if (!returnTuple.HasErrors)
            {
                returnTuple = VisitExpression(returnTuple);
            }
            BoundExpression result = _factory.Sequence(temps.ToImmutableAndFree(), effects.ToImmutableAndFree(), returnTuple);

            Binder.DeconstructionVariable.FreeDeconstructionVariables(lhsTargets);

            return(result);
        }
        /// <summary>
        /// The left represents a tree of L-values. The structure of right can be missing parts of the tree on the left.
        /// The conversion holds nested conversions and deconstruction information, which matches the tree from the left,
        /// and it provides the information to fill in the missing parts of the tree from the right and convert it to
        /// the tree from the left.
        ///
        /// A bound sequence is returned which has different phases of side-effects:
        /// - the initialization phase includes side-effects from the left, followed by evaluations of the right
        /// - the deconstruction phase includes all the invocations of Deconstruct methods and tuple element accesses below a Deconstruct call
        /// - the conversion phase
        /// - the assignment phase
        /// </summary>
        private BoundExpression RewriteDeconstruction(BoundTupleExpression left, Conversion conversion, BoundExpression right, bool isUsed)
        {
            var lhsTemps = ArrayBuilder <LocalSymbol> .GetInstance();

            var lhsEffects = ArrayBuilder <BoundExpression> .GetInstance();

            ArrayBuilder <Binder.DeconstructionVariable> lhsTargets = GetAssignmentTargetsAndSideEffects(left, lhsTemps, lhsEffects);
            BoundExpression result = RewriteDeconstruction(lhsTargets, conversion, left.Type, right, isUsed);

            Binder.DeconstructionVariable.FreeDeconstructionVariables(lhsTargets);
            if (result is null)
            {
                lhsTemps.Free();
                lhsEffects.Free();
                return(null);
            }

            return(_factory.Sequence(lhsTemps.ToImmutableAndFree(), lhsEffects.ToImmutableAndFree(), result));
        }
        /// <summary>
        /// The left represents a tree of L-values. The structure of right can be missing parts of the tree on the left.
        /// The conversion holds nested conversions and deconstruction information, which matches the tree from the left,
        /// and it provides the information to fill in the missing parts of the tree from the right and convert it to
        /// the tree from the left.
        ///
        /// A bound sequence is returned which has different phases of side-effects:
        /// - the initialization phase includes side-effects from the left, followed by evaluations of the right
        /// - the deconstruction phase includes all the invocations of Deconstruct methods and tuple element accesses below a Deconstruct call
        /// - the conversion phase
        /// - the assignment phase
        /// </summary>
        private BoundExpression RewriteDeconstruction(BoundTupleExpression left, Conversion conversion, BoundExpression right, bool isUsed)
        {
            var temps = ArrayBuilder <LocalSymbol> .GetInstance();

            var effects = DeconstructionSideEffects.GetInstance();
            ArrayBuilder <Binder.DeconstructionVariable> lhsTargets = GetAssignmentTargetsAndSideEffects(left, temps, effects.init);

            BoundExpression returnValue = ApplyDeconstructionConversion(lhsTargets, right, conversion, temps, effects, isUsed, inInit: true);

            effects.Consolidate();

            BoundExpression result;

            if (!isUsed)
            {
                // When a deconstruction is not used, the last effect is used as return value
                Debug.Assert(returnValue is null);
                var last = effects.PopLast();
                if (last is null)
                {
                    // Deconstructions with no effects lower to nothing. For example, `(_, _) = (1, 2);`
                    result = null;
                    temps.Free();
                    _ = effects.ToImmutableAndFree();
                }
                else
                {
                    result = _factory.Sequence(temps.ToImmutableAndFree(), effects.ToImmutableAndFree(), last);
                }
            }
            else
            {
                if (!returnValue.HasErrors)
                {
                    returnValue = VisitExpression(returnValue);
                }
                result = _factory.Sequence(temps.ToImmutableAndFree(), effects.ToImmutableAndFree(), returnValue);
            }

            Binder.DeconstructionVariable.FreeDeconstructionVariables(lhsTargets);
            return(result);
        }
Example #9
0
        /// <summary>
        /// Adds the side effects to stores and returns temporaries (as a flat list) to access them.
        /// </summary>
        private ImmutableArray <BoundExpression> GetAssignmentTargetsAndSideEffects(BoundTupleExpression variables, ArrayBuilder <LocalSymbol> temps, ArrayBuilder <BoundExpression> stores)
        {
            var assignmentTargets = ArrayBuilder <BoundExpression> .GetInstance(variables.Arguments.Length);

            variables.VisitAllElements(
                (variable, args) =>
            {
                if (variable.Kind == BoundKind.DiscardExpression)
                {
                    args.targets.Add(variable);
                }
                else
                {
                    args.targets.Add(args.self.TransformCompoundAssignmentLHS(variable, args.stores, args.temps, isDynamicAssignment: variable.Type.IsDynamic()));
                }
            },
                (temps: temps, stores: stores, targets: assignmentTargets, self: this)
                );

            return(assignmentTargets.ToImmutableAndFree());
        }
 /// <summary>
 /// Converts the expression for creating a tuple instance into an expression creating a ValueTuple (if short) or nested ValueTuples (if longer).
 ///
 /// For instance, for a long tuple we'll generate:
 /// creationExpression(ctor=largestCtor, args=firstArgs+(nested creationExpression for remainder, with smaller ctor and next few args))
 /// </summary>
 private BoundExpression RewriteTupleCreationExpression(BoundTupleExpression node, ImmutableArray <BoundExpression> rewrittenArguments)
 {
     return(MakeTupleCreationExpression(node.Syntax, (NamedTypeSymbol)node.Type, rewrittenArguments));
 }
        private BoundNode VisitTupleExpression(BoundTupleExpression node)
        {
            ImmutableArray <BoundExpression> rewrittenArguments = VisitList(node.Arguments);

            return(RewriteTupleCreationExpression(node, rewrittenArguments));
        }
Example #12
0
        /// <summary>
        /// Adds the side effects to effects and returns temporaries to access them.
        /// The caller is responsible for releasing the nested ArrayBuilders.
        /// The variables should be unlowered.
        /// </summary>
        private ArrayBuilder <Binder.DeconstructionVariable> GetAssignmentTargetsAndSideEffects(BoundTupleExpression variables, ArrayBuilder <LocalSymbol> temps, ArrayBuilder <BoundExpression> effects)
        {
            var assignmentTargets = ArrayBuilder <Binder.DeconstructionVariable> .GetInstance(variables.Arguments.Length);

            foreach (var variable in variables.Arguments)
            {
                switch (variable.Kind)
                {
                case BoundKind.DiscardExpression:
                    assignmentTargets.Add(new Binder.DeconstructionVariable(variable, variable.Syntax));
                    break;

                case BoundKind.TupleLiteral:
                    var tuple = (BoundTupleExpression)variable;
                    assignmentTargets.Add(new Binder.DeconstructionVariable(GetAssignmentTargetsAndSideEffects(tuple, temps, effects), tuple.Syntax));
                    break;

                case BoundKind.ConvertedTupleLiteral:
                    throw ExceptionUtilities.UnexpectedValue(variable.Kind);

                default:
                    var temp = this.TransformCompoundAssignmentLHS(variable, effects, temps, isDynamicAssignment: variable.Type.IsDynamic());
                    assignmentTargets.Add(new Binder.DeconstructionVariable(temp, variable.Syntax));
                    break;
                }
            }

            return(assignmentTargets);
        }
 /// <summary>
 /// Converts the expression for creating a tuple instance into an expression creating a ValueTuple (if short) or nested ValueTuples (if longer).
 ///
 /// For instance, for a long tuple we'll generate:
 /// creationExpression(ctor=largestCtor, args=firstArgs+(nested creationExpression for remainder, with smaller ctor and next few args))
 /// </summary>
 private BoundExpression RewriteTupleCreationExpression(BoundTupleExpression node, ImmutableArray <BoundExpression> rewrittenArguments)
 {
     Debug.Assert(node.Type is { });
 private BoundNode VisitTupleExpression(BoundTupleExpression node)
 {
     ImmutableArray<BoundExpression> rewrittenArguments = VisitList(node.Arguments);
     return RewriteTupleCreationExpression(node, rewrittenArguments);
 }
 /// <summary>
 /// Converts the expression for creating a tuple instance into an expression creating a ValueTuple (if short) or nested ValueTuples (if longer).
 ///
 /// For instance, for a long tuple we'll generate:
 /// creationExpression(ctor=largestCtor, args=firstArgs+(nested creationExpression for remainder, with smaller ctor and next few args))
 /// </summary>
 private BoundExpression RewriteTupleCreationExpression(BoundTupleExpression node, ImmutableArray<BoundExpression> rewrittenArguments)
 {
     return MakeTupleCreationExpression(node.Syntax, (NamedTypeSymbol)node.Type, rewrittenArguments);
 }