public override BoundNode VisitSequence(BoundSequence node) { BoundSpillSequenceBuilder valueBuilder = null; var value = VisitExpression(ref valueBuilder, node.Value); BoundSpillSequenceBuilder builder = null; var sideEffects = VisitExpressionList(ref builder, node.SideEffects, forceSpill: valueBuilder != null, sideEffectsOnly: true); if (builder == null && valueBuilder == null) { return(node.Update(node.Locals, sideEffects, value, node.Type)); } if (builder == null) { builder = new BoundSpillSequenceBuilder(); } PromoteAndAddLocals(builder, node.Locals); builder.AddExpressions(sideEffects); builder.Include(valueBuilder); return(builder.Update(value)); }
public override BoundNode VisitSequence(BoundSequence node) { AddAll(node.Locals); base.VisitSequence(node); RemoveAll(node.Locals); return(null); }
internal void AddSequence(SyntheticBoundNodeFactory F, BoundSequence sequence) { locals.AddRange(sequence.Locals); foreach (var sideEffect in sequence.SideEffects) { statements.Add(F.ExpressionStatement(sideEffect)); } }
public override BoundNode VisitSequence(BoundSequence node) { var oldScope = _currentScope; _currentScope = CreateOrReuseScope(node, node.Locals); var result = base.VisitSequence(node); _currentScope = oldScope; return(result); }
public override BoundNode VisitSequence(BoundSequence node) { // Spilled local temps do not appear here in a sequence expression, because any temps in a // sequence expression that need to be spilled would have been moved up to the // statement level by the AwaitLiftingRewriter. foreach (var local in node.Locals) { Debug.Assert(!NeedsProxy(local) || proxies.ContainsKey(local)); } return(base.VisitSequence(node)); }
// Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type. // We should have ignored the 'ref' on the parameter during overload resolution for the given method call. // If we had any ref omitted argument for the given call, we create a temporary local and // replace the argument with the following BoundSequence: { sideeffects: { temp = argument }, value = { ref temp } } // NOTE: The temporary local must be scoped to live across the entire BoundCall node, // otherwise the codegen optimizer might re-use the same temporary for multiple ref-omitted arguments for this call. private void RewriteArgumentsForComCall( ImmutableArray <ParameterSymbol> parameters, BoundExpression[] actualArguments, //already re-ordered to match parameters ArrayBuilder <RefKind> argsRefKindsBuilder, ArrayBuilder <LocalSymbol> temporariesBuilder) { Debug.Assert(actualArguments != null); Debug.Assert(actualArguments.Length == parameters.Length); Debug.Assert(argsRefKindsBuilder == null || argsRefKindsBuilder.Count == parameters.Length); var argsCount = actualArguments.Length; for (int argIndex = 0; argIndex < argsCount; ++argIndex) { RefKind paramRefKind = parameters[argIndex].RefKind; RefKind argRefKind = argsRefKindsBuilder[argIndex]; // Rewrite only if the argument was passed with no ref/out and the // parameter was declared ref. if (argRefKind != RefKind.None || paramRefKind != RefKind.Ref) { continue; } var argument = actualArguments[argIndex]; if (argument.Kind == BoundKind.Local) { var localRefKind = ((BoundLocal)argument).LocalSymbol.RefKind; if (localRefKind == RefKind.Ref) { // Already passing an address from the ref local. continue; } Debug.Assert(localRefKind == RefKind.None); } BoundAssignmentOperator boundAssignmentToTemp; BoundLocal boundTemp = factory.StoreToTemp(argument, out boundAssignmentToTemp); actualArguments[argIndex] = new BoundSequence( argument.Syntax, locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(boundAssignmentToTemp), value: boundTemp, type: boundTemp.Type); argsRefKindsBuilder[argIndex] = RefKind.Ref; temporariesBuilder.Add(boundTemp.LocalSymbol); } }
private BoundNode RewriteCatch(BoundCatchBlock node, ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals) { AddLocals(node.Locals, newLocals); var rewrittenCatchLocals = newLocals.ToImmutableAndFree(); // If exception variable got lifted, IntroduceFrame will give us frame init prologue. // It needs to run before the exception variable is accessed. // To ensure that, we will make exception variable a sequence that performs prologue as its its sideeffecs. BoundExpression rewrittenExceptionSource = null; var rewrittenFilter = (BoundExpression)this.Visit(node.ExceptionFilterOpt); if (node.ExceptionSourceOpt != null) { rewrittenExceptionSource = (BoundExpression)Visit(node.ExceptionSourceOpt); if (prologue.Count > 0) { rewrittenExceptionSource = new BoundSequence( rewrittenExceptionSource.Syntax, ImmutableArray.Create <LocalSymbol>(), prologue.ToImmutable(), rewrittenExceptionSource, rewrittenExceptionSource.Type); } } else if (prologue.Count > 0) { Debug.Assert(rewrittenFilter != null); rewrittenFilter = new BoundSequence( rewrittenFilter.Syntax, ImmutableArray.Create <LocalSymbol>(), prologue.ToImmutable(), rewrittenFilter, rewrittenFilter.Type); } // done with this. prologue.Free(); // rewrite filter and body // NOTE: this will proxy all accesses to exception local if that got lifted. var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); var rewrittenBlock = (BoundBlock)this.Visit(node.Body); return(node.Update( rewrittenCatchLocals, rewrittenExceptionSource, exceptionTypeOpt, rewrittenFilter, rewrittenBlock)); }
public override BoundNode VisitSequence(BoundSequence node) { if (node.Locals.IsDefaultOrEmpty) { // ignore blocks that declare no variables. return(base.VisitSequence(node)); } var previousBlock = PushBlock(node, node.Locals); var result = base.VisitSequence(node); PopBlock(previousBlock); return(result); }
public override BoundNode VisitSequence(BoundSequence node) { LambdaFrame frame; // Test if this frame has captured variables and requires the introduction of a closure class. if (frames.TryGetValue(node, out frame)) { return(IntroduceFrame(node, frame, (ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals) => { return RewriteSequence(node, prologue, newLocals); })); } else { return(RewriteSequence(node, ArrayBuilder <BoundExpression> .GetInstance(), ArrayBuilder <LocalSymbol> .GetInstance())); } }
private void XsInsertMissingOptionalArguments(SyntaxNode syntax, ImmutableArray <ParameterSymbol> parameters, BoundExpression[] arguments, ArrayBuilder <RefKind> refKinds, ArrayBuilder <LocalSymbol> temps, ThreeState enableCallerInfo = ThreeState.Unknown ) { Debug.Assert(refKinds.Count == arguments.Length); for (int p = 0; p < arguments.Length; ++p) { if (arguments[p] == null || arguments[p].Syntax.XIsMissingArgument) { ParameterSymbol parameter = parameters[p]; BoundExpression expr = GetDefaultParameterValue(syntax, parameter, enableCallerInfo); if (expr is BoundDefaultExpression && parameter.Type == _compilation.UsualType()) { var flds = _compilation.UsualType().GetMembers("_NIL"); var type = new BoundTypeExpression(syntax, null, _compilation.UsualType()); expr = _factory.Field(type, (FieldSymbol)flds[0]); } BoundAssignmentOperator boundAssignmentToTemp; BoundLocal boundTemp = _factory.StoreToTemp(expr, out boundAssignmentToTemp); expr = new BoundSequence( syntax, locals: ImmutableArray <LocalSymbol> .Empty, sideEffects: ImmutableArray.Create <BoundExpression>(boundAssignmentToTemp), value: boundTemp, type: boundTemp.Type); refKinds[p] = parameters[p].RefKind; temps.Add(boundTemp.LocalSymbol); arguments[p] = expr; Debug.Assert(arguments[p].Type == parameter.Type); if (parameters[p].RefKind == RefKind.In) { Debug.Assert(refKinds[p] == RefKind.None); refKinds[p] = RefKind.In; } } } }
private BoundSequence RewriteSequence(BoundSequence node, ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals) { AddLocals(node.Locals, newLocals); foreach (var expr in node.SideEffects) { var replacement = (BoundExpression)this.Visit(expr); if (replacement != null) { prologue.Add(replacement); } } var newValue = (BoundExpression)this.Visit(node.Value); var newType = this.VisitType(node.Type); return(node.Update(newLocals.ToImmutableAndFree(), prologue.ToImmutableAndFree(), newValue, newType)); }
public override BoundNode VisitSequence(BoundSequence node) { BoundSpillSequence2 ss2 = null; var value = VisitExpression(ref ss2, node.Value); BoundSpillSequence2 ss1 = null; var sideEffects = VisitExpressionList(ref ss1, node.SideEffects, forceSpill: ss2 != null, sideEffectsOnly: true); if (ss1 == null && ss2 == null) { return(node.Update(node.Locals, sideEffects, value, node.Type)); } if (ss1 == null) { ss1 = new BoundSpillSequence2(); // possible if sideEffects is empty } ss1.AddRange(sideEffects, MakeExpressionStatement); ss1.AddRange(node.Locals); ss1.IncludeSequence(ss2); return(ss1.Update(value)); }
public override BoundNode VisitSequence(BoundSequence node) { ReadOnlyArray <BoundExpression> sideEffects = (ReadOnlyArray <BoundExpression>) this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence) { return(node.Update(node.Locals, sideEffects, value, type)); } var spillBuilder = new SpillBuilder(); spillBuilder.Locals.AddRange(node.Locals); foreach (var sideEffect in sideEffects) { spillBuilder.Statements.Add( (sideEffect.Kind == BoundKind.SpillSequence) ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect) : F.ExpressionStatement(sideEffect)); } BoundExpression newValue; if (value.Kind == BoundKind.SpillSequence) { var awaitEffect = (BoundSpillSequence)value; spillBuilder.AddSpill(awaitEffect); newValue = awaitEffect.Value; } else { newValue = value; } return(spillBuilder.BuildSequenceAndFree(F, newValue)); }
public override BoundNode VisitSequence(BoundSequence node) { try { if (!node.Locals.IsNullOrEmpty) { foreach (var l in node.Locals) { localsDefined.Add(l); } } return(base.VisitSequence(node)); } finally { if (!node.Locals.IsNullOrEmpty) { foreach (var l in node.Locals) { localsDefined.Remove(l); } } } }
private BoundNode RewriteWithRefOperand( bool isPrefix, bool isChecked, ArrayBuilder <LocalSymbol> tempSymbols, ArrayBuilder <BoundExpression> tempInitializers, SyntaxNode syntax, BoundExpression operand, TypeSymbol operandType, BoundExpression boundTemp, BoundExpression newValue) { var tempValue = isPrefix ? newValue : MakeRValue(operand); var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false); var operandValue = isPrefix ? boundTemp : newValue; var tempAssignedAndOperandValue = new BoundSequence( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create <BoundExpression>(tempAssignment), operandValue, tempValue.Type); // prefix: operand = Seq{temp = (T)(operand + 1); temp;} // postfix: operand = Seq{temp = operand; ; (T)(temp + 1);} BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false); // prefix: Seq{operand initializers; operand = Seq{temp = (T)(operand + 1); temp;} result: temp} // postfix: Seq{operand initializers; operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp} tempInitializers.Add(operandAssignment); return(new BoundSequence( syntax: syntax, locals: tempSymbols.ToImmutableAndFree(), sideEffects: tempInitializers.ToImmutableAndFree(), value: boundTemp, type: operandType)); }
/// <summary> /// Process tempStores and add them as sideeffects to arguments where needed. The return /// value tells how many temps are actually needed. For unnecessary temps the corresponding /// temp store will be cleared. /// </summary> private static int MergeArgumentsAndSideEffects( BoundExpression[] arguments, ArrayBuilder <RefKind> refKinds, ArrayBuilder <BoundAssignmentOperator> tempStores) { Debug.Assert(arguments != null); Debug.Assert(refKinds != null); Debug.Assert(tempStores != null); int tempsRemainedInUse = tempStores.Count; // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E() // and arguments: t0, t2, t1, t4, 10, t5 // // We wish to produce arguments list: A(), SEQ(t1=B(), C()), t1, D(), 10, E() // // Our algorithm essentially finds temp stores that must happen before given argument // load, and if there are any they become side effects of the given load. // Stores immediately followed by loads of the same thing can be eliminated. // // Constraints: // Stores must happen before corresponding loads. // Stores cannot move relative to other stores. If arg was movable it would not need a temp. int firstUnclaimedStore = 0; for (int a = 0; a < arguments.Length; ++a) { var argument = arguments[a]; // if argument is a load, search for corresponding store. if store is found, extract // the actual expression we were storing and add it as an argument - this one does // not need a temp. if there are any unclaimed stores before the found one, add them // as side effects that precede this arg, they cannot happen later. if (argument.Kind == BoundKind.Local) { var correspondingStore = -1; for (int i = firstUnclaimedStore; i < tempStores.Count; i++) { if (tempStores[i].Left == argument) { correspondingStore = i; break; } } // store found? if (correspondingStore != -1) { var value = tempStores[correspondingStore].Right; // When we created the temp, we dropped the argument RefKind // since the local contained its own RefKind. Since we're removing // the temp, the argument RefKind needs to be restored. refKinds[a] = ((BoundLocal)argument).LocalSymbol.RefKind; // the matched store will not need to go into sideffects, only ones before it will // remove the store to signal that we are not using its temp. tempStores[correspondingStore] = null; tempsRemainedInUse--; // no need for sideeffects? // just combine store and load if (correspondingStore == firstUnclaimedStore) { arguments[a] = value; } else { var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore]; for (int s = 0; s < sideffects.Length; s++) { sideffects[s] = tempStores[firstUnclaimedStore + s]; } arguments[a] = new BoundSequence( value.Syntax, // this sequence does not own locals. Note that temps that // we use for the rewrite are stored in one arg and loaded // in another so they must live in a scope above. ImmutableArray <LocalSymbol> .Empty, sideffects.AsImmutableOrNull(), value, value.Type); } firstUnclaimedStore = correspondingStore + 1; } } } Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed"); return(tempsRemainedInUse); }
private BoundExpression OptimizeLiftedUnaryOperator( UnaryOperatorKind operatorKind, SyntaxNode syntax, MethodSymbol method, BoundExpression loweredOperand, TypeSymbol type) { if (NullableNeverHasValue(loweredOperand)) { return(new BoundDefaultOperator(syntax, null, type)); } // Second, another simple optimization. If we know that the operand is never null // then we can obtain the non-null value and skip generating the temporary. That is, // "~(new int?(M()))" is the same as "new int?(~M())". BoundExpression neverNull = NullableAlwaysHasValue(loweredOperand); if (neverNull != null) { return(GetLiftedUnaryOperatorConsequence(operatorKind, syntax, method, type, neverNull)); } var conditionalLeft = loweredOperand as BoundLoweredConditionalAccess; // NOTE: we could in theory handle side-effecting loweredRight here too // by including it as a part of whenNull, but there is a concern // that it can lead to code duplication var optimize = conditionalLeft != null && (conditionalLeft.WhenNullOpt == null || conditionalLeft.WhenNullOpt.IsDefaultValue()); if (optimize) { var result = LowerLiftedUnaryOperator(operatorKind, syntax, method, conditionalLeft.WhenNotNull, type); return(conditionalLeft.Update( conditionalLeft.Receiver, conditionalLeft.HasValueMethodOpt, whenNotNull: result, whenNullOpt: null, id: conditionalLeft.Id, type: result.Type )); } // This optimization is analogous to DistributeLiftedConversionIntoLiftedOperand. // Suppose we have a lifted unary conversion whose operand is itself a lifted operation. // That is, we have something like: // // int? r = - (M() + N()); // // where M() and N() return nullable ints. We would simply codegen this as first // creating the nullable int result of M() + N(), then checking it for nullity, // and then doing the unary minus. That is: // // int? m = M(); // int? n = N(); // int? t = m.HasValue && n.HasValue ? new int?(m.Value + n.Value) : new int?(); // int? r = t.HasValue ? new int?(-t.Value) : new int?(); // // However, we also observe that we can distribute the unary minus into both branches of // the conditional: // // int? m = M(); // int? n = N(); // int? r = m.HasValue && n.HasValue ? - (new int?(m.Value + n.Value))) : - new int?(); // // And we already optimize those! So we could reduce this to: // // int? m = M(); // int? n = N(); // int? r = m.HasValue && n.HasValue ? new int?(- (m.Value + n.Value)) : new int?()); // // which avoids entirely the creation of the unnecessary nullable int and the unnecessary // extra null check. if (loweredOperand.Kind == BoundKind.Sequence) { BoundSequence seq = (BoundSequence)loweredOperand; if (seq.Value.Kind == BoundKind.ConditionalOperator) { BoundConditionalOperator conditional = (BoundConditionalOperator)seq.Value; Debug.Assert(seq.Type == conditional.Type); Debug.Assert(conditional.Type == conditional.Consequence.Type); Debug.Assert(conditional.Type == conditional.Alternative.Type); if (NullableAlwaysHasValue(conditional.Consequence) != null && NullableNeverHasValue(conditional.Alternative)) { return(new BoundSequence( syntax, seq.Locals, seq.SideEffects, RewriteConditionalOperator( syntax, conditional.Condition, MakeUnaryOperator(operatorKind, syntax, method, conditional.Consequence, type), MakeUnaryOperator(operatorKind, syntax, method, conditional.Alternative, type), ConstantValue.NotAvailable, type), type)); } } } return(null); }
private BoundExpression VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node, bool used) { Debug.Assert(TypeSymbol.Equals(node.Right.Type, node.Operator.RightType, TypeCompareKind.ConsiderEverything2)); BoundExpression loweredRight = VisitExpression(node.Right); var temps = ArrayBuilder <LocalSymbol> .GetInstance(); var stores = ArrayBuilder <BoundExpression> .GetInstance(); var kind = node.Operator.Kind; bool isChecked = kind.IsChecked(); bool isDynamic = kind.IsDynamic(); var binaryOperator = kind.Operator(); // This will be filled in with the LHS that uses temporaries to prevent // double-evaluation of side effects. BoundExpression transformedLHS = TransformCompoundAssignmentLHS(node.Left, stores, temps, isDynamic); var lhsRead = MakeRValue(transformedLHS); BoundExpression rewrittenAssignment; if (node.Left.Kind == BoundKind.DynamicMemberAccess && (binaryOperator == BinaryOperatorKind.Addition || binaryOperator == BinaryOperatorKind.Subtraction)) { // If this could be an event assignment at runtime, we need to rewrite to the following form: // Original: // receiver.EV += handler // Rewritten: // dynamic memberAccessReceiver = receiver; // bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV"); // dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null; // var loweredRight = handler; // Only necessary if handler can change values, or is something like a lambda // isEvent ? add_Event(memberAccessReceiver, "EV", loweredRight) : transformedLHS = storeNonEvent + loweredRight; // // This is to ensure that if handler is something like a lambda, we evaluate fully evaluate the left // side before storing the lambda to a temp for use in both possible branches. // The first store to memberAccessReceiver has already been taken care of above by TransformCompoundAssignmentLHS var eventTemps = ArrayBuilder <LocalSymbol> .GetInstance(); var sequence = ArrayBuilder <BoundExpression> .GetInstance(); // dynamic memberAccessReceiver = receiver; var memberAccess = (BoundDynamicMemberAccess)transformedLHS; // bool isEvent = Runtime.IsEvent(memberAccessReceiver, "EV"); var isEvent = _factory.StoreToTemp(_dynamicFactory.MakeDynamicIsEventTest(memberAccess.Name, memberAccess.Receiver).ToExpression(), out BoundAssignmentOperator isEventAssignment); eventTemps.Add(isEvent.LocalSymbol); sequence.Add(isEventAssignment); // dynamic storeNonEvent = !isEvent ? memberAccessReceiver.EV : null; lhsRead = _factory.StoreToTemp(lhsRead, out BoundAssignmentOperator receiverAssignment); eventTemps.Add(((BoundLocal)lhsRead).LocalSymbol); var storeNonEvent = _factory.StoreToTemp(_factory.Conditional(_factory.Not(isEvent), receiverAssignment, _factory.Null(receiverAssignment.Type), receiverAssignment.Type), out BoundAssignmentOperator nonEventStore); eventTemps.Add(storeNonEvent.LocalSymbol); sequence.Add(nonEventStore); // var loweredRight = handler; if (CanChangeValueBetweenReads(loweredRight)) { loweredRight = _factory.StoreToTemp(loweredRight, out BoundAssignmentOperator possibleHandlerAssignment); eventTemps.Add(((BoundLocal)loweredRight).LocalSymbol); sequence.Add(possibleHandlerAssignment); } // add_Event(t1, "add_EV"); var invokeEventAccessor = _dynamicFactory.MakeDynamicEventAccessorInvocation( (binaryOperator == BinaryOperatorKind.Addition ? "add_" : "remove_") + memberAccess.Name, memberAccess.Receiver, loweredRight); // transformedLHS = storeNonEvent + loweredRight rewrittenAssignment = rewriteAssignment(lhsRead); // Final conditional var condition = _factory.Conditional(isEvent, invokeEventAccessor.ToExpression(), rewrittenAssignment, rewrittenAssignment.Type); rewrittenAssignment = new BoundSequence(node.Syntax, eventTemps.ToImmutableAndFree(), sequence.ToImmutableAndFree(), condition, condition.Type); } else { rewrittenAssignment = rewriteAssignment(lhsRead); } BoundExpression result = (temps.Count == 0 && stores.Count == 0) ? rewrittenAssignment : new BoundSequence( node.Syntax, temps.ToImmutable(), stores.ToImmutable(), rewrittenAssignment, rewrittenAssignment.Type); temps.Free(); stores.Free(); return(result); BoundExpression rewriteAssignment(BoundExpression leftRead) { SyntaxNode syntax = node.Syntax; // OK, we now have the temporary declarations, the temporary stores, and the transformed left hand side. // We need to generate // // xlhs = (FINAL)((LEFT)xlhs op rhs) // // And then wrap it up with the generated temporaries. // // (The right hand side has already been converted to the type expected by the operator.) BoundExpression opLHS = isDynamic ? leftRead : MakeConversionNode( syntax: syntax, rewrittenOperand: leftRead, conversion: node.LeftConversion, rewrittenType: node.Operator.LeftType, @checked: isChecked); BoundExpression operand = MakeBinaryOperator(syntax, node.Operator.Kind, opLHS, loweredRight, node.Operator.ReturnType, node.Operator.Method, isCompoundAssignment: true); BoundExpression opFinal = MakeConversionNode( syntax: syntax, rewrittenOperand: operand, conversion: node.FinalConversion, rewrittenType: node.Left.Type, explicitCastInCode: isDynamic, @checked: isChecked); return(MakeAssignmentOperator(syntax, transformedLHS, opFinal, node.Left.Type, used: used, isChecked: isChecked, isCompoundAssignment: true)); } }
private BoundNode RewriteCatch(BoundCatchBlock node, ArrayBuilder <BoundExpression> prologue, ArrayBuilder <LocalSymbol> newLocals) { LocalSymbol rewrittenCatchLocal = null; if (newLocals.Count > 0) { Debug.Assert(newLocals.Count == 1, "must be only one local that is the frame reference"); Debug.Assert(this.proxies.ContainsKey(node.LocalOpt), "original local should be proxied"); // getting new locals means that our original local was lifted into a closure // and instead of an actual local catch will own frame reference. rewrittenCatchLocal = newLocals[0]; } else if (node.LocalOpt != null) { // local was not lifted, but its type may need to be rewritten // this happens when it has a generic type which needs to be rewritten // when lambda body was moved to a separate method. var origLocal = node.LocalOpt; Debug.Assert(!this.proxies.ContainsKey(origLocal), "captured local should not need rewriting"); var newType = VisitType(origLocal.Type); if (newType == origLocal.Type) { // keeping same local rewrittenCatchLocal = origLocal; } else { // need a local of a different type rewrittenCatchLocal = new SynthesizedLocal(CurrentMethod, newType, origLocal.Name, declarationKind: LocalDeclarationKind.Catch); localMap.Add(origLocal, rewrittenCatchLocal); } } // If exception variable got lifted, IntroduceFrame will give us frame init prologue. // It needs to run before the exception variable is accessed. // To ensure that, we will make exception variable a sequence that performs prologue as its its sideeffecs. BoundExpression rewrittenExceptionSource = null; if (node.ExceptionSourceOpt != null) { rewrittenExceptionSource = (BoundExpression)Visit(node.ExceptionSourceOpt); if (prologue.Count > 0) { rewrittenExceptionSource = new BoundSequence( rewrittenExceptionSource.Syntax, ImmutableArray.Create <LocalSymbol>(), prologue.ToImmutable(), rewrittenExceptionSource, rewrittenExceptionSource.Type); } } // done with these. newLocals.Free(); prologue.Free(); // rewrite filter and body // NOTE: this will proxy all accesses to exception local if that got lifted. var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); var rewrittenFilter = (BoundExpression)this.Visit(node.ExceptionFilterOpt); var rewrittenBlock = (BoundBlock)this.Visit(node.Body); return(node.Update( rewrittenCatchLocal, rewrittenExceptionSource, exceptionTypeOpt, rewrittenFilter, rewrittenBlock)); }
public override BoundNode VisitSequence(BoundSequence node) { AddVariables(node.Locals); return(base.VisitSequence(node)); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { Debug.Assert(node != null); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> temps; ImmutableArray <RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments( node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps); BoundExpression rewrittenObjectCreation; if (_inExpressionLambda) { if (!temps.IsDefaultOrEmpty) { throw ExceptionUtilities.UnexpectedValue(temps.Length); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType); if (node.Type.IsInterfaceType()) { Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2)); rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false); } return(rewrittenObjectCreation); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, argumentRefKindsOpt, newInitializerExpression: null, changeTypeOpt: node.Constructor.ContainingType); // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { rewrittenObjectCreation = new BoundDefaultExpression(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type !); } if (!temps.IsDefaultOrEmpty) { rewrittenObjectCreation = new BoundSequence( node.Syntax, temps, ImmutableArray <BoundExpression> .Empty, rewrittenObjectCreation, node.Type); } if (node.Type.IsInterfaceType()) { Debug.Assert(TypeSymbol.Equals(rewrittenObjectCreation.Type, ((NamedTypeSymbol)node.Type).ComImportCoClass, TypeCompareKind.ConsiderEverything2)); rewrittenObjectCreation = MakeConversionNode(rewrittenObjectCreation, node.Type, false, false); } if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors) { return(rewrittenObjectCreation); } return(MakeExpressionWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type)); }