package x10.visit; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.Stack; import java.util.TreeSet; import polyglot.ast.FlagsNode_c; import polyglot.ast.Node; import polyglot.ast.LocalDecl; import polyglot.ast.FieldDecl; import polyglot.util.Position; import polyglot.util.ErrorInfo; import polyglot.visit.NodeVisitor; import polyglot.frontend.Job; import polyglot.main.Reporter; import polyglot.types.SemanticException; import x10.ast.AnnotationNode_c; import x10.ast.X10Formal_c; public class PositionInvariantChecker extends NodeVisitor { private final Job job; private final Reporter reporter; private final String previousGoalName; private final boolean performOverlapCheck; private final Stack<Set<Node>> children; public PositionInvariantChecker(Job job, String previousName) { this(job, previousName, false); } public PositionInvariantChecker(Job job, String previousName, boolean performOverlapCheck) { this.job = job; this.reporter = job.extensionInfo().getOptions().reporter; previousGoalName = previousName; this.performOverlapCheck = performOverlapCheck; if (this.performOverlapCheck) { children= new Stack<Set<Node>>(); } else { children= null; } } public Node visitEdgeNoOverride(Node parent, Node n) { if (performOverlapCheck) { children.push(new HashSet<Node>()); } if (reporter.should_report(Reporter.PositionInvariantChecker, 2)) reporter.report(2, "Checking invariants for: " + n); String m = checkInvariants(parent, n); if (m!=null) { String msg; if (parent != null) { msg = "After goal "+previousGoalName+": "+ m+("!")+ (" parentPos=")+(parent.position())+ (" nPos=")+(n.position())+ (" parent=")+(parent)+ (" n=")+(n).toString(); } else { msg = "After goal "+previousGoalName+": "+ m+("!")+ (" nPos=")+(n.position())+ (" n=")+(n).toString(); } job.compiler().errorQueue().enqueue(ErrorInfo.INVARIANT_VIOLATION_KIND,msg,n.position()); } else { n.del().visitChildren(this); // if there is an error, I don't recurse to the children } if (performOverlapCheck) { checkChildExtents(); children.pop(); if (!children.empty()) { children.peek().add(n); } } return n; } private void checkChildExtents() { if (children.peek().size() == 0) { return; } Set<Node> orderedChildren= new TreeSet<Node>(new Comparator<Node>() { public int compare(Node n1, Node n2) { Position p1 = n1.position(); Position p2 = n2.position(); if (p1.offset() == p2.offset()) { return p1.endOffset() - p2.endOffset(); } return p1.offset() - p2.offset(); } }); orderedChildren.addAll(children.peek()); List<Node> orderedChildList = new LinkedList<Node>(); orderedChildList.addAll(orderedChildren); for(int i=0; i < orderedChildList.size() - 1; i++) { Node n1 = orderedChildList.get(i); Node n2 = orderedChildList.get(i+1); Position p1 = n1.position(); Position p2 = n2.position(); int end1 = p1.endOffset(); int end2 = p2.endOffset(); if (end2 < end1 && !p1.isCompilerGenerated() && !p2.isCompilerGenerated()) { String msg = "Positions overlap for non-synthetic nodes " + n1 + " and " + n2 + "\n position 1 = " + p1 + "\n position 2 = " + p2; job.compiler().errorQueue().enqueue(ErrorInfo.INVARIANT_VIOLATION_KIND, msg, p1); } } } private String checkInvariants(Node parent, Node n) { if (parent == null) return null; if (n == null) return "Cannot visit null"; Position pPos = parent.position(); Position nPos = n.position(); if ((pPos == null) || (nPos == null)) return "Positions must never be null"; if (nPos.isCompilerGenerated()) return null; if (pPos.isCompilerGenerated()) { //myAssert(nPos.isCompilerGenerated()) : "If your parent is COMPILER_GENERATED, then you must be COMPILER_GENERATED"; // todo: take from some ancestor return null; } if (!(equals(pPos.file(), nPos.file()))) return "Positions must have the same file"; if (!(equals(pPos.path(), nPos.path()))) return "Positions must have the same path"; /* todo: remove this. left to fix: Desugaring problems: 1) AmbMacroTypeNode_c (Macro expansion) 2) val p(i,j) = ... 3) public var x,y; E.g., after "ReassembleAST" stage in Array.x10: original source is: Point(reg.rank) parent=x10.array.Point{self.rank==reg.rank} n={[x10.array.Point.self.rank == reg.rank]} parentPos=C:\cygwin\home\Yoav\intellij\sourceforge\x10.runtime\src-x10\x10\array\Array.x10:132,12-26 nPos=C:\cygwin\home\Yoav\intellij\sourceforge\x10.runtime\src-x10\x10\lang\_.x10:73,45-58 A simple example that causes it: public class Hello { public val i=3, j=4; def m() { for (var i:Int=1,j:Int=2; i<10; i++); } } */ if (parent instanceof LocalDecl || parent instanceof FieldDecl || parent instanceof X10Formal_c) { // e.g., "for (val p(i,j): Point(2) in r) {" if (n instanceof FlagsNode_c) return null; if (n instanceof AnnotationNode_c) return null; //if (n instanceof ClosureCall_c) return; //val q1(m,n) = [0,1] as Point; } String s; if ((s=checkNumbers(pPos.line(), nPos.line(), true))!=null) return s; if ((s=checkNumbers(pPos.endLine(), nPos.endLine(), false))!=null) return s; if ((s=checkNumbers(pPos.offset(), nPos.offset(), true))!=null) return s; if ((s=checkNumbers(pPos.endOffset(), nPos.endOffset(), false))!=null) return s; if (pPos.line() == nPos.line()) if ((s=checkNumbers(pPos.column(), nPos.column(), true))!=null) return s; if (pPos.endLine() == nPos.endLine()) if ((s=checkNumbers(pPos.endColumn(), nPos.endColumn(), false))!=null) return s; return null; } public String checkNumbers(int pNum, int nNum, boolean isBeginning) { if (nNum<0 || pNum<0) return "We have unknown numbers"; if (isBeginning ? pNum>nNum : pNum<nNum) return "Illegal containment of positions"; return null; } public static boolean equals(Object o1, Object o2) { return (o1 == o2) || ((o1 != null) && (o2 != null) && (o1.equals(o2))); } }