/* * This file is part of the X10 project (http://x10-lang.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.opensource.org/licenses/eclipse-1.0.php * * This file was originally derived from the Polyglot extensible compiler framework. * * (C) Copyright 2000-2007 Polyglot project group, Cornell University * (C) Copyright IBM Corporation 2007-2012. */ package polyglot.visit; import java.util.*; import polyglot.ast.*; import polyglot.frontend.Job; import polyglot.types.FunctionDef; import polyglot.types.MethodDef; import polyglot.types.SemanticException; import polyglot.types.TypeSystem; import polyglot.visit.DataFlow.Item; import polyglot.visit.FlowGraph.EdgeKey; import x10.ast.Closure; import x10.errors.Errors; /** * Visitor which checks that all (terminating) paths through a * method must return. */ public class ExitChecker extends DataFlow { protected CodeNode code; public ExitChecker(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf, false /* backward analysis */); } protected FlowGraph initGraph(CodeNode code, Term root) { boolean returnsValue; this.code = code; if (code instanceof MethodDecl) { MethodDecl d = (MethodDecl) code; if (! d.methodDef().returnType().get().isVoid()) { return super.initGraph(code, root); } } if (code instanceof Closure) { Closure d = (Closure) code; if (! d.closureDef().returnType().get().isVoid()) { return super.initGraph(code, root); } } return null; } public Item createInitialItem(FlowGraph graph, Term node, boolean entry) { return DataFlowItem.EXITS; } protected static class DataFlowItem extends Item { public final boolean exits; // whether all paths leaving this node lead to an exit protected DataFlowItem(boolean exits) { this.exits = exits; } public static final DataFlowItem EXITS = new DataFlowItem(true); public static final DataFlowItem DOES_NOT_EXIT = new DataFlowItem(false); public String toString() { return "exits=" + exits; } public boolean equals(Object o) { if (o instanceof DataFlowItem) { return this.exits == ((DataFlowItem)o).exits; } return false; } public int hashCode() { return (exits ? 5235 : 8673); } } public Map<EdgeKey, Item> flow(Item in, FlowGraph graph, Term n, boolean entry, Set<EdgeKey> succEdgeKeys) { // If every path from the exit node to the entry goes through a return, // we're okay. So make the exit bit false at exit and true at every return; // the confluence operation is &&. // We deal with exceptions specially, and assume that any exception // edge to the exit node is OK. if (n instanceof Return) { return itemToMap(DataFlowItem.EXITS, succEdgeKeys); } if (n == graph.root() && !entry) { // all exception edges to the exit node are regarded as exiting // correctly. Make sure non-exception edges have the // exit bit false. Map<EdgeKey, Item> m = itemToMap(DataFlowItem.EXITS, succEdgeKeys); if (succEdgeKeys.contains(FlowGraph.EDGE_KEY_OTHER)) { m.put(FlowGraph.EDGE_KEY_OTHER, DataFlowItem.DOES_NOT_EXIT); } if (succEdgeKeys.contains(FlowGraph.EDGE_KEY_TRUE)) { m.put(FlowGraph.EDGE_KEY_TRUE, DataFlowItem.DOES_NOT_EXIT); } if (succEdgeKeys.contains(FlowGraph.EDGE_KEY_FALSE)) { m.put(FlowGraph.EDGE_KEY_FALSE, DataFlowItem.DOES_NOT_EXIT); } return m; } return itemToMap(in, succEdgeKeys); } public Item confluence(List<Item> inItems, Term node, boolean entry, FlowGraph graph) { // all paths must have an exit for (Item item : inItems) { if (!((DataFlowItem)item).exits) { return DataFlowItem.DOES_NOT_EXIT; } } return DataFlowItem.EXITS; } public void check(FlowGraph graph, Term n, boolean entry, Item inItem, Map<EdgeKey, Item> outItems) { // Check for statements not on the path to exit; compound // statements are allowed to be off the path. (e.g., "{ return; }" // or "while (true) S"). If a compound statement is truly // unreachable, one of its sub-statements will be also and we will // report an error there. if (n == graph.root() && entry) { if (outItems != null && !outItems.isEmpty()) { // due to the flow equations, all DataFlowItems in the outItems map // are the same, so just take the first one. DataFlowItem outItem = (DataFlowItem)outItems.values().iterator().next(); if (outItem != null && !outItem.exits) { if (code.codeDef() instanceof FunctionDef) { FunctionDef fd = (FunctionDef) code.codeDef(); String designator = (fd instanceof MethodDef) ? "Method" : "Closure"; reportError(new Errors.MustReturnValueOfType(designator, fd, code.codeDef().errorPosition())); } else { reportError(new Errors.MissingReturnStatement(code.codeDef().errorPosition())); } } } } } }