/// <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 { });
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 } :
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); } } }
/// <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); }
/// <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)); }
/// <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); }