private static BoundStatement RewriteIfStatement( SyntaxNode syntax, BoundExpression rewrittenCondition, BoundStatement rewrittenConsequence, BoundStatement rewrittenAlternativeOpt, bool hasErrors) { var afterif = new GeneratedLabelSymbol("afterif"); // if (condition) // consequence; // // becomes // // GotoIfFalse condition afterif; // consequence; // afterif: if (rewrittenAlternativeOpt == null) { return BoundStatementList.Synthesized(syntax, new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, afterif), rewrittenConsequence, new BoundLabelStatement(syntax, afterif)); } // if (condition) // consequence; // else // alternative // // becomes // // GotoIfFalse condition alt; // consequence // goto afterif; // alt: // alternative; // afterif: var alt = new GeneratedLabelSymbol("alternative"); return BoundStatementList.Synthesized(syntax, hasErrors, new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, alt), rewrittenConsequence, new BoundGotoStatement(syntax, afterif), new BoundLabelStatement(syntax, alt), rewrittenAlternativeOpt, new BoundLabelStatement(syntax, afterif)); }
private BoundStatement RewriteForStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, CSharpSyntaxNode conditionSyntaxOpt, TextSpan conditionSpanOpt, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for([|int i = 0, j = 0|]; ; [|i++, j++|]) // // then all the initializers are treated as a single sequence point, as are // all the loop incrementers. // // We now make each one individually a sequence point: // // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|]) // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementers. var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } var endLabel = new GeneratedLabelSymbol("end"); // initializer; // goto end; // Mark the initial jump as hidden. // We do it to tell that this is not a part of previous statement. // This jump may be a target of another jump (for example if loops are nested) and that will make // impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.GenerateDebugInfo) { if (!conditionSpanOpt.IsEmpty) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSpanOpt); } else { // hidden sequence point if there is no condition branchBack = new BoundSequencePoint(conditionSyntaxOpt, branchBack); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, outerLocals, statements, hasErrors)); }
private BoundStatement RewriteWhileStatement( SyntaxNode syntax, BoundExpression rewrittenCondition, TextSpan conditionSequencePointSpan, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.generateDebugInfo) { ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan); } // while (condition) // body; // // becomes // // goto continue; // start: // body // continue: // GotoIfTrue condition start; // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.generateDebugInfo) { //mark the initial jump as hidden. //We do it to tell that this is not a part of previou statement. //This jump may be a target of another jump (for example if loops are nested) and that will make //impression of the previous statement being re-executed gotoContinue = new BoundSequencePoint(null, gotoContinue); } return BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel)); }
/// <summary> /// Build the decision dag, giving an error if some cases are subsumed and a warning if the switch expression is not exhaustive. /// </summary> /// <param name="node"></param> /// <param name="boundInputExpression"></param> /// <param name="switchArms"></param> /// <param name="decisionDag"></param> /// <param name="diagnostics"></param> /// <returns>true if there was a non-exhaustive warning reported</returns> private bool CheckSwitchExpressionExhaustive( SwitchExpressionSyntax node, BoundExpression boundInputExpression, ImmutableArray <BoundSwitchExpressionArm> switchArms, out BoundDecisionDag decisionDag, out LabelSymbol defaultLabel, DiagnosticBag diagnostics) { defaultLabel = new GeneratedLabelSymbol("default"); decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression(this.Compilation, node, boundInputExpression, switchArms, defaultLabel, diagnostics); var reachableLabels = decisionDag.ReachableLabels; foreach (BoundSwitchExpressionArm arm in switchArms) { if (!reachableLabels.Contains(arm.Label)) { diagnostics.Add(ErrorCode.ERR_SwitchArmSubsumed, arm.Pattern.Syntax.Location); } } if (!reachableLabels.Contains(defaultLabel)) { // switch expression is exhaustive; no default label needed. defaultLabel = null; return(false); } // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. foreach (var n in TopologicalSort.IterativeSort <BoundDecisionDagNode>(new[] { decisionDag.RootNode }, nonNullSuccessors)) { if (n is BoundLeafDecisionDagNode leaf && leaf.Label == defaultLabel) { diagnostics.Add(ErrorCode.WRN_SwitchExpressionNotExhaustive, node.SwitchKeyword.GetLocation()); return(true); } } return(false); ImmutableArray <BoundDecisionDagNode> nonNullSuccessors(BoundDecisionDagNode n) { switch (n) { case BoundTestDecisionDagNode p: switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null return(ImmutableArray.Create(p.WhenTrue)); case BoundDagExplicitNullTest t: // checks that the input is null return(ImmutableArray.Create(p.WhenFalse)); default: return(BoundDecisionDag.Successors(n)); } default: return(BoundDecisionDag.Successors(n)); } } }
private static LabelSymbol GetNullValueTargetSwitchLabel(ImmutableArray <BoundSwitchSection> sections, GeneratedLabelSymbol breakLabel) { LabelSymbol fallThroughLabel = breakLabel; foreach (var section in sections) { foreach (BoundSwitchLabel boundLabel in section.SwitchLabels) { var label = (SourceLabelSymbol)boundLabel.Label; var labelConstant = boundLabel.ConstantValueOpt; if (labelConstant == ConstantValue.Null) { return(label); } else if (labelConstant == null) { // Default label Debug.Assert(label.IdentifierNodeOrToken.Kind() == SyntaxKind.DefaultSwitchLabel); Debug.Assert(fallThroughLabel == breakLabel); fallThroughLabel = label; } } } return(fallThroughLabel); }
internal SwitchBinder(Binder next, SwitchStatementSyntax switchSyntax) : base(next) { _switchSyntax = switchSyntax; _breakLabel = new GeneratedLabelSymbol("break"); }
protected LoopBinder(Binder enclosing) : base(enclosing) { _breakLabel = new GeneratedLabelSymbol("break"); _continueLabel = new GeneratedLabelSymbol("continue"); }
private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray <BoundExpression> .CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray <LocalSymbol> .CreateFrom(iterationVar), statements: ReadOnlyArray <BoundStatement> .CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray <BoundExpression> dimensionArgument = ReadOnlyArray <BoundExpression> .CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt : ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type : intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray <LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray <LocalSymbol> .CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray <BoundStatement> .CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray <LocalSymbol> .CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return(forLoop); }
protected LoopBinder(MethodSymbol owner, Binder enclosing) : base(owner, enclosing) { this.breakLabel = new GeneratedLabelSymbol("break"); this.continueLabel = new GeneratedLabelSymbol("continue"); }
private BoundStatement RewriteWhileStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> innerLocals, BoundExpression rewrittenCondition, TextSpan conditionSequencePointSpan, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { if (!innerLocals.IsDefaultOrEmpty) { var walker = new AnyLocalCapturedInALambdaWalker(innerLocals); if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenBody)) { // If any inner local is captured within a lambda, we need to enter scope-block // always from the top, that is where an instance of a display class will be created. // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas. // while (condition) // body; // // becomes // // continue: // { // GotoIfFalse condition break; // body // goto continue; // } // break: // TODO: We could perform more fine analysis. // If locals declared in condition (the innerLocals) are captured, but not referenced in the body, we could use optimal IL by creating // another block around the condition and use it as a scope for the locals declared in condition. // This optimization can be applied to 'for' as well, while-body === for-body + increment. // Note however that the scope adjusments will likely be observable during debugging, in locals window. BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel); if (this.generateDebugInfo) { ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSequencePointSpan); } return(BoundStatementList.Synthesized(syntax, hasErrors, new BoundLabelStatement(syntax, continueLabel), new BoundBlock(syntax, innerLocals, ImmutableArray.Create <BoundStatement>( ifNotConditionGotoBreak, rewrittenBody, new BoundGotoStatement(syntax, continueLabel))), new BoundLabelStatement(syntax, breakLabel))); } } var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.generateDebugInfo) { ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan); } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.generateDebugInfo) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. gotoContinue = new BoundSequencePoint(null, gotoContinue); } if (!innerLocals.IsDefaultOrEmpty) { return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), new BoundBlock(syntax, innerLocals, ImmutableArray.Create <BoundStatement>( rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart)), new BoundLabelStatement(syntax, breakLabel))); } return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel))); }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove); MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics); if ((object)compareExchangeMethod == null) { return(new BoundBlock(syntax, localsOpt: default(ImmutableArray <LocalSymbol>), statements: ImmutableArray.Create <BoundStatement>( new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }); } GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; BoundStatement @return = new BoundReturnStatement(syntax, expressionOpt: null) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, localsOpt: tmps.AsImmutableOrNull(), statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
private BoundStatement RewriteForStatement( SyntaxNode syntax, ReadOnlyArray <LocalSymbol> locals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, SyntaxNodeOrToken conditionSyntax, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { var startLabel = new GeneratedLabelSymbol("start"); var endLabel = new GeneratedLabelSymbol("end"); // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } // initializer; // goto end; var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } //mark the initial jump as hidden. //We do it to tell that this is not a part of previous statement. //This jump may be a target of another jump (for example if loops are nested) and that will make //impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); Debug.Assert(rewrittenBody != null); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span); } else { //if there is no condition, make this a hidden point so that //it does not count as a part of previous statement branchBack = new BoundSequencePoint(conditionSyntax.AsNode(), branchBack); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToReadOnlyAndFree(); return(new BoundBlock(syntax, locals, statements, hasErrors)); }
private BoundStatement RewriteForStatement( SyntaxNode syntax, ReadOnlyArray<LocalSymbol> locals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, SyntaxNodeOrToken conditionSyntax, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { var startLabel = new GeneratedLabelSymbol("start"); var endLabel = new GeneratedLabelSymbol("end"); // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } // initializer; // goto end; var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } //mark the initial jump as hidden. //We do it to tell that this is not a part of previous statement. //This jump may be a target of another jump (for example if loops are nested) and that will make //impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); Debug.Assert(rewrittenBody != null); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span); } else { //if there is no condition, make this a hidden point so that //it does not count as a part of previous statement branchBack = new BoundSequencePoint(conditionSyntax.AsNode(), branchBack); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToReadOnlyAndFree(); return new BoundBlock(syntax, locals, statements, hasErrors); }
public override BoundNode VisitDoStatement(BoundDoStatement node) { Debug.Assert(node != null); var rewrittenCondition = (BoundExpression)Visit(node.Condition); var rewrittenBody = (BoundStatement)Visit(node.Body); var startLabel = new GeneratedLabelSymbol("start"); var syntax = node.Syntax; BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, rewrittenCondition, true, startLabel); if (this.generateDebugInfo) { var doSyntax = (DoStatementSyntax)syntax; var span = TextSpan.FromBounds( doSyntax.WhileKeyword.Span.Start, doSyntax.SemicolonToken.Span.End); ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span); } // do // body // while (condition); // // becomes // // start: // body // continue: // sequence point // GotoIfTrue condition start; // break: return BoundStatementList.Synthesized(syntax, node.HasErrors, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, node.ContinueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, node.BreakLabel)); }
private static BoundStatement RewriteIfStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> patternVariables, BoundExpression rewrittenCondition, BoundStatement rewrittenConsequence, BoundStatement rewrittenAlternativeOpt, bool hasErrors) { var afterif = new GeneratedLabelSymbol("afterif"); var builder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenAlternativeOpt == null) { // if (condition) // consequence; // // becomes // // GotoIfFalse condition afterif; // consequence; // afterif: builder.Add(new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, afterif)); builder.Add(rewrittenConsequence); builder.Add(new BoundLabelStatement(syntax, afterif)); var statements = builder.ToImmutableAndFree(); return((patternVariables.IsDefaultOrEmpty) ? new BoundStatementList(syntax, statements, hasErrors) : new BoundBlock(syntax, patternVariables, ImmutableArray <LocalFunctionSymbol> .Empty, statements, hasErrors) { WasCompilerGenerated = true }); } else { // if (condition) // consequence; // else // alternative // // becomes // // GotoIfFalse condition alt; // consequence // goto afterif; // alt: // alternative; // afterif: var alt = new GeneratedLabelSymbol("alternative"); builder.Add(new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, alt)); builder.Add(rewrittenConsequence); builder.Add(new BoundGotoStatement(syntax, afterif)); builder.Add(new BoundLabelStatement(syntax, alt)); if (!patternVariables.IsDefaultOrEmpty) { // pattern variables are not in scope in the else part var firstPart = new BoundBlock(syntax, patternVariables, ImmutableArray <LocalFunctionSymbol> .Empty, builder.ToImmutableAndFree(), hasErrors) { WasCompilerGenerated = true }; builder = ArrayBuilder <BoundStatement> .GetInstance(); builder.Add(firstPart); } builder.Add(rewrittenAlternativeOpt); builder.Add(new BoundLabelStatement(syntax, afterif)); return(new BoundStatementList(syntax, builder.ToImmutableAndFree(), hasErrors)); } }
private BoundStatement RewriteForStatement( CSharpSyntaxNode syntax, ImmutableArray <LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, ImmutableArray <LocalSymbol> innerLocals, BoundExpression rewrittenCondition, SyntaxNodeOrToken conditionSyntax, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for(int i = 0, j = 0; ; i++, j++) // ^--------------^ ^------^ // // then all the initializers are treated as a single sequence point, as are // all the loop incrementers. // // We now make each one individually a sequence point: // // for(int i = 0, j = 0; ; i++, j++) // ^-------^ ^---^ ^-^ ^-^ // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementers. var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); if (!innerLocals.IsDefaultOrEmpty) { var walker = new AnyLocalCapturedInALambdaWalker(innerLocals); if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenIncrement) || walker.Analyze(rewrittenBody)) { // If any inner local is captured within a lambda, we need to enter scope-block // always from the top, that is where an instance of a display class will be created. // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas. // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // start: // { // GotoIfFalse condition break; // body; // continue: // increment; // goto start; // } // break: // } // start: statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); var blockBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // GotoIfFalse condition break; if (rewrittenCondition != null) { BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel); if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSyntax.Span); } else { ifNotConditionGotoBreak = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), ifNotConditionGotoBreak); } } blockBuilder.Add(ifNotConditionGotoBreak); } // body; blockBuilder.Add(rewrittenBody); // continue: // increment; blockBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { blockBuilder.Add(rewrittenIncrement); } // goto start; blockBuilder.Add(new BoundGotoStatement(syntax, startLabel)); statementBuilder.Add(new BoundBlock(syntax, innerLocals, blockBuilder.ToImmutableAndFree())); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); return(new BoundBlock(syntax, outerLocals, statementBuilder.ToImmutableAndFree(), hasErrors)); } } var endLabel = new GeneratedLabelSymbol("end"); // for (initializer; condition; increment) // body; // // becomes the following (with // block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } // initializer; // goto end; //mark the initial jump as hidden. //We do it to tell that this is not a part of previous statement. //This jump may be a target of another jump (for example if loops are nested) and that will make //impression of the previous statement being re-executed var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel)); statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); ArrayBuilder <BoundStatement> saveBuilder = null; if (!innerLocals.IsDefaultOrEmpty) { saveBuilder = statementBuilder; statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); } statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.generateDebugInfo) { if (conditionSyntax.IsToken) { branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span); } else { //if there is no condition, make this a hidden point so that //it does not count as a part of previous statement branchBack = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), branchBack); } } statementBuilder.Add(branchBack); if (!innerLocals.IsDefaultOrEmpty) { var block = new BoundBlock(syntax, innerLocals, statementBuilder.ToImmutableAndFree()); statementBuilder = saveBuilder; statementBuilder.Add(block); } // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, outerLocals, statements, hasErrors)); }
private BoundStatement RewriteForStatement( BoundLoopStatement original, ImmutableArray <LocalSymbol> outerLocals, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(original.Kind == BoundKind.ForStatement || original.Kind == BoundKind.ForEachStatement); Debug.Assert(rewrittenBody != null); // The sequence point behavior exhibited here is different from that of the native compiler. In the native // compiler, if you have something like // // for([|int i = 0, j = 0|]; ; [|i++, j++|]) // // then all the initializers are treated as a single sequence point, as are // all the loop incrementors. // // We now make each one individually a sequence point: // // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|]) // // If we decide that we want to preserve the native compiler stepping behavior // then we'll need to be a bit fancy here. The initializer and increment statements // can contain lambdas whose bodies need to have sequence points inserted, so we // need to make sure we visit the children. But we'll also need to make sure that // we do not generate one sequence point for each statement in the initializers // and the incrementors. CSharpSyntaxNode syntax = original.Syntax; var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // goto end; // start: // body; // continue: // increment; // end: // GotoIfTrue condition start; // break: // } var endLabel = new GeneratedLabelSymbol("end"); // initializer; // goto end; BoundStatement gotoEnd = new BoundGotoStatement(syntax, endLabel); if (this.Instrument) { switch (original.Kind) { case BoundKind.ForEachStatement: gotoEnd = _instrumenter.InstrumentForEachStatementGotoEnd((BoundForEachStatement)original, gotoEnd); break; case BoundKind.ForStatement: gotoEnd = _instrumenter.InstrumentForStatementGotoEnd((BoundForStatement)original, gotoEnd); break; default: throw ExceptionUtilities.UnexpectedValue(original.Kind); } } statementBuilder.Add(gotoEnd); // start: // body; statementBuilder.Add(new BoundLabelStatement(syntax, startLabel)); statementBuilder.Add(rewrittenBody); // continue: // increment; statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel)); if (rewrittenIncrement != null) { statementBuilder.Add(rewrittenIncrement); } // end: // GotoIfTrue condition start; statementBuilder.Add(new BoundLabelStatement(syntax, endLabel)); BoundStatement branchBack = null; if (rewrittenCondition != null) { branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); } else { branchBack = new BoundGotoStatement(syntax, startLabel); } if (this.Instrument) { switch (original.Kind) { case BoundKind.ForEachStatement: branchBack = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)original, branchBack); break; case BoundKind.ForStatement: branchBack = _instrumenter.InstrumentForStatementConditionalGotoStart((BoundForStatement)original, branchBack); break; default: throw ExceptionUtilities.UnexpectedValue(original.Kind); } } statementBuilder.Add(branchBack); // break: statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, outerLocals, ImmutableArray <LocalFunctionSymbol> .Empty, statements, hasErrors)); }
private BoundStatement RewriteWhileStatement( BoundLoopStatement loop, BoundExpression rewrittenCondition, BoundStatement rewrittenBody, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) { Debug.Assert(loop.Kind == BoundKind.WhileStatement || loop.Kind == BoundKind.ForEachStatement); SyntaxNode syntax = loop.Syntax; var startLabel = new GeneratedLabelSymbol("start"); BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel); if (this.Instrument && !loop.WasCompilerGenerated) { switch (loop.Kind) { case BoundKind.WhileStatement: ifConditionGotoStart = _instrumenter.InstrumentWhileStatementConditionalGotoStart((BoundWhileStatement)loop, ifConditionGotoStart); break; case BoundKind.ForEachStatement: ifConditionGotoStart = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)loop, ifConditionGotoStart); break; default: throw ExceptionUtilities.UnexpectedValue(loop.Kind); } } // while (condition) // body; // // becomes // // goto continue; // start: // { // body // continue: // GotoIfTrue condition start; // } // break: BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel); if (this.Instrument && !loop.WasCompilerGenerated) { // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This // jump may be a target of another jump (for example if loops are nested) and that would give the // impression that the previous statement is being re-executed. switch (loop.Kind) { case BoundKind.WhileStatement: gotoContinue = _instrumenter.InstrumentWhileStatementGotoContinue((BoundWhileStatement)loop, gotoContinue); break; case BoundKind.ForEachStatement: gotoContinue = _instrumenter.InstrumentForEachStatementGotoContinue((BoundForEachStatement)loop, gotoContinue); break; default: throw ExceptionUtilities.UnexpectedValue(loop.Kind); } } return(BoundStatementList.Synthesized(syntax, hasErrors, gotoContinue, new BoundLabelStatement(syntax, startLabel), rewrittenBody, new BoundLabelStatement(syntax, continueLabel), ifConditionGotoStart, new BoundLabelStatement(syntax, breakLabel))); }
private bool CheckSwitchExpressionExhaustive( SwitchExpressionSyntax node, BoundExpression boundInputExpression, ImmutableArray <BoundSwitchExpressionArm> switchArms, out BoundDecisionDag decisionDag, out LabelSymbol defaultLabel, BindingDiagnosticBag diagnostics) { defaultLabel = new GeneratedLabelSymbol("default"); decisionDag = DecisionDagBuilder.CreateDecisionDagForSwitchExpression(this.Compilation, node, boundInputExpression, switchArms, defaultLabel, diagnostics); var reachableLabels = decisionDag.ReachableLabels; bool hasErrors = false; foreach (BoundSwitchExpressionArm arm in switchArms) { hasErrors |= arm.HasErrors; if (!hasErrors && !reachableLabels.Contains(arm.Label)) { diagnostics.Add(ErrorCode.ERR_SwitchArmSubsumed, arm.Pattern.Syntax.Location); } } if (!reachableLabels.Contains(defaultLabel)) { // switch expression is exhaustive; no default label needed. defaultLabel = null; return(false); } if (hasErrors) { return(true); } // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. bool wasAcyclic = TopologicalSort.TryIterativeSort <BoundDecisionDagNode>(new[] { decisionDag.RootNode }, nonNullSuccessors, out var nodes); // Since decisionDag.RootNode is acyclic by construction, its subset of nodes sorted here cannot be cyclic Debug.Assert(wasAcyclic); foreach (var n in nodes) { if (n is BoundLeafDecisionDagNode leaf && leaf.Label == defaultLabel) { var samplePattern = PatternExplainer.SamplePatternForPathToDagNode( BoundDagTemp.ForOriginalInput(boundInputExpression), nodes, n, nullPaths: false, out bool requiresFalseWhenClause, out bool unnamedEnumValue); ErrorCode warningCode = requiresFalseWhenClause ? ErrorCode.WRN_SwitchExpressionNotExhaustiveWithWhen : unnamedEnumValue ? ErrorCode.WRN_SwitchExpressionNotExhaustiveWithUnnamedEnumValue : ErrorCode.WRN_SwitchExpressionNotExhaustive; diagnostics.Add( warningCode, node.SwitchKeyword.GetLocation(), samplePattern); return(true); } } return(false); ImmutableArray <BoundDecisionDagNode> nonNullSuccessors(BoundDecisionDagNode n) { switch (n) { case BoundTestDecisionDagNode p: switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null return(ImmutableArray.Create(p.WhenTrue)); case BoundDagExplicitNullTest t: // checks that the input is null return(ImmutableArray.Create(p.WhenFalse)); default: return(BoundDecisionDag.Successors(n)); } default: return(BoundDecisionDag.Successors(n)); } } }
protected void AddState(out int stateNumber, out GeneratedLabelSymbol resumeLabel) { stateNumber = _nextState++; AddState(stateNumber, out resumeLabel); }
/// <summary> /// Lower a foreach loop that will enumerate a multi-dimensional array. /// /// A[...] a = x; /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...; /// for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) /// for (int p_1 = a.GetLowerBound(1); p_1 <= q_1; p_1 = p_1 + 1) /// ... /// { V v = (V)a[p_0, p_1, ...]; /* body */ } /// </summary> /// <remarks> /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's /// implementation of IEnumerable and just indexing into its elements. /// /// NOTE: We're assuming that sequence points have already been generated. /// Otherwise, lowering to nested for-loops would generated spurious ones. /// </remarks> private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(!arrayType.IsSZArray); TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray); BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to // initializing each one at the corresponding level of nesting). Doing it at the same time as // the lower bound would make this code a bit simpler, but it would make it harder to compare // the roslyn and dev10 IL. // int q_0, q_1, ... LocalSymbol[] upperVar = new LocalSymbol[rank]; BoundLocal[] boundUpperVar = new BoundLocal[rank]; BoundStatement[] upperVarDecl = new BoundStatement[rank]; for (int dimension = 0; dimension < rank; dimension++) { // int q_dimension upperVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayLimit); boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType); ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetUpperBound(dimension) BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int q_dimension = a.GetUpperBound(dimension); upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound); } // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = MakeConversion( syntax: forEachSyntax, rewrittenOperand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ImmutableArray.Create((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, rewrittenType: iterationVarType, @checked: node.Checked); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax); // work from most-nested to least-nested // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ImmutableArray <BoundExpression> dimensionArgument = ImmutableArray.Create( MakeLiteral(forEachSyntax, constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(dimension) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // int p_dimension = a.GetLowerBound(dimension); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop ? node.BreakLabel // i.e. the one that break statements will jump to : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused // p_dimension <= q_dimension //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: boundUpperVar[dimension], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_dimension = p_dimension + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if (forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( syntax: forEachSyntax, outerLocals: ImmutableArray.Create(positionVar[dimension]), rewrittenInitializer: positionVarDecl, rewrittenCondition: exitCondition, conditionSyntaxOpt: null, conditionSpanOpt: forEachSyntax.InKeyword.Span, rewrittenIncrement: positionIncrement, rewrittenBody: body, breakLabel: breakLabel, continueLabel: continueLabel, hasErrors: node.HasErrors); } Debug.Assert(forLoop != null); BoundStatement result = new BoundBlock( forEachSyntax, ImmutableArray.Create(arrayVar).Concat(upperVar.AsImmutableOrNull()), ImmutableArray <LocalFunctionSymbol> .Empty, ImmutableArray.Create(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop)); AddForEachKeywordSequencePoint(forEachSyntax, ref result); return(result); }
internal SwitchBinder(MethodSymbol method, Binder next, SwitchStatementSyntax switchSyntax) : base(method, next) { this.switchSyntax = switchSyntax; this.breakLabel = new GeneratedLabelSymbol("break"); }
private BoundStatement MakeSwitchStatementWithNullableExpression( SyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray <LocalSymbol> locals, ImmutableArray <LocalFunctionSymbol> localFunctions, GeneratedLabelSymbol breakLabel, BoundSwitchStatement oldNode) { Debug.Assert(rewrittenExpression.Type.IsNullableType()); var exprSyntax = rewrittenExpression.Syntax; var exprNullableType = rewrittenExpression.Type; var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // Rewrite the nullable expression to a temp as we might have a user defined conversion from source expression to switch governing type. // We can avoid generating the temp if the expression is a bound local. LocalSymbol tempLocal; if (rewrittenExpression.Kind != BoundKind.Local) { BoundAssignmentOperator assignmentToTemp; BoundLocal boundTemp = _factory.StoreToTemp(rewrittenExpression, out assignmentToTemp); var tempAssignment = new BoundExpressionStatement(exprSyntax, assignmentToTemp); statementBuilder.Add(tempAssignment); tempLocal = boundTemp.LocalSymbol; rewrittenExpression = boundTemp; } else { tempLocal = null; } // Generate a BoundConditionalGoto with null check as the conditional expression and appropriate switch label as the target: null, default or exit label. BoundStatement condGotoNullValueTargetLabel = new BoundConditionalGoto( exprSyntax, condition: MakeNullCheck(exprSyntax, rewrittenExpression, BinaryOperatorKind.NullableNullEqual), jumpIfTrue: true, label: GetNullValueTargetSwitchLabel(rewrittenSections, breakLabel)); // Rewrite the switch statement using nullable expression's underlying value as the switch expression. // rewrittenExpression.GetValueOrDefault() MethodSymbol getValueOrDefault = GetNullableMethod(syntax, exprNullableType, SpecialMember.System_Nullable_T_GetValueOrDefault); BoundCall callGetValueOrDefault = BoundCall.Synthesized(exprSyntax, rewrittenExpression, getValueOrDefault); rewrittenExpression = callGetValueOrDefault; // rewrite switch statement BoundStatement rewrittenSwitchStatement = MakeSwitchStatementWithNonNullableExpression( syntax, condGotoNullValueTargetLabel, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, localFunctions, breakLabel, oldNode); statementBuilder.Add(rewrittenSwitchStatement); return(new BoundBlock( syntax, locals: (object)tempLocal == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(tempLocal), statements: statementBuilder.ToImmutableAndFree())); }
private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray<BoundExpression>.CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray<BoundExpression> dimensionArgument = ReadOnlyArray<BoundExpression>.CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray<LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray<LocalSymbol>.CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray<LocalSymbol>.CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if(forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return forLoop; }
/// <summary> /// Generate a thread-safe accessor for a regular field-like event. /// /// DelegateType tmp0 = _event; //backing field /// DelegateType tmp1; /// DelegateType tmp2; /// do { /// tmp1 = tmp0; /// tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -= /// tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); /// } while ((object)tmp0 != (object)tmp1); /// /// Note, if System.Threading.Interlocked.CompareExchange<T> is not available, /// we emit the following code and mark the method Synchronized (unless it is a struct). /// /// _event = (DelegateType)Delegate.Combine(_event, value); //Remove for -= /// /// </summary> internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics) { CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode; TypeSymbol delegateType = eventSymbol.Type; MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod; ParameterSymbol thisParameter = accessor.ThisParameter; TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); SpecialMember updateMethodId = isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove; MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(updateMethodId); BoundStatement @return = new BoundReturnStatement(syntax, refKind: RefKind.None, expressionOpt: null) { WasCompilerGenerated = true }; if (updateMethod == null) { MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(updateMethodId); diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name), syntax.Location)); return(BoundBlock.SynthesizedNoLocals(syntax, @return)); } Binder.ReportUseSiteDiagnostics(updateMethod, diagnostics, syntax); BoundThisReference fieldReceiver = eventSymbol.IsStatic ? null : new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true }; BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax, receiver: fieldReceiver, fieldSymbol: eventSymbol.AssociatedField, constantValueOpt: null) { WasCompilerGenerated = true }; BoundParameter boundParameter = new BoundParameter(syntax, parameterSymbol: accessor.Parameters[0]) { WasCompilerGenerated = true }; BoundExpression delegateUpdate; MethodSymbol compareExchangeMethod = (MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); if ((object)compareExchangeMethod == null) { // (DelegateType)Delegate.Combine(_event, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // _event = (DelegateType)Delegate.Combine(_event, value); BoundStatement eventUpdate = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundBackingField, right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; return(BoundBlock.SynthesizedNoLocals(syntax, statements: ImmutableArray.Create <BoundStatement>( eventUpdate, @return))); } compareExchangeMethod = compareExchangeMethod.Construct(ImmutableArray.Create <TypeSymbol>(delegateType)); Binder.ReportUseSiteDiagnostics(compareExchangeMethod, diagnostics, syntax); GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop"); const int numTemps = 3; LocalSymbol[] tmps = new LocalSymbol[numTemps]; BoundLocal[] boundTmps = new BoundLocal[numTemps]; for (int i = 0; i < numTemps; i++) { tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp); boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType); } // tmp0 = _event; BoundStatement tmp0Init = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: boundBackingField, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // LOOP: BoundStatement loopStart = new BoundLabelStatement(syntax, label: loopLabel) { WasCompilerGenerated = true }; // tmp1 = tmp0; BoundStatement tmp1Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[1], right: boundTmps[0], type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // (DelegateType)Delegate.Combine(tmp1, value) delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, method: updateMethod, arguments: ImmutableArray.Create <BoundExpression>(boundTmps[1], boundParameter)), kind: ConversionKind.ExplicitReference, type: delegateType); // tmp2 = (DelegateType)Delegate.Combine(tmp1, value); BoundStatement tmp2Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[2], right: delegateUpdate, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, method: compareExchangeMethod, arguments: ImmutableArray.Create <BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1])); // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1); BoundStatement tmp0Update = new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundTmps[0], right: compareExchange, type: delegateType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise BoundExpression loopExitCondition = new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.ObjectEqual, left: boundTmps[0], right: boundTmps[1], constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType) { WasCompilerGenerated = true }; // branchfalse (tmp0 == tmp1) LOOP BoundStatement loopEnd = new BoundConditionalGoto(syntax, condition: loopExitCondition, jumpIfTrue: false, label: loopLabel) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, locals: tmps.AsImmutable(), localFunctions: ImmutableArray <LocalFunctionSymbol> .Empty, statements: ImmutableArray.Create <BoundStatement>( tmp0Init, loopStart, tmp1Update, tmp2Update, tmp0Update, loopEnd, @return)) { WasCompilerGenerated = true }); }
private BoundStatement RewriteForStatement( BoundForStatement node, BoundStatement rewrittenInitializer, BoundExpression rewrittenCondition, BoundStatement rewrittenIncrement, BoundStatement rewrittenBody) { if (node.InnerLocals.IsEmpty) { return(RewriteForStatementWithoutInnerLocals( node, node.OuterLocals, rewrittenInitializer, rewrittenCondition, rewrittenIncrement, rewrittenBody, node.BreakLabel, node.ContinueLabel, node.HasErrors)); } // We need to enter inner_scope-block from the top, that is where an instance of a display class will be created // if any local is captured within a lambda. // for (initializer; condition; increment) // body; // // becomes the following (with block added for locals) // // { // initializer; // start: // { // GotoIfFalse condition break; // body; // continue: // increment; // goto start; // } // break: // } Debug.Assert(rewrittenBody != null); SyntaxNode syntax = node.Syntax; var statementBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // initializer; if (rewrittenInitializer != null) { statementBuilder.Add(rewrittenInitializer); } var startLabel = new GeneratedLabelSymbol("start"); // start: BoundStatement startLabelStatement = new BoundLabelStatement(syntax, startLabel); if (Instrument) { startLabelStatement = new BoundSequencePoint(null, startLabelStatement); } statementBuilder.Add(startLabelStatement); var blockBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // GotoIfFalse condition break; if (rewrittenCondition != null) { BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, node.BreakLabel); if (this.Instrument) { ifNotConditionGotoBreak = _instrumenter.InstrumentForStatementConditionalGotoStartOrBreak(node, ifNotConditionGotoBreak); } blockBuilder.Add(ifNotConditionGotoBreak); } // body; blockBuilder.Add(rewrittenBody); // continue: // increment; blockBuilder.Add(new BoundLabelStatement(syntax, node.ContinueLabel)); if (rewrittenIncrement != null) { blockBuilder.Add(rewrittenIncrement); } // goto start; blockBuilder.Add(new BoundGotoStatement(syntax, startLabel)); statementBuilder.Add(new BoundBlock(syntax, node.InnerLocals, blockBuilder.ToImmutableAndFree())); // break: statementBuilder.Add(new BoundLabelStatement(syntax, node.BreakLabel)); var statements = statementBuilder.ToImmutableAndFree(); return(new BoundBlock(syntax, node.OuterLocals, statements, node.HasErrors)); }
/// <summary> /// The try statement is the most complex part of the state machine transformation. /// Since the CLR will not allow a 'goto' into the scope of a try statement, we must /// generate the dispatch to the state's label stepwise. That is done by translating /// the try statements from the inside to the outside. Within a try statement, we /// start with an empty dispatch table (representing the mapping from state numbers /// to labels). During translation of the try statement's body, the dispatch table /// will be filled in with the data necessary to dispatch once we're inside the try /// block. We generate that at the head of the translated try statement. Then, we /// copy all of the states from that table into the table for the enclosing construct, /// but associate them with a label just before the translated try block. That way /// the enclosing construct will generate the code necessary to get control into the /// try block for all of those states. /// </summary> public override BoundNode VisitTryStatement(BoundTryStatement node) { var oldDispatches = _dispatches; var oldFinalizerState = _currentFinalizerState; var oldHasFinalizerState = _hasFinalizerState; _dispatches = null; _currentFinalizerState = -1; _hasFinalizerState = false; BoundBlock tryBlock = F.Block((BoundStatement)this.Visit(node.TryBlock)); GeneratedLabelSymbol dispatchLabel = null; if (_dispatches != null) { dispatchLabel = F.GenerateLabel("tryDispatch"); if (_hasFinalizerState) { // cause the current finalizer state to arrive here and then "return false" var finalizer = F.GenerateLabel("finalizer"); _dispatches.Add(finalizer, new List <int>() { _currentFinalizerState }); var skipFinalizer = F.GenerateLabel("skipFinalizer"); tryBlock = F.Block( F.HiddenSequencePoint(), Dispatch(), F.Goto(skipFinalizer), F.Label(finalizer), // code for the finalizer here GenerateSetBothStates(StateMachineStates.NotStartedStateMachine), GenerateReturn(false), F.Label(skipFinalizer), tryBlock); } else { tryBlock = F.Block( F.HiddenSequencePoint(), Dispatch(), tryBlock); } if (oldDispatches == null) { Debug.Assert(!oldHasFinalizerState); oldDispatches = new Dictionary <LabelSymbol, List <int> >(); } oldDispatches.Add(dispatchLabel, new List <int>(from kv in _dispatches.Values from n in kv orderby n select n)); } _hasFinalizerState = oldHasFinalizerState; _currentFinalizerState = oldFinalizerState; _dispatches = oldDispatches; ImmutableArray <BoundCatchBlock> catchBlocks = this.VisitList(node.CatchBlocks); CloseTryCatchBlocks(); BoundBlock finallyBlockOpt = node.FinallyBlockOpt == null ? null : F.Block( F.HiddenSequencePoint(), F.If( condition: ShouldEnterFinallyBlock(), thenClause: (BoundBlock)this.Visit(node.FinallyBlockOpt) ), F.HiddenSequencePoint()); BoundStatement result = node.Update(tryBlock, catchBlocks, finallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler); if ((object)dispatchLabel != null) { result = F.Block( F.HiddenSequencePoint(), F.Label(dispatchLabel), result); } return(result); }
public BoundForEachStatement(SyntaxNode syntax, ForEachEnumeratorInfo enumeratorInfoOpt, Conversion elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray <LocalSymbol> iterationVariables, BoundExpression expression, BoundForEachDeconstructStep deconstructionOpt, BoundStatement body, bool @checked, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) : this(syntax, enumeratorInfoOpt, elementConversion, iterationVariableType, iterationVariables, iterationErrorExpressionOpt : null, expression, deconstructionOpt, body, @checked, breakLabel, continueLabel, hasErrors) { }