internal BitArray AssignedWhenFalse; // used only for boolean states public FlowAnalysisLocalState(bool reachable, BitArray assigned) { this.Reachable = reachable; this.Assigned = assigned; Debug.Assert(!assigned.IsNull); this.AssignedWhenTrue = this.AssignedWhenFalse = BitArray.Null; }
public void UnionWith(BitArray other) { int l = other.bits.Length; if (l > this.bits.Length) Array.Resize(ref bits, l + 1); for (int i = 0; i < l; i++) this.bits[i] |= other.bits[i]; }
public bool IntersectWith(BitArray other) { bool anyChanged = false; int l = other.bits.Length; if (l > this.bits.Length) Array.Resize(ref bits, l + 1); for (int i = 0; i < l; i++) { var thisBits = this.bits; var oldV = thisBits[i]; var newV = oldV & other.bits[i]; if (newV != oldV) { thisBits[i] = newV; anyChanged = true; } } return anyChanged; }
/// <summary> /// Turn this state into a non-conditional state (i.e. not to be used for control-flow). /// </summary> public void Merge() { if (!this.Assigned.IsNull) return; this.Assigned = this.AssignedWhenTrue; this.Assigned.IntersectWith(this.AssignedWhenFalse); this.AssignedWhenFalse = AssignedWhenTrue = BitArray.Null; }
/// <summary> /// Turn this state into a conditional (boolean) state, to be used for control-flow. /// </summary> public void Split() { if (this.Assigned.IsNull) return; this.AssignedWhenTrue = this.Assigned.Clone(); this.AssignedWhenFalse = this.Assigned; this.Assigned = BitArray.Null; }
protected virtual void Free() { Diagnostics.Free(); Diagnostics = null; pendingBranches.Free(); pendingBranches = null; this.alreadyReported = BitArray.Null; }
/// <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. }