// when two control points merge. Returns true if this state changed. public bool Join(FlowAnalysisLocalState other) { this.Merge(); other.Merge(); this.Reachable |= other.Reachable; return(this.Assigned.IntersectWith(other.Assigned)); }
/// <summary> /// Visit a boolean condition expression, where we will be wanting AssignedWhenTrue and /// AssignedWhenFalse. /// </summary> /// <param name="node"></param> protected void VisitCondition(BoundExpression node) { Debug.Assert(!this.state.Assigned.IsNull); Visit(node); // We implement the foundational rules missing from the language specification: // v is "definitely assigned when true" after a constant expression whose value is false. // v is "definitely assigned when false" after a constant expression whose value is true. // These rules are to be added to the language specification. // It was the lack of these foundational rules that led to the invention of the concept // of "unreachable expression" in the native compiler. if (IsConstantTrue(node)) { state.Merge(); this.state = new FlowAnalysisLocalState(this.state.Reachable, this.state.Assigned, BitArray.AllSet(nextVariableSlot)); } else if (IsConstantFalse(node)) { state.Merge(); this.state = new FlowAnalysisLocalState(this.state.Reachable, BitArray.AllSet(nextVariableSlot), this.state.Assigned); } else { state.Split(); } }
public override object VisitConditionalOperator(BoundConditionalOperator node, object arg) { VisitCondition(node.Condition); var consequenceState = new FlowAnalysisLocalState(this.state.Reachable, this.state.AssignedWhenTrue); var alternativeState = new FlowAnalysisLocalState(this.state.Reachable, this.state.AssignedWhenFalse); this.state = consequenceState; Visit(node.Consequence); consequenceState = this.state; this.state = alternativeState; Visit(node.Alternative); alternativeState = this.state; if (IsConstantTrue(node.Condition)) { this.state = consequenceState; // it may be a boolean state at this point. } else if (IsConstantFalse(node.Condition)) { this.state = alternativeState; // it may be a boolean state at this point. } else { // is may not be a boolean state at this point (5.3.3.28) this.state = consequenceState; this.state.Join(alternativeState); } return(null); }
public override object VisitForStatement(BoundForStatement node, object arg) { VisitStatement(node.Initializer); LoopHead(node); bool isTrue; bool isFalse; if (node.Condition != null) { isFalse = IsConstantFalse(node.Condition); isTrue = IsConstantTrue(node.Condition); VisitCondition(node.Condition); } else { isTrue = true; isFalse = false; this.state = new FlowAnalysisLocalState(this.state.Reachable, this.state.Assigned, BitArray.AllSet(nextVariableSlot)); } var bodyState = new FlowAnalysisLocalState(this.state.Reachable && !isFalse, this.state.AssignedWhenTrue); var breakState = new FlowAnalysisLocalState(this.state.Reachable && !isTrue, this.state.AssignedWhenFalse); var oldPendingBranches = this.pendingBranches; this.pendingBranches = ArrayBuilder <PendingBranch> .GetInstance(); this.state = bodyState; VisitStatement(node.Body); ResolveContinues(node.ContinueLabel); VisitStatement(node.Increment); LoopTail(node); ResolveBreaks(oldPendingBranches, breakState, node.BreakLabel); return(null); }
/// <summary> /// Return the flow analysis state associated with a label. /// </summary> /// <param name="label"></param> /// <returns></returns> FlowAnalysisLocalState?LabelState(LabelSymbol label) { FlowAnalysisLocalState?result; if (labels.TryGetValue(label, out result)) { return(result); } result = FlowAnalysisLocalState.UnreachableState(nextVariableSlot); labels.Add(label, result); return(result); }
public override object VisitWhileStatement(BoundWhileStatement node, object arg) { // while (node.Condition) { node.Body; node.ContinueLabel: } node.BreakLabel: LoopHead(node); VisitCondition(node.Condition); var bodyState = new FlowAnalysisLocalState(this.state.Reachable && !IsConstantFalse(node.Condition), this.state.AssignedWhenTrue); var breakState = new FlowAnalysisLocalState(this.state.Reachable && !IsConstantTrue(node.Condition), this.state.AssignedWhenFalse); var oldPendingBranches = SavePending(); this.state = bodyState; VisitStatement(node.Body); ResolveContinues(node.ContinueLabel); LoopTail(node); ResolveBreaks(oldPendingBranches, breakState, node.BreakLabel); return(null); }
public override object VisitDoStatement(BoundDoStatement node, object arg) { // do { statements; node.ContinueLabel: } while (node.Condition) node.BreakLabel: var oldPendingBranches = this.pendingBranches; this.pendingBranches = ArrayBuilder <PendingBranch> .GetInstance(); LoopHead(node); VisitStatement(node.Body); ResolveContinues(node.ContinueLabel); VisitCondition(node.Condition); var breakState = new FlowAnalysisLocalState(this.state.Reachable && !IsConstantTrue(node.Condition), this.state.AssignedWhenFalse); this.state = new FlowAnalysisLocalState(this.state.Reachable && !IsConstantFalse(node.Condition), this.state.AssignedWhenTrue); LoopTail(node); ResolveBreaks(oldPendingBranches, breakState, node.BreakLabel); return(null); }
public override object VisitIfStatement(BoundIfStatement node, object arg) { // 5.3.3.5 If statements VisitCondition(node.Condition); var trueState = new FlowAnalysisLocalState(this.state.Reachable && !IsConstantFalse(node.Condition), state.AssignedWhenTrue); var falseState = new FlowAnalysisLocalState(this.state.Reachable && !IsConstantTrue(node.Condition), state.AssignedWhenFalse); this.state = trueState; VisitStatement(node.Consequence); trueState = this.state; this.state = falseState; if (node.Alternative != null) { VisitStatement(node.Alternative); } this.state.Join(trueState); return(null); }
/// <summary> /// Used to resolve break statements in each statement form that has a break statement /// (loops, switch). /// </summary> /// <param name="oldPendingBranches"></param> /// <param name="breakState"></param> /// <param name="breakLabel"></param> private void ResolveBreaks(ArrayBuilder <PendingBranch> oldPendingBranches, FlowAnalysisLocalState breakState, LabelSymbol breakLabel) { foreach (var pending in this.pendingBranches) { switch (pending.Branch.Kind) { case BoundKind.BreakStatement: Debug.Assert((pending.Branch as BoundBreakStatement).Label == breakLabel); breakState.Join(pending.State); break; default: oldPendingBranches.Add(pending); break; } } this.pendingBranches.Free(); this.pendingBranches = oldPendingBranches; this.state = breakState; }
/// <summary> /// Set the current state to one that indicates that it is unreachable. /// </summary> protected void SetUnreachable() { this.state = FlowAnalysisLocalState.UnreachableState(this.nextVariableSlot); }
/// <summary> /// Perform a single pass of flow analysis. Note that after this pass, /// this.backwardBranchChanged indicates if a further pass is required. /// </summary> protected virtual void Scan() { // the entry point of a method is assumed reachable state = FlowAnalysisLocalState.ReachableState(); // label out parameters as not assigned. foreach (var parameter in method.Parameters) { if (parameter.RefKind == RefKind.Out) { SetSlotState(MakeSlot(parameter), initiallyAssignedVariables != null && initiallyAssignedVariables.Contains(parameter)); } else { // this code has no effect except in the region analysis APIs, which assign // variable slots to all parameters. int slot = VariableSlot(parameter); SetSlotState(slot, true); } } // TODO: if this is the body of an initial struct constructor, mark "this" as unassigned. this.backwardBranchChanged = false; if (this.Diagnostics != null) { this.Diagnostics.Free(); } this.Diagnostics = DiagnosticBag.GetInstance(); // clear reported diagnostics this.alreadyReported = BitArray.Empty; // no variables yet reported unassigned VisitStatement(block); // check that each local variable is used somewhere (or warn if it isn't) foreach (var symbol in variableBySlot) { if (symbol != null && symbol.Kind == SymbolKind.Local && !readVariables.Contains(symbol)) { Diagnostics.Add(writtenVariables.Contains(symbol) ? ErrorCode.WRN_UnreferencedVarAssg : ErrorCode.WRN_UnreferencedVar, symbol.Locations[0], symbol.Name); } } // check that each out parameter is definitely assigned at the end of the method if // there's more than one location, then the method is partial and we prefer to report an // out parameter in partial method error if (method.Locations.Count == 1) { foreach (ParameterSymbol parameter in method.Parameters) { if (parameter.RefKind == RefKind.Out) { var slot = VariableSlot(parameter); if (!state.IsAssigned(slot)) { ReportUnassignedOutParameter(parameter, null, method.Locations[0]); } foreach (PendingBranch returnBranch in pendingBranches) { if (!returnBranch.State.IsAssigned(slot)) { ReportUnassignedOutParameter(parameter, returnBranch.Branch.Syntax, null); } } } } } // TODO: handle "this" in struct constructor. }
// when two control points merge. Returns true if this state changed. public bool Join(FlowAnalysisLocalState other) { this.Merge(); other.Merge(); this.Reachable |= other.Reachable; return this.Assigned.IntersectWith(other.Assigned); }
/// <summary> /// Used to resolve break statements in each statement form that has a break statement /// (loops, switch). /// </summary> /// <param name="oldPendingBranches"></param> /// <param name="breakState"></param> /// <param name="breakLabel"></param> private void ResolveBreaks(ArrayBuilder<PendingBranch> oldPendingBranches, FlowAnalysisLocalState breakState, LabelSymbol breakLabel) { foreach (var pending in this.pendingBranches) { switch (pending.Branch.Kind) { case BoundKind.BreakStatement: Debug.Assert((pending.Branch as BoundBreakStatement).Label == breakLabel); breakState.Join(pending.State); break; default: oldPendingBranches.Add(pending); break; } } this.pendingBranches.Free(); this.pendingBranches = oldPendingBranches; this.state = breakState; }
/// <summary> /// Perform a single pass of flow analysis. Note that after this pass, /// this.backwardBranchChanged indicates if a further pass is required. /// </summary> protected virtual void Scan() { // the entry point of a method is assumed reachable state = FlowAnalysisLocalState.ReachableState(); // label out parameters as not assigned. foreach (var parameter in method.Parameters) { if (parameter.RefKind == RefKind.Out) { SetSlotState(MakeSlot(parameter), initiallyAssignedVariables != null && initiallyAssignedVariables.Contains(parameter)); } else { // this code has no effect except in the region analysis APIs, which assign // variable slots to all parameters. int slot = VariableSlot(parameter); SetSlotState(slot, true); } } // TODO: if this is the body of an initial struct constructor, mark "this" as unassigned. this.backwardBranchChanged = false; if (this.Diagnostics != null) this.Diagnostics.Free(); this.Diagnostics = DiagnosticBag.GetInstance(); // clear reported diagnostics this.alreadyReported = BitArray.Empty; // no variables yet reported unassigned VisitStatement(block); // check that each local variable is used somewhere (or warn if it isn't) foreach (var symbol in variableBySlot) { if (symbol != null && symbol.Kind == SymbolKind.Local && !readVariables.Contains(symbol)) { Diagnostics.Add(writtenVariables.Contains(symbol) ? ErrorCode.WRN_UnreferencedVarAssg : ErrorCode.WRN_UnreferencedVar, symbol.Locations[0], symbol.Name); } } // check that each out parameter is definitely assigned at the end of the method if // there's more than one location, then the method is partial and we prefer to report an // out parameter in partial method error if (method.Locations.Count == 1) { foreach (ParameterSymbol parameter in method.Parameters) { if (parameter.RefKind == RefKind.Out) { var slot = VariableSlot(parameter); if (!state.IsAssigned(slot)) { ReportUnassignedOutParameter(parameter, null, method.Locations[0]); } foreach (PendingBranch returnBranch in pendingBranches) { if (!returnBranch.State.IsAssigned(slot)) { ReportUnassignedOutParameter(parameter, returnBranch.Branch.Syntax, null); } } } } } // TODO: handle "this" in struct constructor. }