/* * 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.*; import polyglot.util.Position; import polyglot.util.InternalCompilerError; import x10.util.CollectionFactory; import polyglot.visit.FlowGraph.EdgeKey; import x10.ast.Finish; import x10.ast.ParExpr; import x10.ast.Async_c; import x10.ast.Finish_c; import x10.ast.X10Cast; import x10.ast.Closure; import x10.ast.X10Formal_c; import x10.ast.X10Formal; import x10.ast.X10ClassBody_c; import x10.errors.Errors; import x10.extension.X10Ext_c; import x10.types.X10LocalDef; import x10.types.X10LocalDef_c; import x10.types.X10LocalInstance; import polyglot.types.TypeSystem; import x10.visit.Lowerer; import x10.visit.CheckEscapingThis; import x10.util.Synthesizer; /** * Visitor which checks that all local variables must be defined before use, * Fields (instance and static) are checked by CheckEscapingThis. * * The checking of the rules is implemented in the methods leaveCall(Node) * and check(FlowGraph, Term, Item, Item). * Yoav Zibin added: Adding finish-async initialization of var/val: See XTENLANG-1565. I kept the CFG without changes. So, an Async_c exit has two incoming edges: from the exit of the body and from the exit of the place. I changed in InitChecker's data structure: for each variable we now keep: [minSeq,maxSeq,minAsync,maxAsync] minSeq - the minimal number of times the variable is assigned in sequential code. maxSeq - the maximal number of times the variable is assigned in sequential code. minAsync - the minimal number of times the variable is assigned (in async or sequential code). maxAsync - the maximal number of times the variable is assigned (in async or sequential code). We keep the invariant that: minSeq<=minAsync && maxSeq<=maxAsync You can read from a variable only if minSeq>=1. A val at the end of a ctor must have: [1,1,1,1] Note that for our purposes, [0,0,1,1] and [0,1,1,1] carry the same information and restriction. (After a finish, both will become the same: [1,1,0,0].) Just having a boolean flag (i.e, [min,max,isAsync]) is ok, but I think it is less clear because of this example: var k:Int; k=1; async { k=2; } // can read from "k". The representation with a flag must be: [2,2,false] So, the meaning of the flag is: isAsync=false means that at least one assignment was in sequential code. Therefore, I prefer the representation as a quartet: [minSeq,maxSeq,minAsync,maxAsync] For example, in this simple program the flow is as follows: val i:Int; // i=[0,0,0,0] finish { // i=[0,0,0,0] async { i=42; // i=[1,1,1,1] } // i=[0,0,1,1] } // i=[1,1,1,1] Here is a more complicated example: var i:Int, j:Int, k:Int; val m:Int, n:Int, q:Int; i=1; // i:[1,1,1,1] finish { m=2; // m:[1,1,1,1] if (true) { async { n=3; i=4; j=5; k=6; q=7; // n:[1,1,1,1] i:[2,2,2,2] j:[1,1,1,1] k:[1,1,1,1] q:[1,1,1,1] } // n:[0,0,1,1] i:[1,1,2,2] j:[0,0,1,1] k:[0,0,1,1] q:[0,0,1,1] k=8; // k:[1,1,2,2] } else { // n:[0,0,0,0] m:[1,1,1,1] i:[1,1,1,1] j:[0,0,0,0] k:[0,0,0,0] q:[0,0,0,0] n=9; m=10; // n:[1,1,1,1] m:[2,2,2,2] } // k:[0,1,0,2] n:[0,1,1,1] m:[1,2,1,2] i:[1,1,1,2] j:[0,0,0,1] q:[0,0,0,1] k=11; // k:[1,2,1,3] } // k:[1,3,1,3] n:[1,1,1,1] m:[1,2,1,2] i:[1,2,1,2] j:[0,1,0,1] q:[0,1,0,1] j=12; // j:[1,2,1,2] // all (except q) are definitely-assigned now. "m" was assigned too many times. The exact rules are: Consider a join between two elements: [a,b,c,d] and [a',b',c',d']. 1) If join (like at the end of an IF): [min(a,a'), max(b,b'), min(c,c'), max(d,d')] 2) join at the end of an Async_c(PLACE,BODY): suppose that the first element has flown from the BODY and the second has flown from the PLACE (so a>=a', b>=b', ...). Then, the result is: [a',b', c, d] 3) join at the end of a Finish_c: you have only one incoming edge, and the result is: [c,d,c,d] Another way to write down these rules: S [a,b,c,d] S' [a',b',c',d'] 0) S; S' [a+a', b+b', c+c', d+d'] 1) if S else S [min(a,a'), max(b,b'), min(c,c'), max(d,d')] 2) async S [0,0,c,d] 3) finish S [c,d,c,d] For example (see variables "n", "i"): IfJoin([0,0,1,1],[1,1,1,1]) = [0,1,1,1] AsyncJoin([2,2,2,2],[1,1,1,1]) = [1,1,2,2] FinishJoin([0,1,1,1]) = [1,1,1,1] */ public final class InitChecker extends DataFlow { public static long TIME = 0; public static int ASYNC_INIT_COUNT = 0; public InitChecker(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf, true /* forward analysis */, true /* perform dataflow when when entering */); } private Map<Object, Set<LocalDef>> initLocals = new HashMap<Object, Set<LocalDef>>(); /** * Class representing the initialization counts of variables. The * different values of the counts that we are interested in are ZERO, * ONE and MANY. */ public enum InitCount { ZERO(0,"0"), ONE(1,"1"), MANY(2,"many"); public final int count; private final String name; private InitCount(int i, String name) { count = i; this.name = name; } public boolean isZero() { return this==ZERO; } public boolean isOne() { return this==ONE; } public String toString() { return name; } public InitCount increment() { if (count == 0) { return ONE; } return MANY; } public InitCount min(InitCount b) { return fromNum(Math.min(count,b.count)); } public InitCount max(InitCount b) { return fromNum(Math.max(count,b.count)); } public InitCount add(InitCount b) { return fromNum(count+b.count); } private static InitCount fromNum(int i) { assert i>=0 : i; switch (i) { case 0: return ZERO; case 1: return ONE; default: return MANY; } } } /** * Class to record counts of the minimum and maximum number of times * a local has been initialized or assigned to. */ public static class MinMaxInitCount { public final static MinMaxInitCount ZERO = new MinMaxInitCount(InitCount.ZERO,InitCount.ZERO,InitCount.ZERO,InitCount.ZERO); public final static MinMaxInitCount ONE = new MinMaxInitCount(InitCount.ONE,InitCount.ONE,InitCount.ONE,InitCount.ONE); private final boolean wasRead; // used in CheckEscapingThis for fields read on NonEscaping methods (not used for locals in InitChecker) private final InitCount minSeq, maxSeq, minAsync, maxAsync; public MinMaxInitCount(InitCount minSeq, InitCount maxSeq,InitCount minAsync, InitCount maxAsync) { this(minSeq, maxSeq,minAsync,maxAsync,false); } public MinMaxInitCount(InitCount minSeq, InitCount maxSeq,InitCount minAsync, InitCount maxAsync, boolean wasRead) { this.minSeq = minSeq; this.maxSeq = maxSeq; this.minAsync = minAsync; this.maxAsync = maxAsync; this.wasRead = wasRead; assert minSeq.count<=minAsync.count && maxSeq.count<=maxAsync.count; } public MinMaxInitCount increment() { // when a variable is sequentially assigned return new MinMaxInitCount(minSeq.increment(),maxSeq.increment(),minAsync.increment(),maxAsync.increment(), wasRead); } public InitCount getMin() { return minSeq; } public boolean isZero() { return equals(ZERO); } public boolean isOne() { return equals(ONE); } public boolean isIllegalVal() { return !isOne(); } public int hashCode() { return (wasRead?256:0) + minSeq.hashCode() * 64 + maxSeq.hashCode() * 16 + minAsync.hashCode() * 4 + maxAsync.hashCode(); } public String toString() { return "[ min: " + minSeq + "; max: " + maxSeq + "; minAsync: " + minAsync + "; maxAsync: " + maxAsync + "; wasRead="+wasRead+"]"; } public boolean equals(MinMaxInitCount maxInitCount) { return this.wasRead==maxInitCount.wasRead && this.minSeq==maxInitCount.minSeq && this.maxSeq==maxInitCount.maxSeq && this.minAsync==maxInitCount.minAsync && this.maxAsync==maxInitCount.maxAsync; } public boolean equals(Object o) { if (o instanceof MinMaxInitCount) { return equals((MinMaxInitCount) o); } return false; } public boolean isAsynInit() { return minSeq!=minAsync || maxSeq!=maxAsync; } public MinMaxInitCount finish() { return new MinMaxInitCount(minAsync,maxAsync,minAsync,maxAsync, wasRead);//[c,d,c,d] } public boolean isRead() { return wasRead; } public boolean isWrite() { return minAsync!=InitCount.ZERO; } public boolean isSeqWrite() { return minSeq!=InitCount.ZERO; } public static MinMaxInitCount build(boolean read,boolean write,boolean seqWrite) { final InitCount seqW = seqWrite ? InitCount.ONE : InitCount.ZERO; final InitCount w = write ? InitCount.ONE : InitCount.ZERO; return new MinMaxInitCount(seqW,seqW, w,w, read); } public MinMaxInitCount afterAssign() { return increment(); } public MinMaxInitCount afterRead() { return new MinMaxInitCount(minSeq, maxSeq,minAsync,maxAsync,wasRead || minSeq==InitCount.ZERO); } public MinMaxInitCount afterSeqBlock(MinMaxInitCount after) { return new MinMaxInitCount( minSeq.add(after.minSeq), maxSeq.add(after.maxSeq), minAsync.add(after.minAsync), maxAsync.add(after.maxAsync), wasRead || (!isSeqWrite() && after.wasRead)); } public MinMaxInitCount afterAsync(MinMaxInitCount initCount2, boolean isUncounted) { MinMaxInitCount initCount1 = this; // one flow must be smaller than the other (the one coming from the entry of the Async is // smaller or equal to the one coming after the BODY) MinMaxInitCount small = // is initCount1 the min? initCount1.minSeq.count<initCount2.minSeq.count ? initCount1 : initCount1.maxSeq.count<initCount2.maxSeq.count ? initCount1 : initCount1.minAsync.count<initCount2.minAsync.count ? initCount1 : initCount1.maxAsync.count<initCount2.maxAsync.count ? initCount1 : // is initCount2 the min? initCount1.minSeq.count>initCount2.minSeq.count ? initCount2 : initCount1.maxSeq.count>initCount2.maxSeq.count ? initCount2 : initCount1.minAsync.count>initCount2.minAsync.count ? initCount2 : initCount1.maxAsync.count>initCount2.maxAsync.count ? initCount2 : initCount1; // both are equal, so we just choose initCount1 MinMaxInitCount big = small==initCount1 ? initCount2 : initCount1; assert small.minSeq.count<=big.minSeq.count && small.maxSeq.count<=big.maxSeq.count && small.minAsync.count<=big.minAsync.count && small.maxAsync.count<=big.maxAsync.count; final MinMaxInitCount res = new MinMaxInitCount( small.minSeq, small.maxSeq, isUncounted ? small.minAsync : big.minAsync, big.maxAsync, small.wasRead || big.wasRead); return res; // [a',b', c, d] } public MinMaxInitCount afterIf(MinMaxInitCount o) { // normal join: [min(a,a'), max(b,b'), min(c,c'), max(d,d')] return new MinMaxInitCount( minSeq.min(o.minSeq), maxSeq.max(o.maxSeq), minAsync.min(o.minAsync), maxAsync.max(o.maxAsync), wasRead || o.wasRead); } public static MinMaxInitCount join(TypeSystem xts, VarDef v, Term node, boolean entry, MinMaxInitCount initCount1, MinMaxInitCount initCount2) { assert !(node instanceof Finish); if (initCount1 == null) return initCount2; if (initCount2 == null) return initCount1; if (!entry && node instanceof Async_c) { Async_c async = (Async_c) node; boolean isUncounted = Lowerer.isUncountedAsync(xts,async); //@Uncounted async S //is treated like this: //async if (flag) S //so the statement in S might or might not get executed. //Therefore even after a "finish" we still can't use anything assigned in S. if (!initCount1.equals(initCount2) && v instanceof X10LocalDef) { ((X10LocalDef)v).setAsyncInit(); } return initCount1.afterAsync(initCount2,isUncounted); } // normal join return initCount1.afterIf(initCount2); } } /** * Dataflow items for this dataflow are maps of VarInstances to counts * of the min and max number of times those locals have * been initialized. These min and max counts are then used to determine * if variables have been initialized before use, and that final variables * are not initialized too many times. * * This class is immutable. */ public static class BaseDataFlowItem<T extends VarDef> extends Item { public final Map<T, MinMaxInitCount> initStatus = CollectionFactory.newHashMap(); // map of VarDef to MinMaxInitCount public String toString() { return initStatus.toString(); } public boolean equals(Object o) { if (o instanceof BaseDataFlowItem) { return this.initStatus.equals(((BaseDataFlowItem)o).initStatus); } return false; } public int hashCode() { return (initStatus.hashCode()); } } public static class DataFlowItem extends BaseDataFlowItem<LocalDef> {} /** * Construct a flow graph for the <code>Expr</code> provided, and call * <code>dataflow(FlowGraph)</code>. Is also responsible for calling * <code>post(FlowGraph, Term)</code> after * <code>dataflow(FlowGraph)</code> has been called. * There is no need to push a CFG onto the stack, as dataflow is not * performed on entry in this analysis. */ protected void dataflow(Expr root) { // Build the control flow graph. FlowGraph g = new FlowGraph(root, forward); CFGBuilder v = createCFGBuilder(ts, g); v.visitGraph(); dataflow(g); post(g, root); } public Item createInitialItem(FlowGraph graph, Term node, boolean entry) { return createInitDFI(); } private DataFlowItem createInitDFI() { return new DataFlowItem(); } /** * The confluence operator for <code>Initializer</code>s and * <code>Constructor</code>s needs to be a * little special, as we are only concerned with non-exceptional flows in * these cases. * This method ensures that a slightly different confluence is performed * for these <code>Term</code>s, otherwise * <code>confluence(List, Term)</code> is called instead. */ protected Item confluence(List<Item> items, List<EdgeKey> itemKeys, Term node, boolean entry, FlowGraph graph) { if (node instanceof Initializer || node instanceof ConstructorDecl) { List<Item> filtered = filterItemsNonException(items, itemKeys); if (filtered.isEmpty()) { return createInitDFI(); } else if (filtered.size() == 1) { return (Item)filtered.get(0); } else { return confluence(filtered, node, entry, graph); } } return confluence(items, node, entry, graph); } /** * The confluence operator is essentially the union of all of the * inItems. However, if two or more of the initCount maps from * the inItems each have a MinMaxInitCounts entry for the same * VarInstance, the conflict must be resolved, by using the * minimum of all mins and the maximum of all maxs. */ public Item confluence(List<Item> inItems, Term node, boolean entry, FlowGraph graph) { // Resolve any conflicts pairwise. final DataFlowItem res = new DataFlowItem(); Map<LocalDef, MinMaxInitCount> m = null; for (Item itm : inItems) { if (m == null) { m = res.initStatus; m.putAll(((DataFlowItem)itm).initStatus); } else { Map<LocalDef, MinMaxInitCount> n = ((DataFlowItem)itm).initStatus; for (Map.Entry<LocalDef, MinMaxInitCount> e : n.entrySet()) { LocalDef v = e.getKey(); MinMaxInitCount initCount1 = m.get(v); MinMaxInitCount initCount2 = (MinMaxInitCount)e.getValue(); m.put(v, MinMaxInitCount.join((TypeSystem)ts,v,node,entry,initCount1, initCount2)); } } } return res; } protected Map<EdgeKey, Item> flow(List<Item> inItems, List<EdgeKey> inItemKeys, FlowGraph graph, Term n, boolean entry, Set<EdgeKey> edgeKeys) { return this.flowToBooleanFlow(inItems, inItemKeys, graph, n, entry, edgeKeys); } /** * Perform the appropriate flow operations for the Terms. This method * delegates to other appropriate methods in this class, for modularity. * * To summarize: * - Formals: declaration of a Formal param, just insert a new * MinMaxInitCount for the LocalInstance. * - LocalDecl: a declaration of a local variable, just insert a new * MinMaxInitCount for the LocalInstance as appropriate * based on whether the declaration has an initializer or not. * - Assign: if the LHS of the assign is a local var, then increment the min and max counts */ public Map<EdgeKey, Item> flow(Item trueItem, Item falseItem, Item otherItem, FlowGraph graph, Term n, boolean entry, Set<EdgeKey> succEdgeKeys) { Map<EdgeKey, Item> res = flow_(trueItem,falseItem,otherItem,graph,n,entry, succEdgeKeys); if (entry) return res; // We also need to mark the variable as "initialized in async" to allow // the backend to implement such initializations. final FlowGraph.Peer peer = graph.peer(n, Term.ENTRY); final Item inItem = peer.inItem(); if (inItem==null) return res; final DataFlowItem in = (DataFlowItem) inItem; final X10Ext_c ext = (X10Ext_c) n.ext(); for (Map.Entry<LocalDef, MinMaxInitCount> e : in.initStatus.entrySet()) { final MinMaxInitCount before = e.getValue(); final LocalDef v = e.getKey(); for (Item outItem : res.values()) { if (outItem==null) continue; final DataFlowItem out = (DataFlowItem) outItem; final MinMaxInitCount after = out.initStatus.get(v); if (after==null) continue; final Flags flags = v.flags(); if (!before.equals(after) && after.isOne() && flags !=null && flags.isFinal()) { if (ext.initVals ==null) ext.initVals = CollectionFactory.newHashSet(); ext.initVals.add(v); break; // optimization, cause we already added "v" } } } return res; } public Map<EdgeKey, Item> flow_(Item trueItem, Item falseItem, Item otherItem, FlowGraph graph, Term n, boolean entry, Set<EdgeKey> succEdgeKeys) { Item inItem = safeConfluence(trueItem, FlowGraph.EDGE_KEY_TRUE, falseItem, FlowGraph.EDGE_KEY_FALSE, otherItem, FlowGraph.EDGE_KEY_OTHER, n, entry, graph); if (entry) { return itemToMap(inItem, succEdgeKeys); } DataFlowItem inDFItem = ((DataFlowItem)inItem); Map<EdgeKey, Item> ret = null; if (n instanceof Formal) { // formal argument declaration. ret = flowFormal(inDFItem, graph, (Formal)n, succEdgeKeys); } else if (n instanceof LocalDecl) { // local variable declaration. ret = flowLocalDecl(inDFItem, graph, (LocalDecl)n, succEdgeKeys); } else if (n instanceof LocalAssign) { // assignment to a local variable ret = flowLocalAssign(inDFItem, graph, (LocalAssign)n, succEdgeKeys); } else if (n instanceof Expr && ((Expr)n).type().isBoolean() && (n instanceof Binary || n instanceof Unary)) { if (trueItem == null) trueItem = inDFItem; if (falseItem == null) falseItem = inDFItem; ret = flowBooleanConditions(trueItem, falseItem, inDFItem, graph, (Expr)n, succEdgeKeys); } else if (n instanceof ParExpr && ((ParExpr)n).type().isBoolean()) { if (trueItem == null) trueItem = inDFItem; if (falseItem == null) falseItem = inDFItem; return itemsToMap(trueItem, falseItem, inDFItem, succEdgeKeys); } else if (n instanceof Finish_c) { final DataFlowItem res = new DataFlowItem(); for (Map.Entry<LocalDef, MinMaxInitCount> e : inDFItem.initStatus.entrySet()) { final MinMaxInitCount before = e.getValue(); final MinMaxInitCount after = before.finish(); final LocalDef v = e.getKey(); res.initStatus.put(v, after); if (CheckEscapingThis.GATHER_STATS && before.isAsynInit() && v.flags().isFinal()) { System.out.println("Async local init="+v.position()); ASYNC_INIT_COUNT++; } } return itemToMap(res, succEdgeKeys); } if (ret != null) { return ret; } return itemToMap(inItem, succEdgeKeys); } /** * Perform the appropriate flow operations for declaration of a formal * parameter */ protected Map<EdgeKey, Item> flowFormal(DataFlowItem inItem, FlowGraph graph, Formal f, Set<EdgeKey> succEdgeKeys) { final DataFlowItem res = new DataFlowItem(); res.initStatus.putAll(inItem.initStatus); // a formal argument is always defined. res.initStatus.put(f.localDef(), MinMaxInitCount.ONE); for (Formal ff : ((X10Formal)f).vars()) res.initStatus.put(ff.localDef(), MinMaxInitCount.ONE); return itemToMap(res, succEdgeKeys); } /** * Perform the appropriate flow operations for declaration of a local * variable */ protected Map<EdgeKey, Item> flowLocalDecl(DataFlowItem inItem, FlowGraph graph, LocalDecl ld, Set<EdgeKey> succEdgeKeys) { final DataFlowItem res = new DataFlowItem(); res.initStatus.putAll(inItem.initStatus); MinMaxInitCount initCount; //if (initCount == null) { if (ld.init() != null) { // declaration of local var with initialization. initCount = MinMaxInitCount.ONE; } else { // declaration of local var with no initialization. initCount = MinMaxInitCount.ZERO; } res.initStatus.put(ld.localDef(), initCount); return itemToMap(res, succEdgeKeys); } /** * Perform the appropriate flow operations for assignment to a local * variable */ protected Map<EdgeKey, Item> flowLocalAssign(DataFlowItem inItem, FlowGraph graph, LocalAssign a, Set<EdgeKey> succEdgeKeys) { Local l = (Local) a.local(); final DataFlowItem res = new DataFlowItem(); res.initStatus.putAll(inItem.initStatus); MinMaxInitCount initCount = res.initStatus.get(l.localInstance().def()); // initcount could be null if the local is defined in the outer // class, or if we have not yet seen its declaration (i.e. the // local is used in its own initialization) if (initCount == null) { initCount = MinMaxInitCount.ZERO; } initCount = initCount.increment(); res.initStatus.put(l.localInstance().def(), initCount); return itemToMap(res, succEdgeKeys); } /** * Check that the conditions of initialization are not broken. * * To summarize the conditions: * - Local variables must be initialized before use, (i.e. min count > 0) * - Final local variables (including Formals) cannot be assigned to more * than once (i.e. max count <= 1) */ public void check(final FlowGraph graph, Term n, boolean entry, Item inItem, Map<EdgeKey, Item> outItems) { long start = System.currentTimeMillis(); DataFlowItem dfIn = (DataFlowItem)inItem; if (dfIn == null) { // There is no input data flow item. This can happen if we are // checking an unreachable term, and so no Items have flowed // through the term. For example, in the code fragment: // a: do { break a; } while (++i < 10); // the expression "++i < 10" is unreachable, but the as there is // no unreachable statement, the Java Language Spec permits it. // Set inItem to a default Item dfIn = createInitDFI(); } x10.ExtensionInfo x10Info = (x10.ExtensionInfo) job().extensionInfo(); x10Info.stats.incrFrequency("InitChecker.check", 1); x10Info.stats.startTiming("InitChecker.1","InitChecker.1"); DataFlowItem dfOut; if (!entry && outItems != null && !outItems.isEmpty()) { // due to the flow equations, all DataFlowItems in the outItems map // are the same, so just take the first one. dfOut = (DataFlowItem)outItems.values().iterator().next(); if (n instanceof Local) { final Local l = (Local) n; checkLocal(l.localInstance().def(), dfIn, l.reachable(), n.position()); } else if (n instanceof X10Cast) { X10Cast cast = (X10Cast)n; // convert constraint to Expr final Set<VarDef> exprs = Synthesizer.getLocals(cast.castType()); for (VarDef e : exprs) if (e instanceof LocalDef) checkLocal((LocalDef)e, dfIn, true, n.position()); } else if (n instanceof LocalAssign) { checkLocalAssign(graph, (LocalAssign)n, dfIn, dfOut); } else if (n instanceof Closure) { checkCode(getCurrKey(n), dfIn); } else if (n instanceof ClassBody) { ClassBody cb = (ClassBody) n; if (cb.members().size()>0) { checkCode(getCurrKey(cb.members().get(0)), dfIn); } } } else { // this local assign node has not had data flow performed over it. // probably a node in a finally block. Just ignore it. } x10Info.stats.stopTiming(); TIME += Math.abs(System.currentTimeMillis()-start); } private void reportVarNotInit(Name n, Position p) { reportError(new Errors.MayNotHaveBeenInitialized(n, p)); } /** * Check that the local variable <code>l</code> is used correctly. */ protected void checkLocal(LocalDef l, DataFlowItem dfIn, boolean isReachable, Position p) { if (((X10LocalInstance)l.asInstance()).error()!=null) return; if (l instanceof X10LocalDef_c && ((X10LocalDef_c) l).hidden()) return; MinMaxInitCount initCount = dfIn.initStatus.get(l); if (initCount == null) { // check the outer local was init Set<LocalDef> initDefs = getInitDefs(); if ((l.flags().isFinal()) && // "var" fields are already reported: "Local variable is accessed from an inner class or a closure, and must be declared final." (initDefs==null || !initDefs.contains(l))) { reportError(new Errors.LocalVariableMustBeInitializedBeforeClassDeclaration(l.name(),p)); } } else if (initCount.getMin().isZero()) { // the local variable may not have been initialized. // However, we only want to complain if the local is reachable if (isReachable) { reportVarNotInit(l.name(),p); } } } protected void checkLocalInstanceInit(LocalDef li, DataFlowItem dfIn, Position pos) { if (((X10LocalInstance)li.asInstance()).error()!=null) return; MinMaxInitCount initCount = dfIn.initStatus.get(li); if (initCount != null && initCount.getMin().isZero()) { // the local variable may not have been initialized. reportVarNotInit(li.name(), pos); } } /** * Check that the assignment to a local variable is correct. */ protected void checkLocalAssign(FlowGraph graph, LocalAssign a, DataFlowItem dfIn, DataFlowItem dfOut) { X10LocalInstance instance = (X10LocalInstance) ((Local) a.local()).localInstance(); if (instance.error()!=null) return; LocalDef li = instance.def(); MinMaxInitCount initCount = dfIn.initStatus.get(li); if (initCount==null) { reportError(new Errors.FinalLocalVariableCannotBeAssignedTo(li.name(),a.position())); } else if (li.flags().isFinal() && !initCount.isZero()) { reportError(new Errors.FinalVariableAlreadyInitialized(li.name(), a.position())); } } /** * Check that the set of <code>LocalInstance</code>s * <code>localsUsed</code>, which is the set of locals used in the inner * class declared by <code>cb</code> * are initialized before the class declaration. */ protected void checkCode(Object key,DataFlowItem dfIn) { Set<LocalDef> initDefs = getInitDefs(); if (initDefs==null) initDefs = Collections.EMPTY_SET; Set<LocalDef> cbInitDefs = new HashSet<LocalDef>(initDefs); for (Map.Entry<LocalDef, MinMaxInitCount> en : dfIn.initStatus.entrySet()) { final LocalDef def = en.getKey(); if (def.flags().isFinal() && en.getValue().isOne()) { cbInitDefs.add(def); } } initLocals.put(key,cbInitDefs); } private CodeNode currCode = null; private Set<LocalDef> getInitDefs() { if (currCode==null) return null; Object key = getCurrKey(currCode); Set<LocalDef> res = initLocals.get(key); if (res!=null) return res; if (key instanceof ClassDef) { ClassDef def = (ClassDef) key; while (true) { def = Types.get(def.outer()); if (def==null) return null; res = initLocals.get(def); if (res!=null) return res; } } return null; } private Object getCurrKey(Node currCode) { // either ClassDef or Closure if (currCode instanceof ClassMember) { return ((ClassType)Types.get(((ClassMember)currCode).memberDef().container())).def(); } if (currCode instanceof Closure) return currCode; throw new InternalCompilerError("Unexpected currCode="+currCode); } protected FlowGraph initGraph(CodeNode code, Term root) { currCode = code; return super.initGraph(code, root); } }