use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project checker-framework by typetools.
the class MustCallConsistencyAnalyzer method propagateObligationsToSuccessorBlocks.
/**
* Propagates a set of Obligations to successors, and performs consistency checks when variables
* are going out of scope.
*
* <p>The basic algorithm loops over the successor blocks of the current block. For each
* successor, it checks every Obligation in obligations. If the successor is an exit block or all
* of an Obligation's resource aliases might be going out of scope, then a consistency check
* occurs (with two exceptions, both related to temporary variables that don't actually get
* assigned; see code comments for details) and an error is issued if it fails. If the successor
* is any other kind of block and there is information about at least one of the Obligation's
* aliases in the successor store (i.e. the resource itself definitely does not go out of scope),
* then the Obligation is passed forward to the successor ("propagated") with any definitely
* out-of-scope aliases removed from its resource alias set.
*
* @param obligations Obligations for the current block
* @param currentBlock the current block
* @param visited block-Obligations pairs already analyzed or already on the worklist
* @param worklist current worklist
*/
private void propagateObligationsToSuccessorBlocks(Set<Obligation> obligations, Block currentBlock, Set<BlockWithObligations> visited, Deque<BlockWithObligations> worklist) {
List<Node> currentBlockNodes = currentBlock.getNodes();
// loop performs a consistency check for that Obligation.
for (Pair<Block, @Nullable TypeMirror> successorAndExceptionType : getSuccessorsExceptIgnoredExceptions(currentBlock)) {
Block successor = successorAndExceptionType.first;
// If nonnull, currentBlock is an ExceptionBlock.
TypeMirror exceptionType = successorAndExceptionType.second;
// successorObligations eventually contains the Obligations to propagate to successor. The
// loop below mutates it.
Set<Obligation> successorObligations = new LinkedHashSet<>();
// A detailed reason to give in the case that the last resource alias of an Obligation
// goes out of scope without a called-methods type that satisfies the corresponding
// must-call obligation along the current control-flow edge. Computed here for efficiency;
// used in the loop over the Obligations, below.
String exitReasonForErrorMessage = exceptionType == null ? // doesn't seem to provide additional helpful information.
"regular method exit" : "possible exceptional exit due to " + ((ExceptionBlock) currentBlock).getNode().getTree() + " with exception type " + exceptionType;
// Computed outside the Obligation loop for efficiency.
CFStore regularStoreOfSuccessor = analysis.getInput(successor).getRegularStore();
for (Obligation obligation : obligations) {
// This boolean is true if there is no evidence that the Obligation does not go out of
// scope - that is, if there is definitely a resource alias that is in scope in the
// successor.
boolean obligationGoesOutOfScopeBeforeSuccessor = true;
for (ResourceAlias resourceAlias : obligation.resourceAliases) {
if (aliasInScopeInSuccessor(regularStoreOfSuccessor, resourceAlias)) {
obligationGoesOutOfScopeBeforeSuccessor = false;
break;
}
}
// should occur.
if (successor.getType() == BlockType.SPECIAL_BLOCK || /* special blocks are exit blocks */
obligationGoesOutOfScopeBeforeSuccessor) {
MustCallAnnotatedTypeFactory mcAtf = typeFactory.getTypeFactoryOfSubchecker(MustCallChecker.class);
// are ignored. Whether exceptionType is null captures the logic of both of these cases.
if (exceptionType != null) {
Node exceptionalNode = NodeUtils.removeCasts(((ExceptionBlock) currentBlock).getNode());
LocalVariableNode tmpVarForExcNode = typeFactory.getTempVarForNode(exceptionalNode);
if (tmpVarForExcNode != null && obligation.resourceAliases.size() == 1 && obligation.canBeSatisfiedThrough(tmpVarForExcNode)) {
continue;
}
}
// unwrapped at various points in the analysis.
if (currentBlockNodes.size() == 1 && inCast(currentBlockNodes.get(0))) {
successorObligations.add(obligation);
continue;
}
// being resolved some other way.
if (obligation.derivedFromMustCallAlias()) {
checker.reportError(obligation.resourceAliases.asList().get(0).tree, "mustcallalias.out.of.scope", exitReasonForErrorMessage);
continue;
}
// Which stores from the called-methods and must-call checkers are used in
// the consistency check varies depending on the context. The rules are:
// 1. if the current block has no nodes (and therefore the store must come from a block
// rather than a node):
// 1a. if there is information about any alias in the resource alias set
// in the successor store, use the successor's CM and MC stores, which
// contain whatever information is true after this block finishes.
// 1b. if there is not any information about any alias in the resource alias
// set in the successor store, use the current blocks' CM and MC stores,
// which contain whatever information is true before this (empty) block.
// 2. if the current block has one or more nodes, always use the CM store after
// the last node. To decide which MC store to use:
// 2a. if the last node in the block is the invocation of an @CreatesMustCallFor
// method that might throw an exception, and the consistency check is for
// an exceptional path, use the MC store immediately before the method invocation,
// because the method threw an exception rather than finishing and therefore did
// not actually create any must-call obligation, so the MC store after might
// contain must-call obligations that do not need to be fulfilled along this path.
// 2b. in all other cases, use the MC store from after the last node in the block.
CFStore mcStore, cmStore;
if (currentBlockNodes.size() == 0) /* currentBlock is special or conditional */
{
cmStore = obligationGoesOutOfScopeBeforeSuccessor ? // 1a. (CM)
analysis.getInput(currentBlock).getRegularStore() : // 1b. (CM)
regularStoreOfSuccessor;
mcStore = mcAtf.getStoreForBlock(obligationGoesOutOfScopeBeforeSuccessor, // 1a. (MC)
currentBlock, // 1b. (MC)
successor);
} else {
// In this case, current block has at least one node.
// Use the called-methods store immediately after the last node in currentBlock.
// 2. (CM)
Node last = currentBlockNodes.get(currentBlockNodes.size() - 1);
cmStore = typeFactory.getStoreAfter(last);
// an exception. Otherwise, use the store after.
if (exceptionType != null && isInvocationOfCreatesMustCallForMethod(last)) {
// 2a. (MC)
mcStore = mcAtf.getStoreBefore(last);
} else {
// 2b. (MC)
mcStore = mcAtf.getStoreAfter(last);
}
}
checkMustCall(obligation, cmStore, mcStore, exitReasonForErrorMessage);
} else {
// In this case, there is info in the successor store about some alias in the Obligation.
// Handles the possibility that some resource in the Obligation may go out of scope.
Set<ResourceAlias> copyOfResourceAliases = new LinkedHashSet<>(obligation.resourceAliases);
copyOfResourceAliases.removeIf(alias -> !aliasInScopeInSuccessor(regularStoreOfSuccessor, alias));
successorObligations.add(new Obligation(copyOfResourceAliases));
}
}
propagate(new BlockWithObligations(successor, successorObligations), visited, worklist);
}
}
use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project checker-framework by typetools.
the class BackwardAnalysisImpl method addStoreAfter.
/**
* Add a store after the basic block {@code pred} by merging with the existing stores for that
* location.
*
* @param pred the basic block
* @param node the node of the basic block {@code b}
* @param s the store being added
* @param addBlockToWorklist whether the basic block {@code b} should be added back to {@code
* Worklist}
*/
protected void addStoreAfter(Block pred, @Nullable Node node, S s, boolean addBlockToWorklist) {
// If the block pred is an exception block, decide whether the block of passing node is an
// exceptional successor of the block pred
TypeMirror excSuccType = getSuccExceptionType(pred, node);
if (excSuccType != null) {
if (isIgnoredExceptionType(excSuccType)) {
return;
}
// If the block of passing node is an exceptional successor of Block pred, propagate
// store to the exceptionStores. Currently it doesn't track the label of an
// exceptional edge from exception block to its exceptional successors in backward
// direction. Instead, all exception stores of exceptional successors of an
// exception block will merge to one exception store at the exception block
ExceptionBlock ebPred = (ExceptionBlock) pred;
S exceptionStore = exceptionStores.get(ebPred);
S newExceptionStore = (exceptionStore != null) ? exceptionStore.leastUpperBound(s) : s;
if (!newExceptionStore.equals(exceptionStore)) {
exceptionStores.put(ebPred, newExceptionStore);
inputs.put(ebPred, new TransferInput<V, S>(node, this, newExceptionStore));
addBlockToWorklist = true;
}
} else {
S predOutStore = getStoreAfter(pred);
S newPredOutStore = (predOutStore != null) ? predOutStore.leastUpperBound(s) : s;
if (!newPredOutStore.equals(predOutStore)) {
outStores.put(pred, newPredOutStore);
inputs.put(pred, new TransferInput<>(node, this, newPredOutStore));
addBlockToWorklist = true;
}
}
if (addBlockToWorklist) {
addToWorklist(pred);
}
}
use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project checker-framework by typetools.
the class BackwardAnalysisImpl method runAnalysisFor.
@Override
public S runAnalysisFor(@FindDistinct Node node, Analysis.BeforeOrAfter preOrPost, TransferInput<V, S> blockTransferInput, IdentityHashMap<Node, V> nodeValues, Map<TransferInput<V, S>, IdentityHashMap<Node, TransferResult<V, S>>> analysisCaches) {
Block block = node.getBlock();
assert block != null : "@AssumeAssertion(nullness): invariant";
Node oldCurrentNode = currentNode;
if (isRunning) {
assert currentInput != null : "@AssumeAssertion(nullness): invariant";
return currentInput.getRegularStore();
}
isRunning = true;
try {
switch(block.getType()) {
case REGULAR_BLOCK:
{
RegularBlock rBlock = (RegularBlock) block;
// Apply transfer function to contents until we found the node we are looking for.
TransferInput<V, S> store = blockTransferInput;
List<Node> nodeList = rBlock.getNodes();
ListIterator<Node> reverseIter = nodeList.listIterator(nodeList.size());
while (reverseIter.hasPrevious()) {
Node n = reverseIter.previous();
setCurrentNode(n);
if (n == node && preOrPost == Analysis.BeforeOrAfter.AFTER) {
return store.getRegularStore();
}
// Copy the store to avoid changing other blocks' transfer inputs in
// {@link #inputs}
TransferResult<V, S> transferResult = callTransferFunction(n, store.copy());
if (n == node) {
return transferResult.getRegularStore();
}
store = new TransferInput<>(n, this, transferResult);
}
throw new BugInCF("node %s is not in node.getBlock()=%s", node, block);
}
case EXCEPTION_BLOCK:
{
ExceptionBlock eb = (ExceptionBlock) block;
if (eb.getNode() != node) {
throw new BugInCF("Node should be equal to eb.getNode(). But get: node: " + node + "\teb.getNode(): " + eb.getNode());
}
if (preOrPost == Analysis.BeforeOrAfter.AFTER) {
return blockTransferInput.getRegularStore();
}
setCurrentNode(node);
// Copy the store to avoid changing other blocks' transfer inputs in {@link #inputs}
TransferResult<V, S> transferResult = callTransferFunction(node, blockTransferInput.copy());
// Merge transfer result with the exception store of this exceptional block
S exceptionStore = exceptionStores.get(eb);
return exceptionStore == null ? transferResult.getRegularStore() : transferResult.getRegularStore().leastUpperBound(exceptionStore);
}
default:
// Only regular blocks and exceptional blocks can hold nodes.
throw new BugInCF("Unexpected block type: " + block.getType());
}
} finally {
setCurrentNode(oldCurrentNode);
isRunning = false;
}
}
use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project bazel by bazelbuild.
the class ControlFlowGraph method getSuccessors.
/**
* Get a list of all successor Blocks for cur
* @param cur
* @return A Deque of successor Blocks
*/
private Deque<Block> getSuccessors(Block cur) {
Deque<Block> succs = new LinkedList<>();
if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
ConditionalBlock ccur = ((ConditionalBlock) cur);
succs.add(ccur.getThenSuccessor());
succs.add(ccur.getElseSuccessor());
} else {
assert cur instanceof SingleSuccessorBlock;
Block b = ((SingleSuccessorBlock) cur).getSuccessor();
if (b != null) {
succs.add(b);
}
}
if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
ExceptionBlock ecur = (ExceptionBlock) cur;
for (Set<Block> exceptionSuccSet : ecur.getExceptionalSuccessors().values()) {
succs.addAll(exceptionSuccSet);
}
}
return succs;
}
use of org.checkerframework.dataflow.cfg.block.ExceptionBlock in project bazel by bazelbuild.
the class ControlFlowGraph method getAllBlocks.
/**
* @return The set of all basic block in this control flow graph.
*/
public Set<Block> getAllBlocks() {
Set<Block> visited = new HashSet<>();
Queue<Block> worklist = new LinkedList<>();
Block cur = entryBlock;
visited.add(entryBlock);
// traverse the whole control flow graph
while (true) {
if (cur == null)
break;
Queue<Block> succs = new LinkedList<>();
if (cur.getType() == BlockType.CONDITIONAL_BLOCK) {
ConditionalBlock ccur = ((ConditionalBlock) cur);
succs.add(ccur.getThenSuccessor());
succs.add(ccur.getElseSuccessor());
} else {
assert cur instanceof SingleSuccessorBlock;
Block b = ((SingleSuccessorBlock) cur).getSuccessor();
if (b != null) {
succs.add(b);
}
}
if (cur.getType() == BlockType.EXCEPTION_BLOCK) {
ExceptionBlock ecur = (ExceptionBlock) cur;
for (Set<Block> exceptionSuccSet : ecur.getExceptionalSuccessors().values()) {
succs.addAll(exceptionSuccSet);
}
}
for (Block b : succs) {
if (!visited.contains(b)) {
visited.add(b);
worklist.add(b);
}
}
cur = worklist.poll();
}
return visited;
}
Aggregations