public override BoundNode VisitWithExpression(BoundWithExpression withExpr) { RoslynDebug.AssertNotNull(withExpr.CloneMethod); Debug.Assert(withExpr.CloneMethod.ParameterCount == 0); Debug.Assert(withExpr.Receiver.Type !.Equals(withExpr.Type, TypeCompareKind.ConsiderEverything)); // for a with expression of the form // // receiver with { P1 = e1, P2 = e2 } // // we want to lower it to a call to the receiver's `Clone` method, then // set the given record properties. i.e. // // var tmp = (ReceiverType)receiver.Clone(); // tmp.P1 = e1; // tmp.P2 = e2; // tmp var cloneCall = _factory.Convert( withExpr.Type, _factory.Call( VisitExpression(withExpr.Receiver), withExpr.CloneMethod)); return(MakeExpressionWithInitializer( withExpr.Syntax, cloneCall, withExpr.InitializerExpression, withExpr.Type)); }
public override BoundNode VisitWithExpression(BoundWithExpression node) { if (_inExpressionLambda) { Error(ErrorCode.ERR_ExpressionTreeContainsWithExpression, node); } return(base.VisitWithExpression(node)); }
public override BoundNode VisitWithExpression(BoundWithExpression withExpr) { Debug.Assert(withExpr.Receiver.Type !.Equals(withExpr.Type, TypeCompareKind.ConsiderEverything)); // for a with expression of the form // // receiver with { P1 = e1, P2 = e2 } // // if the receiver is a struct, duplicate the value, then set the given struct properties: // // var tmp = receiver; // tmp.P1 = e1; // tmp.P2 = e2; // tmp // // otherwise the receiver is a record class and we want to lower it to a call to its `Clone` method, then // set the given record properties. i.e. // // var tmp = (ReceiverType)receiver.Clone(); // tmp.P1 = e1; // tmp.P2 = e2; // tmp BoundExpression expression; if (withExpr.Type.IsValueType) { expression = VisitExpression(withExpr.Receiver); } else { Debug.Assert(withExpr.CloneMethod is not null); Debug.Assert(withExpr.CloneMethod.ParameterCount == 0); expression = _factory.Convert( withExpr.Type, _factory.Call( VisitExpression(withExpr.Receiver), withExpr.CloneMethod)); } return(MakeExpressionWithInitializer( withExpr.Syntax, expression, withExpr.InitializerExpression, withExpr.Type)); }
public override BoundNode VisitWithExpression(BoundWithExpression withExpr) { TypeSymbol type = withExpr.Type; BoundExpression receiver = withExpr.Receiver; Debug.Assert(receiver.Type !.Equals(type, TypeCompareKind.ConsiderEverything)); // for a with expression of the form // // receiver with { P1 = e1, P2 = e2 } // P3 is copied implicitly // // if the receiver is a struct, duplicate the value, then set the given struct properties: // // var tmp = receiver; // tmp.P1 = e1; // tmp.P2 = e2; // tmp // // if the receiver is an anonymous type, then invoke its constructor: // // new Type(e1, e2, receiver.P3); // // otherwise the receiver is a record class and we want to lower it to a call to its `Clone` method, then // set the given record properties. i.e. // // var tmp = (ReceiverType)receiver.Clone(); // tmp.P1 = e1; // tmp.P2 = e2; // tmp BoundExpression rewrittenReceiver = VisitExpression(receiver); if (type.IsAnonymousType) { var anonymousType = (AnonymousTypeManager.AnonymousTypePublicSymbol)type; var sideEffects = ArrayBuilder <BoundExpression> .GetInstance(); var temps = ArrayBuilder <LocalSymbol> .GetInstance(); BoundLocal oldValue = _factory.StoreToTemp(rewrittenReceiver, out BoundAssignmentOperator boundAssignmentToTemp); temps.Add(oldValue.LocalSymbol); sideEffects.Add(boundAssignmentToTemp); BoundExpression value = _factory.New(anonymousType, getAnonymousTypeValues(withExpr, oldValue, anonymousType, sideEffects, temps)); return(new BoundSequence(withExpr.Syntax, temps.ToImmutableAndFree(), sideEffects.ToImmutableAndFree(), value, type)); } BoundExpression expression; if (type.IsValueType) { expression = rewrittenReceiver; } else { Debug.Assert(withExpr.CloneMethod is not null); Debug.Assert(withExpr.CloneMethod.ParameterCount == 0); expression = _factory.Convert( type, _factory.Call( rewrittenReceiver, withExpr.CloneMethod)); } return(MakeExpressionWithInitializer( withExpr.Syntax, expression, withExpr.InitializerExpression, type)); ImmutableArray <BoundExpression> getAnonymousTypeValues(BoundWithExpression withExpr, BoundExpression oldValue, AnonymousTypeManager.AnonymousTypePublicSymbol anonymousType, ArrayBuilder <BoundExpression> sideEffects, ArrayBuilder <LocalSymbol> temps) { // map: [propertyIndex] -> valueTemp var valueTemps = ArrayBuilder <BoundExpression?> .GetInstance(anonymousType.Properties.Length, fillWithValue : null); foreach (BoundExpression initializer in withExpr.InitializerExpression.Initializers) { var assignment = (BoundAssignmentOperator)initializer; var left = (BoundObjectInitializerMember)assignment.Left; Debug.Assert(left.MemberSymbol is not null); // We evaluate the values provided in source first var rewrittenRight = VisitExpression(assignment.Right); BoundLocal valueTemp = _factory.StoreToTemp(rewrittenRight, out BoundAssignmentOperator boundAssignmentToTemp); temps.Add(valueTemp.LocalSymbol); sideEffects.Add(boundAssignmentToTemp); var property = left.MemberSymbol; Debug.Assert(property.MemberIndexOpt !.Value >= 0 && property.MemberIndexOpt.Value < anonymousType.Properties.Length); valueTemps[property.MemberIndexOpt.Value] = valueTemp; } var builder = ArrayBuilder <BoundExpression> .GetInstance(anonymousType.Properties.Length); foreach (var property in anonymousType.Properties) { if (valueTemps[property.MemberIndexOpt !.Value] is BoundExpression initializerValue) { builder.Add(initializerValue); }