From 860e66c3577465cf16ec56af0f247eff2f930737 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 1 Feb 2024 13:19:05 +0100
Subject: [PATCH 01/85] Started work on RASI generator
---
.../invariants/AbstractProcess.java | 126 ++++++++++++++++++
.../invariants/AbstractState.java | 30 +++++
.../invariants/ExecutableState.java | 3 +
.../invariants/ExecutionState.java | 8 ++
.../systemctocol/invariants/Generator.java | 47 +++++++
.../variables/ConcreteVariable.java | 20 +++
.../invariants/variables/FieldVariable.java | 27 ++++
.../variables/SequenceIndexVariable.java | 44 ++++++
8 files changed, 305 insertions(+)
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/variables/SequenceIndexVariable.java
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
new file mode 100644
index 0000000000..9a9b861ba7
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
@@ -0,0 +1,126 @@
+package vct.parsers.transform.systemctocol.invariants;
+
+import vct.col.ast.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AbstractProcess {
+
+ protected RunMethod process_method;
+
+ public AbstractProcess(RunMethod method) {
+ this.process_method = method;
+ }
+
+ public List> simulate(int[] program_counter) {
+ return null;
+ }
+
+ private List> small_step() {
+ return null;
+ }
+
+ /**
+ * Returns the current statement stack at the execution state given by the program counter. Iteratively resolves the
+ * context of each statement and returns a stack with the highest-level container at position 0 and the statement
+ * currently to be evaluated at position n - 1.
+ *
+ * @param program_counter A list of indices showing which point in each container points to the right statement
+ * @return A list of container statements, with the desired statement in the last position
+ */
+ private List> resolve(int[] program_counter) {
+ List> stack = new ArrayList<>();
+ Statement context = process_method.body().get();
+ stack.add(context);
+ for(int index : program_counter) {
+ context = get_at_index(context, index);
+ stack.add(context);
+ }
+ return stack;
+ }
+
+ /**
+ * Takes a statement that (can) contain other statements as well as an index and returns the given index statement
+ * contained within the container.
+ *
+ * @param container Container statement
+ * @param index Index of desired contained statement
+ * @return Statement at index index
within the body of container
+ */
+ private Statement get_at_index(Statement container, int index) {
+ // Ignores the following:
+ // TryCatchFinally
+ // Synchronized
+ // ParInvariant
+ // ParAtomic
+ // VecBlock
+ // WandPackage
+ // ModelDo
+ if (container instanceof PVLBranch pvl_branch) {
+ return pvl_branch.branches().apply(index)._2();
+ }
+ else if (container instanceof PVLLoop pvl_loop) {
+ return switch (index) {
+ case 0 -> pvl_loop.init();
+ case 1 -> pvl_loop.body();
+ case 2 -> pvl_loop.update();
+ default -> throw new IndexOutOfBoundsException("Loop index must at most be 2.");
+ };
+ }
+ else if (container instanceof InvokeProcedure invoke_procedure) {
+ if (index == 0) {
+ return invoke_procedure.ref().decl().body().get();
+ }
+ else throw new IndexOutOfBoundsException("Invalid index for procedure invocation.");
+ }
+ else if (container instanceof InvokeConstructor invoke_constructor) {
+ if (index == 0) {
+ return invoke_constructor.ref().decl().body().get();
+ }
+ else throw new IndexOutOfBoundsException("Invalid index for constructor invocation.");
+ }
+ else if (container instanceof InvokeMethod invoke_method) {
+ if (index == 0) {
+ return invoke_method.ref().decl().body().get();
+ }
+ else throw new IndexOutOfBoundsException("Invalid index for method invocation.");
+ }
+ else if (container instanceof Block block) {
+ return block.statements().apply(index);
+ }
+ else if (container instanceof Scope scope) {
+ if (index == 0) {
+ return scope.body();
+ }
+ else throw new IndexOutOfBoundsException("Invalid index for scope.");
+ }
+ else if (container instanceof Branch branch) {
+ return branch.branches().apply(index)._2();
+ }
+ else if (container instanceof IndetBranch indet_branch) {
+ return indet_branch.branches().apply(index);
+ }
+ else if (container instanceof Switch switch_stmt) {
+ if (index == 0) {
+ return switch_stmt.body();
+ }
+ else throw new IndexOutOfBoundsException("Invalid index for switch statement.");
+ }
+ else if (container instanceof Loop loop) {
+ return switch (index) {
+ case 0 -> loop.init();
+ case 1 -> loop.body();
+ case 2 -> loop.update();
+ default -> throw new IndexOutOfBoundsException("Loop index must at most be 2.");
+ };
+ }
+ else if (container instanceof RangedFor ranged_for) {
+ if (index == 0) {
+ return ranged_for.body();
+ }
+ else throw new IndexOutOfBoundsException("Invalid index for foreach loop.");
+ }
+ else throw new IllegalArgumentException("Statement " + container + " is not supported as a container.");
+ }
+}
\ No newline at end of file
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
new file mode 100644
index 0000000000..863223aae8
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
@@ -0,0 +1,30 @@
+package vct.parsers.transform.systemctocol.invariants;
+
+import vct.parsers.transform.systemctocol.invariants.variables.ConcreteVariable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class AbstractState {
+ private final Map, Integer> valuations;
+
+ public AbstractState() {
+ valuations = new HashMap<>();
+ }
+
+ public AbstractState(AbstractState previous) {
+ this.valuations = new HashMap<>(previous.valuations);
+ }
+
+ public void set_valuation(ConcreteVariable var, int value) {
+ valuations.put(var, value);
+ }
+
+ public Integer get_valuation(ConcreteVariable var) {
+ return valuations.get(var);
+ }
+
+ public boolean equals(AbstractState other) {
+ return valuations.equals(other.valuations);
+ }
+}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
new file mode 100644
index 0000000000..1761426ed9
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
@@ -0,0 +1,3 @@
+package vct.parsers.transform.systemctocol.invariants;
+
+public record ExecutableState (AbstractState state, int[][] execution_location_by_process, AbstractProcess next_process) {}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
new file mode 100644
index 0000000000..c14eec3a3e
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
@@ -0,0 +1,8 @@
+package vct.parsers.transform.systemctocol.invariants;
+
+import vct.col.ast.Statement;
+
+public class ExecutionState {
+ private AbstractState abstract_state;
+ private Statement next;
+}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
new file mode 100644
index 0000000000..f13e01ff19
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
@@ -0,0 +1,47 @@
+package vct.parsers.transform.systemctocol.invariants;
+
+import vct.col.ast.InstanceMethod;
+import vct.parsers.ParseResult;
+import vct.parsers.transform.systemctocol.invariants.variables.ConcreteVariable;
+
+import java.util.*;
+
+public class Generator {
+
+ private final List> processes;
+
+ private final Set> active_branches;
+
+ private final AbstractState considered_state;
+
+ public Generator(ParseResult parse_result,
+ Map, Integer> considered_variables,
+ InstanceMethod main_method) {
+ // Initialize processes
+ processes = new ArrayList<>();
+ initialize_processes();
+
+ // Initialize active branches
+ active_branches = new HashSet<>();
+
+ // Set initial state
+ considered_state = new AbstractState<>();
+ }
+
+ public void execute() {
+
+ initialize_branches();
+ }
+
+ private void execute_step() {
+
+ }
+
+ private void initialize_processes() {
+
+ }
+
+ private void initialize_branches() {
+
+ }
+}
\ No newline at end of file
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java
new file mode 100644
index 0000000000..a288348988
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java
@@ -0,0 +1,20 @@
+package vct.parsers.transform.systemctocol.invariants.variables;
+
+import vct.col.ast.Deref;
+import vct.col.ast.Expr;
+import vct.col.ast.Field;
+import vct.col.ast.PVLDeref;
+
+public abstract class ConcreteVariable {
+
+ public abstract boolean is(Expr expression);
+
+ public abstract boolean equals(ConcreteVariable other);
+
+ protected Field extract_from_expression(Expr expression) {
+ if (expression instanceof Deref deref) {
+ return deref.ref().decl();
+ }
+ else throw new IllegalArgumentException("Could not parse expression " + expression.toString());
+ }
+}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java
new file mode 100644
index 0000000000..52f9fbdd94
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java
@@ -0,0 +1,27 @@
+package vct.parsers.transform.systemctocol.invariants.variables;
+
+import vct.col.ast.Expr;
+import vct.col.ast.Field;
+
+public class FieldVariable extends ConcreteVariable {
+
+ protected Field field;
+
+ public FieldVariable(Field field) {
+ this.field = field;
+ }
+
+ @Override
+ public boolean is(Expr expression) {
+ return field.equals(extract_from_expression(expression));
+ }
+
+ @Override
+ public boolean equals(ConcreteVariable other) {
+ if (other == this) return true;
+ if (other instanceof FieldVariable other_fv) {
+ return this.field.equals(other_fv.field);
+ }
+ else return false;
+ }
+}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/SequenceIndexVariable.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/SequenceIndexVariable.java
new file mode 100644
index 0000000000..1ba5eb3a2b
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/SequenceIndexVariable.java
@@ -0,0 +1,44 @@
+package vct.parsers.transform.systemctocol.invariants.variables;
+
+import vct.col.ast.*;
+
+public class SequenceIndexVariable extends ConcreteVariable {
+
+ protected Field sequence;
+ protected int index;
+
+ public SequenceIndexVariable(Field sequence, int index) {
+ this.sequence = sequence;
+ this.index = index;
+ }
+
+ @Override
+ public boolean is(Expr expression) {
+ if (expression instanceof AmbiguousSubscript subscript) {
+ return extract_from_expression(subscript.collection()).equals(sequence)
+ && resolve_index_expression(subscript.index()) == index;
+ }
+ else if (expression instanceof SeqSubscript subscript) {
+ return extract_from_expression(subscript.seq()).equals(sequence)
+ && resolve_index_expression(subscript.index()) == index;
+ }
+ else return false;
+ }
+
+ protected int resolve_index_expression(Expr index_expression) {
+ if (index_expression instanceof IntegerValue integer_value) {
+ return integer_value.value().intValue();
+ }
+ // TODO: Support simple arithmetic?
+ else throw new IllegalArgumentException("Arithmetics in sequence accesses is not yet supported!");
+ }
+
+ @Override
+ public boolean equals(ConcreteVariable other) {
+ if (other == this) return true;
+ if (other instanceof SequenceIndexVariable other_siv) {
+ return this.sequence.equals(other_siv.sequence) && this.index == other_siv.index;
+ }
+ else return false;
+ }
+}
From 97e4ad38171da617d632a8d673d821eb90858d24 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 1 Feb 2024 14:18:09 +0100
Subject: [PATCH 02/85] Solidified architecture of RASI generator
---
.../invariants/AbstractProcess.java | 2 +-
.../invariants/ExecutableState.java | 23 ++++++++++++++++-
.../invariants/ExecutionState.java | 6 +----
.../systemctocol/invariants/Generator.java | 25 +++++++++++--------
4 files changed, 39 insertions(+), 17 deletions(-)
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
index 9a9b861ba7..2edf5048bb 100644
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
@@ -13,7 +13,7 @@ public AbstractProcess(RunMethod method) {
this.process_method = method;
}
- public List> simulate(int[] program_counter) {
+ public List> simulate(int[] program_counter, AbstractState starting_state) {
return null;
}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
index 1761426ed9..383253d726 100644
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
@@ -1,3 +1,24 @@
package vct.parsers.transform.systemctocol.invariants;
-public record ExecutableState (AbstractState state, int[][] execution_location_by_process, AbstractProcess next_process) {}
+import java.util.ArrayList;
+import java.util.List;
+
+public record ExecutableState (AbstractState state,
+ int[][] execution_location_by_process,
+ int next_process_id) {
+
+ public List> execute_step(List> processes) {
+ AbstractProcess next_process = processes.get(next_process_id);
+ List> updates = next_process.simulate(execution_location_by_process[next_process_id], state);
+
+ List> result = new ArrayList<>();
+ for (ExecutionState update : updates) {
+ int[][] updated_counter = execution_location_by_process.clone();
+ updated_counter[next_process_id] = update.program_counter();
+ for (int i = 0; i < processes.size(); i++) {
+ result.add(new ExecutableState<>(update.abstract_state(), updated_counter, i));
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
index c14eec3a3e..a28d2f0bfa 100644
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
@@ -1,8 +1,4 @@
package vct.parsers.transform.systemctocol.invariants;
-import vct.col.ast.Statement;
+public record ExecutionState (AbstractState abstract_state, int[] program_counter) {}
-public class ExecutionState {
- private AbstractState abstract_state;
- private Statement next;
-}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
index f13e01ff19..7587c894e3 100644
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
@@ -10,9 +10,9 @@ public class Generator {
private final List> processes;
- private final Set> active_branches;
+ private final List> active_branches;
- private final AbstractState considered_state;
+ private final Set> considered_branches;
public Generator(ParseResult parse_result,
Map, Integer> considered_variables,
@@ -22,19 +22,24 @@ public Generator(ParseResult parse_result,
initialize_processes();
// Initialize active branches
- active_branches = new HashSet<>();
+ active_branches = new ArrayList<>();
- // Set initial state
- considered_state = new AbstractState<>();
+ // Initialize considered branches to empty list
+ considered_branches = new HashSet<>();
}
public void execute() {
-
initialize_branches();
- }
-
- private void execute_step() {
+ while (!active_branches.isEmpty()) {
+ ExecutableState exploring = active_branches.remove(0);
+ List> new_possibilities = exploring.execute_step(processes)
+ .stream()
+ .filter((state) -> !considered_branches.contains(state))
+ .toList();
+ considered_branches.addAll(new_possibilities);
+ active_branches.addAll(new_possibilities);
+ }
}
private void initialize_processes() {
@@ -42,6 +47,6 @@ private void initialize_processes() {
}
private void initialize_branches() {
-
+ AbstractState initial_state = new AbstractState<>();
}
}
\ No newline at end of file
From c441e636c76027ac2c8c83d4dee315e9c38f2be0 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 1 Feb 2024 16:49:52 +0100
Subject: [PATCH 03/85] Refined execution state tracking
---
.../invariants/AbstractProcess.java | 8 +++-
.../invariants/AbstractState.java | 13 +++++-
.../invariants/ResolvedExecutionState.java | 43 +++++++++++++++++++
3 files changed, 62 insertions(+), 2 deletions(-)
create mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
index 2edf5048bb..53942c79a8 100644
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
@@ -14,7 +14,13 @@ public AbstractProcess(RunMethod method) {
}
public List> simulate(int[] program_counter, AbstractState starting_state) {
- return null;
+ List> final_result = new ArrayList<>();
+
+ List> current_branches = new ArrayList<>();
+ List> current_stack = resolve(program_counter);
+ current_branches.add(new ResolvedExecutionState<>(starting_state, program_counter, current_stack));
+
+ return final_result;
}
private List> small_step() {
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
index 863223aae8..64a2c73cbc 100644
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
@@ -1,5 +1,6 @@
package vct.parsers.transform.systemctocol.invariants;
+import vct.col.ast.Expr;
import vct.parsers.transform.systemctocol.invariants.variables.ConcreteVariable;
import java.util.HashMap;
@@ -16,14 +17,24 @@ public AbstractState(AbstractState previous) {
this.valuations = new HashMap<>(previous.valuations);
}
+ public boolean contains_variable(Expr variable_expression) {
+ return valuations.keySet().stream().anyMatch((var) -> var.is(variable_expression));
+ }
+
public void set_valuation(ConcreteVariable var, int value) {
valuations.put(var, value);
}
- public Integer get_valuation(ConcreteVariable var) {
+ public int get_valuation(ConcreteVariable var) {
return valuations.get(var);
}
+ public int get_valuation(Expr variable_expression) {
+ return valuations.entrySet().stream()
+ .filter((entry) -> entry.getKey().is(variable_expression))
+ .findFirst().orElseThrow().getValue();
+ }
+
public boolean equals(AbstractState other) {
return valuations.equals(other.valuations);
}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java
new file mode 100644
index 0000000000..4a0c8047e2
--- /dev/null
+++ b/src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java
@@ -0,0 +1,43 @@
+package vct.parsers.transform.systemctocol.invariants;
+
+import vct.col.ast.Statement;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ResolvedExecutionState {
+
+ private AbstractState state;
+ private int[] program_counter;
+ private List> program_location;
+
+ public ResolvedExecutionState(AbstractState state, int[] program_counter, List> program_location) {
+ this.state = new AbstractState<>(state);
+ this.program_counter = program_counter.clone();
+ this.program_location = new ArrayList<>(program_location);
+ }
+
+ public AbstractState get_state() {
+ return state;
+ }
+
+ public int[] get_program_counter() {
+ return program_counter;
+ }
+
+ public List> get_program_location() {
+ return program_location;
+ }
+
+ public Statement get_current_statement() {
+ return program_location.get(program_location.size() - 1);
+ }
+
+ public void move_forward() {
+
+ }
+
+ public ExecutionState export() {
+ return new ExecutionState<>(state, program_counter);
+ }
+}
From df1654b4a136303f06f58c4bba8f20032bd626d5 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 8 Feb 2024 17:59:50 +0100
Subject: [PATCH 04/85] Working on Control Flow Graph for VerCors
---
.../vct/rewrite/cfg/CFGGenerator.scala | 187 ++++++++++++++++++
src/rewrite/vct/rewrite/cfg/CFGNode.scala | 5 +
2 files changed, 192 insertions(+)
create mode 100644 src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
create mode 100644 src/rewrite/vct/rewrite/cfg/CFGNode.scala
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
new file mode 100644
index 0000000000..c78ba0a224
--- /dev/null
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -0,0 +1,187 @@
+package vct.rewrite.cfg
+
+import vct.col.ast._
+
+object CFGGenerator {
+
+ def generate[G](entry: InstanceMethod[G]): CFGNode[G] = {
+ convert(entry.body.get, GlobalIndex[G])
+ }
+
+ def convert[G](node: Statement[G], context: GlobalIndex[G]): CFGNode[G] =
+ CFGNode(node, find_successors(node, context))
+
+ def find_successors[G](node: Statement[G], context: GlobalIndex[G]): Set[CFGNode[G]] = node match {
+ case PVLBranch(branches) =>
+ branches.map(b => convert(b._2, context.enter_scope(node))).toSet
+ case PVLLoop(init, cond, update, _, body) => {
+ val cond_node: Eval[G] = Eval(cond)(cond.o)
+
+ }
+ // NonExecutableStatement
+ case LocalDecl(local) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case SpecIgnoreStart() =>
+ CFGNode(node, successors)
+ case SpecIgnoreEnd() =>
+ CFGNode(node, successors)
+ // NormallyCompletingStatement
+ case Assign(target, value) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Send(decl, delta, res) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Recv(ref) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case DefaultCase() => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Case(pattern) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Label(decl, stat) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Goto(lbl) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Exhale(res) => // TODO: Can expressions in specifications be ignored?
+ CFGNode(node, successors)
+ case Assert(res) =>
+ CFGNode(node, successors)
+ case Refute(assn) =>
+ CFGNode(node, successors)
+ case Inhale(res) =>
+ CFGNode(node, successors)
+ case Assume(assn) => // <--
+ CFGNode(node, successors)
+ case Instantiate(cls, out) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Wait(obj) =>
+ CFGNode(node, successors)
+ case Notify(obj) =>
+ CFGNode(node, successors)
+ case Fork(obj) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Join(obj) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Lock(obj) =>
+ CFGNode(node, successors)
+ case Unlock(obj) =>
+ CFGNode(node, successors)
+ case Commit(obj) =>
+ CFGNode(node, successors)
+ case Fold(res) => // TODO: Can expressions in specifications be ignored?
+ CFGNode(node, successors)
+ case Unfold(res) =>
+ CFGNode(node, successors)
+ case WandApply(res) => // <--
+ CFGNode(node, successors)
+ case Havoc(loc) =>
+ CFGNode(node, successors)
+ case FramedProof(pre, body, post) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Extract(contractedStatement) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ // ExceptionalStatement
+ case Eval(expr) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ // InvocationStatement
+ case InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case InvokeConstructor(ref, out, args, outArgs, typeArgs, givenMap, yields) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case InvokeMethod(obj, ref, args, outArgs, typeArgs, givenMap, yields) => {
+ CFGNode[G](node, Set[CFGNode[G]])
+ } // TODO
+ case Return(result) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Throw(obj) =>
+ CFGNode(node, successors)
+ case Break(label) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Continue(label) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ // CompositeStatement
+ case Block(statements) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Scope(locals, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Branch(branches) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case IndetBranch(branches) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Switch(expr, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Loop(init, cond, update, contract, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case RangedFor(iter, contract, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case TryCatchFinally(body, after, catches) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Synchronized(obj, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case ParInvariant(decl, inv, content) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case ParAtomic(inv, content) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case ParBarrier(block, invs, requires, ensures, content) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case ParStatement(impl) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case VecBlock(iters, requires, ensures, content) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case WandPackage(res, proof) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case ModelDo(model, perm, after, action, impl) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ // CStatement
+ case CDeclarationStatement(decl) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case CGoto(label) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ // CPPStatement
+ case CPPDeclarationStatement(decl) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case CPPLifetimeScope(body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case JavaLocalDeclarationStatement(decl) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ // SilverStatement
+ case SilverNewRef(v, fields) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case SilverFieldAssign(obj, field, value) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case SilverLocalAssign(v, value) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ // OTHER
+ case PVLCommunicate(sender, receiver) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case PVLSeqAssign(receiver, field, value) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case Communicate(receiver, sender) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case SeqAssign(receiver, field, value) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case UnresolvedSeqBranch(branches) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case UnresolvedSeqLoop(cond, contract, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case SeqBranch(guards, yes, no) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case SeqLoop(guards, contract, body) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case VeyMontAssignExpression(endpoint, assign) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case CommunicateX(receiver, sender, chanType, assign) =>
+ CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ }
+}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGNode.scala b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
new file mode 100644
index 0000000000..dd72a81012
--- /dev/null
+++ b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
@@ -0,0 +1,5 @@
+package vct.rewrite.cfg
+
+import vct.col.ast.Node
+
+case class CFGNode[G](ast_node: Node[G], successors: Set[CFGNode[G]])
\ No newline at end of file
From 258827c27bdb5b5c9d5782d75862071797fa5666 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 8 Feb 2024 18:00:07 +0100
Subject: [PATCH 05/85] Added indexing system for nested statements
---
src/rewrite/vct/rewrite/cfg/Index.scala | 232 ++++++++++++++++++++++++
1 file changed, 232 insertions(+)
create mode 100644 src/rewrite/vct/rewrite/cfg/Index.scala
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
new file mode 100644
index 0000000000..18918ee9de
--- /dev/null
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -0,0 +1,232 @@
+package vct.rewrite.cfg
+
+import vct.col.ast._
+
+case class GlobalIndex[G](indices: List[Index[G]]) {
+
+ def enter_scope(statement: Statement[G], index: Int = 0): GlobalIndex[G] =
+ GlobalIndex(Index[G](statement, index) :: indices)
+
+
+ def leave_scope(): GlobalIndex[G] =
+ GlobalIndex(indices.tail).make_step()
+
+
+ def make_step(): GlobalIndex[G] = indices.head.make_step() match {
+ case Some(index) => GlobalIndex(index :: indices.tail)
+ case None => GlobalIndex(indices.tail).make_step()
+ }
+
+ def resolve(): Statement[G] = indices.head.resolve()
+}
+
+sealed trait Index[G] {
+ def make_step(): Option[Index[G]]
+ def resolve(): Statement[G]
+}
+
+object Index {
+ def apply[G](pvl_branch: PVLBranch[G], index: Int): Index[G] = PVLBranchIndex(pvl_branch, index)
+ def apply[G](pvl_loop: PVLLoop[G], index: Int): Index[G] = PVLLoopIndex(pvl_loop, index)
+ def apply[G](label: Label[G], index: Int): Index[G] = LabelIndex(label)
+ def apply[G](framed_proof: FramedProof[G], index: Int): Index[G] = FramedProofIndex(framed_proof)
+ def apply[G](extract: Extract[G], index: Int): Index[G] = ExtractIndex(extract)
+ def apply[G](eval: Eval[G], index: Int): Index[G] = EvalIndex(eval, index)
+ def apply[G](block: Block[G], index: Int): Index[G] = BlockIndex(block, index)
+ def apply[G](scope: Scope[G], index: Int): Index[G] = ScopeIndex(scope)
+ def apply[G](branch: Branch[G], index: Int): Index[G] = BranchIndex(branch, index)
+ def apply[G](indet_branch: IndetBranch[G], index: Int): Index[G] = IndetBranchIndex(indet_branch, index)
+ def apply[G](switch: Switch[G], index: Int): Index[G] = SwitchIndex(switch)
+ def apply[G](loop: Loop[G], index: Int): Index[G] = LoopIndex(loop, index)
+ def apply[G](ranged_for: RangedFor[G], index: Int): Index[G] = RangedForIndex(ranged_for)
+ def apply[G](try_catch_finally: TryCatchFinally[G], index: Int): Index[G] = TryCatchFinallyIndex(try_catch_finally, index)
+ def apply[G](synchronized: Synchronized[G], index: Int): Index[G] = SynchronizedIndex(synchronized)
+ def apply[G](par_invariant: ParInvariant[G], index: Int): Index[G] = ParInvariantIndex(par_invariant)
+ def apply[G](par_atomic: ParAtomic[G], index: Int): Index[G] = ParAtomicIndex(par_atomic)
+ def apply[G](par_barrier: ParBarrier[G], index: Int): Index[G] = ParBarrierIndex(par_barrier)
+ def apply[G](vec_block: VecBlock[G], index: Int): Index[G] = VecBlockIndex(vec_block)
+ def apply[G](wand_package: WandPackage[G], index: Int): Index[G] = WandPackageIndex(wand_package)
+ def apply[G](model_do: ModelDo[G], index: Int): Index[G] = ModelDoIndex(model_do)
+ def apply[G](cpp_lifetime_scope: CPPLifetimeScope[G], index: Int): Index[G] = CPPLifetimeScopeIndex(cpp_lifetime_scope)
+ def apply[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int): Index[G] = UnresolvedSeqBranchIndex(unresolved_seq_branch, index)
+ def apply[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int): Index[G] = UnresolvedSeqLoopIndex(unresolved_seq_loop, index)
+ def apply[G](seq_branch: SeqBranch[G], index: Int): Index[G] = SeqBranchIndex(seq_branch, index)
+ def apply[G](seq_loop: SeqLoop[G], index: Int): Index[G] = SeqLoopIndex(seq_loop)
+ def apply[G](veymont_assign_expression: VeyMontAssignExpression[G], index: Int): Index[G] = VeyMontAssignExpressionIndex(veymont_assign_expression)
+ def apply[G](communicatex: CommunicateX[G], index: Int): Index[G] = CommunicateXIndex(communicatex)
+}
+
+case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = pvl_branch.branches.apply(index)._2 // TODO: Handle expressions in branch conditions
+}
+case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = {
+ if (index < 3) Some(PVLLoopIndex(pvl_loop, index + 1))
+ else None
+ }
+
+ override def resolve(): Statement[G] = index match {
+ case 0 => pvl_loop.init
+ case 1 => Eval(pvl_loop.cond)(pvl_loop.cond.o)
+ case 2 => pvl_loop.body
+ case 3 => pvl_loop.update
+ }
+}
+case class LabelIndex[G](label: Label[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = label.stat
+}
+case class FramedProofIndex[G](framed_proof: FramedProof[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = framed_proof.body
+}
+case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = extract.contractedStatement
+}
+case class EvalIndex[G](eval: Eval[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None // TODO: Implement expressions!
+
+ override def resolve(): Statement[G] = ??? // TODO: Implement expressions!
+}
+case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = {
+ if (index < block.statements.size - 1) Some(BlockIndex(block, index + 1))
+ else None
+ }
+
+ override def resolve(): Statement[G] = block.statements.apply(index)
+}
+case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = scope.body
+}
+case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = branch.branches.apply(index)._2
+}
+case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = indet_branch.branches.apply(index)
+}
+case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = switch.body
+}
+case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = {
+ if (index < 3) Some(LoopIndex(loop, index + 1))
+ else None
+ }
+
+ override def resolve(): Statement[G] = index match {
+ case 0 => loop.init
+ case 1 => Eval(loop.cond)(loop.cond.o)
+ case 2 => loop.body
+ case 3 => loop.update
+ }
+}
+case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = ranged_for.body
+}
+case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = {
+ if (index == 0) Some(TryCatchFinallyIndex(try_catch_finally, 1))
+ else None
+ }
+
+ override def resolve(): Statement[G] = index match {
+ case 0 => try_catch_finally.body
+ case 1 => try_catch_finally.after
+ }
+}
+case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = synchronized.body
+}
+case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = par_invariant.content
+}
+case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = par_atomic.content
+}
+case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = par_barrier.content
+}
+case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = vec_block.content
+}
+case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = wand_package.proof
+}
+case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = model_do.impl
+}
+case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = cpp_lifetime_scope.body
+}
+case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = unresolved_seq_branch.branches.apply(index)._2
+}
+case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = {
+ if (index == 0) Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1))
+ else None
+ }
+
+ override def resolve(): Statement[G] = index match {
+ case 0 => Eval(unresolved_seq_loop.cond)(unresolved_seq_loop.cond.o)
+ case 1 => unresolved_seq_loop.body
+ }
+}
+case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = index match {
+ case 0 => seq_branch.yes
+ case 1 => seq_branch.no.get
+ }
+}
+case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = seq_loop.body
+}
+case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = veymont_assign_expression.assign
+}
+case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
+ override def make_step(): Option[Index[G]] = None
+
+ override def resolve(): Statement[G] = communicatex.assign
+}
\ No newline at end of file
From e1cf5334f6307df47fa2a35a72f7e6bf7798cae1 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 9 Feb 2024 12:47:36 +0100
Subject: [PATCH 06/85] Added context indices for subroutine calls
---
src/rewrite/vct/rewrite/cfg/Index.scala | 72 +++++++++++++++----------
1 file changed, 44 insertions(+), 28 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 18918ee9de..1cd425062d 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -32,6 +32,9 @@ object Index {
def apply[G](framed_proof: FramedProof[G], index: Int): Index[G] = FramedProofIndex(framed_proof)
def apply[G](extract: Extract[G], index: Int): Index[G] = ExtractIndex(extract)
def apply[G](eval: Eval[G], index: Int): Index[G] = EvalIndex(eval, index)
+ def apply[G](invoke_procedure: InvokeProcedure[G], index: Int): Index[G] = InvokeProcedureIndex(invoke_procedure, index)
+ def apply[G](invoke_constructor: InvokeConstructor[G], index: Int): Index[G] = InvokeConstructorIndex(invoke_constructor, index)
+ def apply[G](invoke_method: InvokeMethod[G], index: Int): Index[G] = InvokeMethodIndex(invoke_method, index)
def apply[G](block: Block[G], index: Int): Index[G] = BlockIndex(block, index)
def apply[G](scope: Scope[G], index: Int): Index[G] = ScopeIndex(scope)
def apply[G](branch: Branch[G], index: Int): Index[G] = BranchIndex(branch, index)
@@ -58,7 +61,6 @@ object Index {
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = pvl_branch.branches.apply(index)._2 // TODO: Handle expressions in branch conditions
}
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
@@ -66,7 +68,6 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
if (index < 3) Some(PVLLoopIndex(pvl_loop, index + 1))
else None
}
-
override def resolve(): Statement[G] = index match {
case 0 => pvl_loop.init
case 1 => Eval(pvl_loop.cond)(pvl_loop.cond.o)
@@ -74,60 +75,75 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
case 3 => pvl_loop.update
}
}
+
case class LabelIndex[G](label: Label[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = label.stat
}
+
case class FramedProofIndex[G](framed_proof: FramedProof[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = framed_proof.body
}
+
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = extract.contractedStatement
}
+
case class EvalIndex[G](eval: Eval[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = None // TODO: Implement expressions!
-
override def resolve(): Statement[G] = ??? // TODO: Implement expressions!
}
+
+case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = ???
+ override def resolve(): Statement[G] = ???
+}
+
+case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = ???
+ override def resolve(): Statement[G] = ???
+}
+
+case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) extends Index[G] {
+ override def make_step(): Option[Index[G]] = ???
+ override def resolve(): Statement[G] = ???
+}
+
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = {
if (index < block.statements.size - 1) Some(BlockIndex(block, index + 1))
else None
}
-
override def resolve(): Statement[G] = block.statements.apply(index)
}
+
case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = scope.body
}
+
case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = branch.branches.apply(index)._2
}
+
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
}
+
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = switch.body
}
+
case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = {
if (index < 3) Some(LoopIndex(loop, index + 1))
else None
}
-
override def resolve(): Statement[G] = index match {
case 0 => loop.init
case 1 => Eval(loop.cond)(loop.cond.o)
@@ -135,98 +151,98 @@ case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
case 3 => loop.update
}
}
+
case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = ranged_for.body
}
+
case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = {
if (index == 0) Some(TryCatchFinallyIndex(try_catch_finally, 1))
else None
}
-
override def resolve(): Statement[G] = index match {
case 0 => try_catch_finally.body
case 1 => try_catch_finally.after
}
}
+
case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = synchronized.body
}
+
case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = par_invariant.content
}
+
case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = par_atomic.content
}
+
case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = par_barrier.content
}
+
case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = vec_block.content
}
+
case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = wand_package.proof
}
+
case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = model_do.impl
}
+
case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = cpp_lifetime_scope.body
}
+
case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = unresolved_seq_branch.branches.apply(index)._2
}
+
case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = {
if (index == 0) Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1))
else None
}
-
override def resolve(): Statement[G] = index match {
case 0 => Eval(unresolved_seq_loop.cond)(unresolved_seq_loop.cond.o)
case 1 => unresolved_seq_loop.body
}
}
+
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = index match {
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
}
}
+
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = seq_loop.body
}
+
case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = veymont_assign_expression.assign
}
+
case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
override def make_step(): Option[Index[G]] = None
-
override def resolve(): Statement[G] = communicatex.assign
}
\ No newline at end of file
From 9e766d04b683b5a1f7554c56906714fee2cf0bab Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 12 Feb 2024 09:58:50 +0100
Subject: [PATCH 07/85] Continued working on indexing system
---
.../vct/rewrite/cfg/CFGGenerator.scala | 319 +++++++++---------
src/rewrite/vct/rewrite/cfg/CFGNode.scala | 2 +-
src/rewrite/vct/rewrite/cfg/Index.scala | 35 +-
3 files changed, 193 insertions(+), 163 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index c78ba0a224..556b73bd88 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -2,186 +2,199 @@ package vct.rewrite.cfg
import vct.col.ast._
-object CFGGenerator {
+import scala.collection.mutable
- def generate[G](entry: InstanceMethod[G]): CFGNode[G] = {
+case class CFGGenerator[G]() {
+ private val found_labels: mutable.Map[LabelDecl[G], CFGNode[G]] = mutable.HashMap[LabelDecl[G], CFGNode[G]]()
+ //private val searched_labels: mutable.Map[LabelDecl[G], mutable.Set[CFGNode[G]]] = mutable.HashMap[LabelDecl[G], mutable.Set[CFGNode[G]]]()
+ private val searched_labels: mutable.Map[LabelDecl[G], mutable.Set[CFGNode[G]]] = mutable.HashMap[LabelDecl[G], mutable.Set[CFGNode[G]]]()
+
+ def generate(entry: InstanceMethod[G]): CFGNode[G] = {
convert(entry.body.get, GlobalIndex[G])
}
- def convert[G](node: Statement[G], context: GlobalIndex[G]): CFGNode[G] =
- CFGNode(node, find_successors(node, context))
+ def convert(node: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
+ // Create new node with its successors (if possible)
+ val cfg_node: CFGNode[G] = CFGNode(node, find_successors(node, context))
+ // Handle labels and goto statements
+ node match {
+ case label: Label[G] => {
+ // For labels, add them to the label map and add them to any goto statements going to that label
+ found_labels.addOne((label.decl, cfg_node))
+ searched_labels.getOrElse(label.decl, mutable.Set()).map(g => g.successors.addOne(cfg_node))
+ }
+ case goto: Goto[G] => {
+ // For goto statements, if the label could not be resolved below, add the statement to the waiting list for the right label
+ if (cfg_node.successors.isEmpty) {
+ if (searched_labels.contains(goto.lbl.decl)) searched_labels(goto.lbl.decl).addOne(cfg_node)
+ else searched_labels.addOne((goto.lbl.decl, mutable.Set(cfg_node)))
+ }
+ }
+ }
+ cfg_node
+ }
- def find_successors[G](node: Statement[G], context: GlobalIndex[G]): Set[CFGNode[G]] = node match {
+ def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGNode[G]] = node match {
case PVLBranch(branches) =>
- branches.map(b => convert(b._2, context.enter_scope(node))).toSet
+ mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2)))) // TODO: Implement support for statements in expressions
case PVLLoop(init, cond, update, _, body) => {
val cond_node: Eval[G] = Eval(cond)(cond.o)
-
+ // TODO: Implement!
+ ???
}
// NonExecutableStatement
- case LocalDecl(local) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case SpecIgnoreStart() =>
- CFGNode(node, successors)
- case SpecIgnoreEnd() =>
- CFGNode(node, successors)
+ case LocalDecl(_) => sequential_successor(context)
+ case SpecIgnoreStart() => sequential_successor(context) // What is this?
+ case SpecIgnoreEnd() => sequential_successor(context)
// NormallyCompletingStatement
- case Assign(target, value) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Send(decl, delta, res) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Recv(ref) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case DefaultCase() => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Case(pattern) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Label(decl, stat) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
+ case Assign(target, value) => sequential_successor(context) // TODO: Implement support for statements in expressions
+ case Send(_, _, res) => sequential_successor(context) // TODO: Implement support for statements in expressions
+ case Recv(_) => sequential_successor(context)
+ case DefaultCase() => ??? // TODO: Ask Pieter about switch statements
+ case Case(pattern) => ???
+ case Label(_, stat) =>
+ mutable.Set(convert(stat, context.enter_scope(node)))
case Goto(lbl) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Exhale(res) => // TODO: Can expressions in specifications be ignored?
- CFGNode(node, successors)
- case Assert(res) =>
- CFGNode(node, successors)
- case Refute(assn) =>
- CFGNode(node, successors)
- case Inhale(res) =>
- CFGNode(node, successors)
- case Assume(assn) => // <--
- CFGNode(node, successors)
- case Instantiate(cls, out) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Wait(obj) =>
- CFGNode(node, successors)
- case Notify(obj) =>
- CFGNode(node, successors)
- case Fork(obj) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Join(obj) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Lock(obj) =>
- CFGNode(node, successors)
- case Unlock(obj) =>
- CFGNode(node, successors)
- case Commit(obj) =>
- CFGNode(node, successors)
- case Fold(res) => // TODO: Can expressions in specifications be ignored?
- CFGNode(node, successors)
- case Unfold(res) =>
- CFGNode(node, successors)
- case WandApply(res) => // <--
- CFGNode(node, successors)
- case Havoc(loc) =>
- CFGNode(node, successors)
- case FramedProof(pre, body, post) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Extract(contractedStatement) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
+ val found_node: Option[CFGNode[G]] = found_labels.get(lbl.decl)
+ found_node match {
+ case Some(node) => mutable.Set(node)
+ case None => mutable.Set()
+ }
+ }
+ case Exhale(res) => sequential_successor(context) // TODO: Can expressions in specifications be ignored?
+ case Assert(res) => sequential_successor(context)
+ case Refute(assn) => sequential_successor(context)
+ case Inhale(res) => sequential_successor(context)
+ case Assume(assn) => sequential_successor(context) // <--
+ case Instantiate(cls, out) => ??? // TODO
+ case Wait(_) => sequential_successor(context)
+ case Notify(_) => sequential_successor(context)
+ case Fork(obj) => ??? // TODO: Find run method from expression
+ case Join(_) => sequential_successor(context)
+ case Lock(_) => sequential_successor(context)
+ case Unlock(_) => sequential_successor(context)
+ case Commit(_) => sequential_successor(context)
+ case Fold(res) => sequential_successor(context) // TODO: Can expressions in specifications be ignored?
+ case Unfold(res) => sequential_successor(context)
+ case WandApply(res) => sequential_successor(context) // <--
+ case Havoc(_) => sequential_successor(context)
+ case FramedProof(pre, body, post) =>
+ mutable.Set(convert(body, context.enter_scope(node))) // TODO: Can expressions in specifications be ignored?
+ case Extract(contractedStatement) =>
+ mutable.Set(convert(contractedStatement, context.enter_scope(node)))
// ExceptionalStatement
- case Eval(expr) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
+ case Eval(expr) => ??? // TODO
// InvocationStatement
- case InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
+ case InvokeProcedure(ref, args, _, _, givenMap, _) => {
+ if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
+ else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
+ else sequential_successor(context)
+ }
case InvokeConstructor(ref, out, args, outArgs, typeArgs, givenMap, yields) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
+ if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
+ else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
+ else sequential_successor(context)
+ }
case InvokeMethod(obj, ref, args, outArgs, typeArgs, givenMap, yields) => {
- CFGNode[G](node, Set[CFGNode[G]])
- } // TODO
- case Return(result) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Throw(obj) =>
- CFGNode(node, successors)
- case Break(label) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Continue(label) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
+ else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
+ else sequential_successor(context)
+ }
+ case Return(result) => mutable.Set(return_successor(context))
+ case Throw(obj) => mutable.Set(exception_successor(obj, context))
+ case Break(label) => label match {
+ case Some(ref) => ??? // TODO: Handle break label!
+ case None => mutable.Set(break_successor(context))
+ }
+ case Continue(label) => label match {
+ case Some(ref) => ??? // TODO: Handle continue label!
+ case None => mutable.Set(continue_successor(context))
+ }
// CompositeStatement
case Block(statements) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Scope(locals, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ mutable.Set(convert(statements.head, context.enter_scope(node)))
+ case Scope(_, body) =>
+ mutable.Set(convert(body, context.enter_scope(node)))
case Branch(branches) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2)))) // TODO: Handle statements in condition expressions!
case IndetBranch(branches) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Switch(expr, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Loop(init, cond, update, contract, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case RangedFor(iter, contract, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case TryCatchFinally(body, after, catches) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Synchronized(obj, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case ParInvariant(decl, inv, content) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case ParAtomic(inv, content) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case ParBarrier(block, invs, requires, ensures, content) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case ParStatement(impl) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case VecBlock(iters, requires, ensures, content) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ mutable.Set(branches.zipWithIndex.map(b => convert(b._1, context.enter_scope(node, b._2))))
+ case Switch(expr, body) => ??? // TODO: Ask Pieter about switch statements
+ case Loop(init, cond, update, _, body) => {
+ ??? // TODO
+ }
+ case RangedFor(_, _, body) =>
+ mutable.Set(convert(body, context.enter_scope(node)))
+ case TryCatchFinally(body, _, _) =>
+ mutable.Set(convert(body, context.enter_scope(node))) // TODO: Is it safe to ignore the "after" part here?
+ case Synchronized(_, body) =>
+ mutable.Set(convert(body, context.enter_scope(node)))
+ case ParInvariant(_, _, content) =>
+ mutable.Set(convert(content, context.enter_scope(node)))
+ case ParAtomic(_, content) =>
+ mutable.Set(convert(content, context.enter_scope(node)))
+ case ParBarrier(_, _, _, _, content) =>
+ mutable.Set(convert(content, context.enter_scope(node)))
+ case ParStatement(_) => sequential_successor(context)
+ case VecBlock(_, _, _, content) =>
+ mutable.Set(convert(content, context.enter_scope(node)))
case WandPackage(res, proof) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ ??? // TODO
case ModelDo(model, perm, after, action, impl) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ ??? // TODO
// CStatement
- case CDeclarationStatement(decl) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case CGoto(label) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case CDeclarationStatement(_) => sequential_successor(context)
+ case CGoto(label) => ???
// CPPStatement
- case CPPDeclarationStatement(decl) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case CPPDeclarationStatement(_) => sequential_successor(context)
case CPPLifetimeScope(body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case JavaLocalDeclarationStatement(decl) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ mutable.Set(convert(body, context.enter_scope(node)))
+ case JavaLocalDeclarationStatement(_) => sequential_successor(context)
// SilverStatement
- case SilverNewRef(v, fields) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case SilverFieldAssign(obj, field, value) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case SilverLocalAssign(v, value) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case SilverNewRef(_, _) => sequential_successor(context)
+ case SilverFieldAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
+ case SilverLocalAssign(_, value) => sequential_successor(context) // TODO: Statements in expressions
// OTHER
- case PVLCommunicate(sender, receiver) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case PVLSeqAssign(receiver, field, value) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case Communicate(receiver, sender) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case SeqAssign(receiver, field, value) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ case PVLCommunicate(_, _) => sequential_successor(context)
+ case PVLSeqAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
+ case Communicate(_, _) => sequential_successor(context)
+ case SeqAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
case UnresolvedSeqBranch(branches) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case UnresolvedSeqLoop(cond, contract, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case SeqBranch(guards, yes, no) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case SeqLoop(guards, contract, body) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case VeyMontAssignExpression(endpoint, assign) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
- case CommunicateX(receiver, sender, chanType, assign) =>
- CFGNode[G](node, Set[CFGNode[G]]) // TODO
+ mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2))))
+ case UnresolvedSeqLoop(cond, _, body) => {
+ ??? // TODO: Implement
+ }
+ case SeqBranch(_, yes, no) => no match {
+ case Some(stmt) => mutable.Set(convert(yes, context.enter_scope(node, 0)), convert(stmt, context.enter_scope(node, 1)))
+ case None => mutable.Set(convert(yes, context.enter_scope(node)))
+ }
+ case SeqLoop(_, _, body) =>
+ mutable.Set(convert(body, context.enter_scope(node)))
+ case VeyMontAssignExpression(_, assign) =>
+ mutable.Set(convert(assign, context.enter_scope(node)))
+ case CommunicateX(_, _, _, assign) =>
+ mutable.Set(convert(assign, context.enter_scope(node)))
+ }
+
+ def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] = {
+ val next_index: GlobalIndex[G] = index.make_step()
+ val next_statement: Statement[G] = next_index.resolve()
+
+ }
+
+ def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ ??? // TODO: Implement!
+ }
+
+ def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGNode[G] = {
+ ??? // TODO: Implement!
+ }
+
+ def break_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ ??? // TODO: Implement!
+ }
+
+ def continue_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ ??? // TODO: Implement!
}
}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGNode.scala b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
index dd72a81012..e86db9805a 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGNode.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
@@ -2,4 +2,4 @@ package vct.rewrite.cfg
import vct.col.ast.Node
-case class CFGNode[G](ast_node: Node[G], successors: Set[CFGNode[G]])
\ No newline at end of file
+case class CFGNode[G](ast_node: Node[G], successors: scala.collection.mutable.Set[CFGNode[G]])
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 1cd425062d..1d0b4722ee 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -7,13 +7,11 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def enter_scope(statement: Statement[G], index: Int = 0): GlobalIndex[G] =
GlobalIndex(Index[G](statement, index) :: indices)
-
def leave_scope(): GlobalIndex[G] =
GlobalIndex(indices.tail).make_step()
-
def make_step(): GlobalIndex[G] = indices.head.make_step() match {
- case Some(index) => GlobalIndex(index :: indices.tail)
+ case Some(idx) => GlobalIndex(idx :: indices.tail)
case None => GlobalIndex(indices.tail).make_step()
}
@@ -63,6 +61,7 @@ case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index
override def make_step(): Option[Index[G]] = None
override def resolve(): Statement[G] = pvl_branch.branches.apply(index)._2 // TODO: Handle expressions in branch conditions
}
+
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
override def make_step(): Option[Index[G]] = {
if (index < 3) Some(PVLLoopIndex(pvl_loop, index + 1))
@@ -97,18 +96,36 @@ case class EvalIndex[G](eval: Eval[G], index: Int) extends Index[G] {
}
case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = ???
- override def resolve(): Statement[G] = ???
+ override def make_step(): Option[Index[G]] = {
+ if (index < invoke_procedure.args.size) Some(InvokeProcedureIndex(invoke_procedure, index + 1))
+ else None
+ }
+ override def resolve(): Statement[G] = {
+ if (index < invoke_procedure.args.size) Eval(invoke_procedure.args.apply(index))(invoke_proecedure.args.apply(index).o)
+ else invoke_procedure.ref.decl.body.get
+ }
}
case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = ???
- override def resolve(): Statement[G] = ???
+ override def make_step(): Option[Index[G]] = {
+ if (index < invoke_constructor.args.size) Some(InvokeConstructorIndex(invoke_constructor, index + 1))
+ else None
+ }
+ override def resolve(): Statement[G] = {
+ if (index < invoke_constructor.args.size) Eval(invoke_constructor.args.apply(index))(invoke_constructor.args.apply(index).o)
+ else invoke_constructor.ref.decl.body.get
+ }
}
case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = ???
- override def resolve(): Statement[G] = ???
+ override def make_step(): Option[Index[G]] = {
+ if (index < invoke_method.args.size) Some(InvokeMethodIndex(invoke_method, index + 1))
+ else None
+ }
+ override def resolve(): Statement[G] = {
+ if (index < invoke_method.args.size) Eval(invoke_method.args.apply(index))(invoke_method.args.apply(index).o)
+ else invoke_method.ref.decl.body.get
+ }
}
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
From 023ebe3d9b209f998a0ee77a1db4f48a0f2ef4c4 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 12 Feb 2024 09:59:31 +0100
Subject: [PATCH 08/85] Fixed typo
---
src/rewrite/vct/rewrite/cfg/Index.scala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 1d0b4722ee..29d5f1c1d9 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -101,7 +101,7 @@ case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index:
else None
}
override def resolve(): Statement[G] = {
- if (index < invoke_procedure.args.size) Eval(invoke_procedure.args.apply(index))(invoke_proecedure.args.apply(index).o)
+ if (index < invoke_procedure.args.size) Eval(invoke_procedure.args.apply(index))(invoke_procedure.args.apply(index).o)
else invoke_procedure.ref.decl.body.get
}
}
From 485a276d34fee544de77cfe023f8e33db689df09 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 12 Feb 2024 12:33:47 +0100
Subject: [PATCH 09/85] Allowed sets of successor indices, added support for
fork, added recursion stop for resolve
---
.../vct/rewrite/cfg/CFGGenerator.scala | 40 +++---
src/rewrite/vct/rewrite/cfg/Index.scala | 130 ++++++++++--------
2 files changed, 92 insertions(+), 78 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 556b73bd88..b8115c0daf 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -6,14 +6,16 @@ import scala.collection.mutable
case class CFGGenerator[G]() {
private val found_labels: mutable.Map[LabelDecl[G], CFGNode[G]] = mutable.HashMap[LabelDecl[G], CFGNode[G]]()
- //private val searched_labels: mutable.Map[LabelDecl[G], mutable.Set[CFGNode[G]]] = mutable.HashMap[LabelDecl[G], mutable.Set[CFGNode[G]]]()
private val searched_labels: mutable.Map[LabelDecl[G], mutable.Set[CFGNode[G]]] = mutable.HashMap[LabelDecl[G], mutable.Set[CFGNode[G]]]()
+ private val converted_nodes: mutable.Map[GlobalIndex[G], CFGNode[G]] = mutable.HashMap[GlobalIndex[G], CFGNode[G]]()
def generate(entry: InstanceMethod[G]): CFGNode[G] = {
convert(entry.body.get, GlobalIndex[G])
}
def convert(node: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
+ // If a node has already been visited, then it should not be created again
+ if (converted_nodes.contains(context)) return converted_nodes(context)
// Create new node with its successors (if possible)
val cfg_node: CFGNode[G] = CFGNode(node, find_successors(node, context))
// Handle labels and goto statements
@@ -31,17 +33,15 @@ case class CFGGenerator[G]() {
}
}
}
+ converted_nodes.addOne((context, cfg_node))
cfg_node
}
def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGNode[G]] = node match {
case PVLBranch(branches) =>
mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2)))) // TODO: Implement support for statements in expressions
- case PVLLoop(init, cond, update, _, body) => {
- val cond_node: Eval[G] = Eval(cond)(cond.o)
- // TODO: Implement!
- ???
- }
+ case PVLLoop(init, _, _, _, _) =>
+ mutable.Set(convert(init, context.enter_scope(node)))
// NonExecutableStatement
case LocalDecl(_) => sequential_successor(context)
case SpecIgnoreStart() => sequential_successor(context) // What is this?
@@ -66,10 +66,13 @@ case class CFGGenerator[G]() {
case Refute(assn) => sequential_successor(context)
case Inhale(res) => sequential_successor(context)
case Assume(assn) => sequential_successor(context) // <--
- case Instantiate(cls, out) => ??? // TODO
+ case Instantiate(_, out) => sequential_successor(context)
case Wait(_) => sequential_successor(context)
case Notify(_) => sequential_successor(context)
- case Fork(obj) => ??? // TODO: Find run method from expression
+ case Fork(obj) => {
+ val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect { case r: RunMethod[G] => r }.head
+ val run_inv: MethodInvocation[G] =
+ }
case Join(_) => sequential_successor(context)
case Lock(_) => sequential_successor(context)
case Unlock(_) => sequential_successor(context)
@@ -83,7 +86,7 @@ case class CFGGenerator[G]() {
case Extract(contractedStatement) =>
mutable.Set(convert(contractedStatement, context.enter_scope(node)))
// ExceptionalStatement
- case Eval(expr) => ??? // TODO
+ case Eval(expr) => ??? // TODO: Resolve expression statements!
// InvocationStatement
case InvokeProcedure(ref, args, _, _, givenMap, _) => {
if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
@@ -120,13 +123,12 @@ case class CFGGenerator[G]() {
case IndetBranch(branches) =>
mutable.Set(branches.zipWithIndex.map(b => convert(b._1, context.enter_scope(node, b._2))))
case Switch(expr, body) => ??? // TODO: Ask Pieter about switch statements
- case Loop(init, cond, update, _, body) => {
- ??? // TODO
- }
+ case Loop(init, _, _, _, _) =>
+ mutable.Set(convert(init, context.enter_scope(node)))
case RangedFor(_, _, body) =>
mutable.Set(convert(body, context.enter_scope(node)))
case TryCatchFinally(body, _, _) =>
- mutable.Set(convert(body, context.enter_scope(node))) // TODO: Is it safe to ignore the "after" part here?
+ mutable.Set(convert(body, context.enter_scope(node)))
case Synchronized(_, body) =>
mutable.Set(convert(body, context.enter_scope(node)))
case ParInvariant(_, _, content) =>
@@ -161,9 +163,8 @@ case class CFGGenerator[G]() {
case SeqAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
case UnresolvedSeqBranch(branches) =>
mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2))))
- case UnresolvedSeqLoop(cond, _, body) => {
- ??? // TODO: Implement
- }
+ case UnresolvedSeqLoop(cond, _, _) =>
+ mutable.Set(convert(Eval(cond)(cond.o), context.enter_scope(node)))
case SeqBranch(_, yes, no) => no match {
case Some(stmt) => mutable.Set(convert(yes, context.enter_scope(node, 0)), convert(stmt, context.enter_scope(node, 1)))
case None => mutable.Set(convert(yes, context.enter_scope(node)))
@@ -176,11 +177,8 @@ case class CFGGenerator[G]() {
mutable.Set(convert(assign, context.enter_scope(node)))
}
- def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] = {
- val next_index: GlobalIndex[G] = index.make_step()
- val next_statement: Statement[G] = next_index.resolve()
-
- }
+ def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] =
+ mutable.Set(index.make_step().map(i => convert(i.resolve(), i)))
def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
??? // TODO: Implement!
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 29d5f1c1d9..0e62a6fa3d 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -4,26 +4,32 @@ import vct.col.ast._
case class GlobalIndex[G](indices: List[Index[G]]) {
- def enter_scope(statement: Statement[G], index: Int = 0): GlobalIndex[G] =
- GlobalIndex(Index[G](statement, index) :: indices)
-
- def leave_scope(): GlobalIndex[G] =
- GlobalIndex(indices.tail).make_step()
-
- def make_step(): GlobalIndex[G] = indices.head.make_step() match {
- case Some(idx) => GlobalIndex(idx :: indices.tail)
- case None => GlobalIndex(indices.tail).make_step()
+ def enter_scope(node: Node[G], index: Int = 0): GlobalIndex[G] =
+ GlobalIndex(Index[G](node, index) :: indices)
+
+ def make_step(): Set[GlobalIndex[G]] = {
+ if (indices.isEmpty) return Set[GlobalIndex[G]]()
+ val steps: Set[Option[Index[G]]] = indices.head.make_step()
+ var res = Set[GlobalIndex[G]]()
+ for (step <- steps) {
+ step match {
+ case Some(index) => res = res ++ GlobalIndex(index +: indices.tail)
+ case None => res = res ++ GlobalIndex(indices.tail).make_step()
+ }
+ }
+ res
}
def resolve(): Statement[G] = indices.head.resolve()
}
sealed trait Index[G] {
- def make_step(): Option[Index[G]]
+ def make_step(): Set[Option[Index[G]]]
def resolve(): Statement[G]
}
object Index {
+ def apply[G](run_method: RunMethod[G], index: Int): Index[G] = RunMethodIndex(run_method)
def apply[G](pvl_branch: PVLBranch[G], index: Int): Index[G] = PVLBranchIndex(pvl_branch, index)
def apply[G](pvl_loop: PVLLoop[G], index: Int): Index[G] = PVLLoopIndex(pvl_loop, index)
def apply[G](label: Label[G], index: Int): Index[G] = LabelIndex(label)
@@ -57,15 +63,22 @@ object Index {
def apply[G](communicatex: CommunicateX[G], index: Int): Index[G] = CommunicateXIndex(communicatex)
}
+case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def resolve(): Statement[G] = run_method.body.get
+}
+
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = pvl_branch.branches.apply(index)._2 // TODO: Handle expressions in branch conditions
}
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index < 3) Some(PVLLoopIndex(pvl_loop, index + 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = index match {
+ case 0 => Set(Some(PVLLoopIndex(pvl_loop, 1)))
+ case 1 => Set(Some(PVLLoopIndex(pvl_loop, 2)), None)
+ case 2 => Set(Some(PVLLoopIndex(pvl_loop, 3)))
+ case 3 => Set(Some(PVLLoopIndex(pvl_loop, 1)))
}
override def resolve(): Statement[G] = index match {
case 0 => pvl_loop.init
@@ -76,29 +89,29 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
}
case class LabelIndex[G](label: Label[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = label.stat
}
case class FramedProofIndex[G](framed_proof: FramedProof[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = framed_proof.body
}
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = extract.contractedStatement
}
case class EvalIndex[G](eval: Eval[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = None // TODO: Implement expressions!
+ override def make_step(): Set[Option[Index[G]]] = Set(None) // TODO: Implement expressions!
override def resolve(): Statement[G] = ??? // TODO: Implement expressions!
}
case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index < invoke_procedure.args.size) Some(InvokeProcedureIndex(invoke_procedure, index + 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < invoke_procedure.args.size) Set(Some(InvokeProcedureIndex(invoke_procedure, index + 1)))
+ else Set(None)
}
override def resolve(): Statement[G] = {
if (index < invoke_procedure.args.size) Eval(invoke_procedure.args.apply(index))(invoke_procedure.args.apply(index).o)
@@ -107,9 +120,9 @@ case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index:
}
case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index < invoke_constructor.args.size) Some(InvokeConstructorIndex(invoke_constructor, index + 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < invoke_constructor.args.size) Set(Some(InvokeConstructorIndex(invoke_constructor, index + 1)))
+ else Set(None)
}
override def resolve(): Statement[G] = {
if (index < invoke_constructor.args.size) Eval(invoke_constructor.args.apply(index))(invoke_constructor.args.apply(index).o)
@@ -118,9 +131,9 @@ case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], i
}
case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index < invoke_method.args.size) Some(InvokeMethodIndex(invoke_method, index + 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < invoke_method.args.size) Set(Some(InvokeMethodIndex(invoke_method, index + 1)))
+ else Set(None)
}
override def resolve(): Statement[G] = {
if (index < invoke_method.args.size) Eval(invoke_method.args.apply(index))(invoke_method.args.apply(index).o)
@@ -129,37 +142,39 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
}
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index < block.statements.size - 1) Some(BlockIndex(block, index + 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < block.statements.size - 1) Set(Some(BlockIndex(block, index + 1)))
+ else Set(None)
}
override def resolve(): Statement[G] = block.statements.apply(index)
}
case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = scope.body
}
case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = branch.branches.apply(index)._2
}
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
}
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = switch.body
}
case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index < 3) Some(LoopIndex(loop, index + 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = index match {
+ case 0 => Set(Some(LoopIndex(loop, 1)))
+ case 1 => Set(Some(LoopIndex(loop, 2)), None)
+ case 2 => Set(Some(LoopIndex(loop, 3)))
+ case 3 => Set(Some(LoopIndex(loop, 1)))
}
override def resolve(): Statement[G] = index match {
case 0 => loop.init
@@ -170,70 +185,71 @@ case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
}
case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = ranged_for.body
}
case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index == 0) Some(TryCatchFinallyIndex(try_catch_finally, 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index != 1) Set(Some(TryCatchFinallyIndex(try_catch_finally, 1)))
+ else Set(None)
}
override def resolve(): Statement[G] = index match {
case 0 => try_catch_finally.body
case 1 => try_catch_finally.after
+ case _ => try_catch_finally.catches.apply(index - 2).body
}
}
case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = synchronized.body
}
case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = par_invariant.content
}
case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = par_atomic.content
}
case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = par_barrier.content
}
case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = vec_block.content
}
case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = wand_package.proof
}
case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = model_do.impl
}
case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = cpp_lifetime_scope.body
}
case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = unresolved_seq_branch.branches.apply(index)._2
}
case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = {
- if (index == 0) Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1))
- else None
+ override def make_step(): Set[Option[Index[G]]] = index match {
+ case 0 => Set(Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1)), None)
+ case 1 => Set(Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0)))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(unresolved_seq_loop.cond)(unresolved_seq_loop.cond.o)
@@ -242,7 +258,7 @@ case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G],
}
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = index match {
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
@@ -250,16 +266,16 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = seq_loop.body
}
case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = veymont_assign_expression.assign
}
case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
- override def make_step(): Option[Index[G]] = None
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = communicatex.assign
}
\ No newline at end of file
From 967a6c40f664af549c50a414641e65e560f2a72d Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 13 Feb 2024 09:48:12 +0100
Subject: [PATCH 10/85] Added support for break, continue, return and throw and
started on switch statements
---
.../vct/rewrite/cfg/CFGGenerator.scala | 39 +++++++++-----
src/rewrite/vct/rewrite/cfg/CFGNode.scala | 2 +-
src/rewrite/vct/rewrite/cfg/Index.scala | 52 +++++++++++++++++++
3 files changed, 78 insertions(+), 15 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index b8115c0daf..c2984b7531 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -50,8 +50,8 @@ case class CFGGenerator[G]() {
case Assign(target, value) => sequential_successor(context) // TODO: Implement support for statements in expressions
case Send(_, _, res) => sequential_successor(context) // TODO: Implement support for statements in expressions
case Recv(_) => sequential_successor(context)
- case DefaultCase() => ??? // TODO: Ask Pieter about switch statements
- case Case(pattern) => ???
+ case DefaultCase() => sequential_successor(context)
+ case Case(pattern) => sequential_successor(context)
case Label(_, stat) =>
mutable.Set(convert(stat, context.enter_scope(node)))
case Goto(lbl) => {
@@ -71,7 +71,7 @@ case class CFGGenerator[G]() {
case Notify(_) => sequential_successor(context)
case Fork(obj) => {
val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect { case r: RunMethod[G] => r }.head
- val run_inv: MethodInvocation[G] =
+ sequential_successor(context).addOne(convert(run_method.body.get, context.enter_scope(run_method)))
}
case Join(_) => sequential_successor(context)
case Lock(_) => sequential_successor(context)
@@ -86,7 +86,7 @@ case class CFGGenerator[G]() {
case Extract(contractedStatement) =>
mutable.Set(convert(contractedStatement, context.enter_scope(node)))
// ExceptionalStatement
- case Eval(expr) => ??? // TODO: Resolve expression statements!
+ case Eval(expr) => ??? // TODO: Implement side effects in expressions (see ResolveExpressionSideEffects.scala)
// InvocationStatement
case InvokeProcedure(ref, args, _, _, givenMap, _) => {
if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
@@ -122,7 +122,10 @@ case class CFGGenerator[G]() {
mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2)))) // TODO: Handle statements in condition expressions!
case IndetBranch(branches) =>
mutable.Set(branches.zipWithIndex.map(b => convert(b._1, context.enter_scope(node, b._2))))
- case Switch(expr, body) => ??? // TODO: Ask Pieter about switch statements
+ case Switch(expr, body) => { // TODO: Handle expressions
+ val cases: mutable.Set[(SwitchCase[G], GlobalIndex[G])] = find_all_cases(body, context)
+ cases.map(t => convert(t._1, t._2))
+ }
case Loop(init, _, _, _, _) =>
mutable.Set(convert(init, context.enter_scope(node)))
case RangedFor(_, _, body) =>
@@ -177,22 +180,30 @@ case class CFGGenerator[G]() {
mutable.Set(convert(assign, context.enter_scope(node)))
}
- def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] =
+ private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] =
mutable.Set(index.make_step().map(i => convert(i.resolve(), i)))
- def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
- ??? // TODO: Implement!
+ private def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ val new_index: GlobalIndex[G] = index.return_from_call()
+ convert(new_index.resolve(), new_index)
+ }
+
+ private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGNode[G] = {
+ val new_index = index.handle_exception(exception)
+ convert(new_index.resolve(), new_index)
}
- def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGNode[G] = {
- ??? // TODO: Implement!
+ private def break_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ val new_index = index.handle_break()
+ convert(new_index.resolve(), new_index)
}
- def break_successor(index: GlobalIndex[G]): CFGNode[G] = {
- ??? // TODO: Implement!
+ private def continue_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ val new_index = index.continue_innermost_loop()
+ convert(new_index.resolve(), new_index)
}
- def continue_successor(index: GlobalIndex[G]): CFGNode[G] = {
- ??? // TODO: Implement!
+ private def find_all_cases(switch_body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = {
+ ???
}
}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGNode.scala b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
index e86db9805a..6a6761b4eb 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGNode.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
@@ -2,4 +2,4 @@ package vct.rewrite.cfg
import vct.col.ast.Node
-case class CFGNode[G](ast_node: Node[G], successors: scala.collection.mutable.Set[CFGNode[G]])
\ No newline at end of file
+case class CFGNode[G](ast_node: Node[G], successors: scala.collection.mutable.Set[CFGNode[G]]) // TODO: Add condition information to edges
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 0e62a6fa3d..66309ab22d 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -21,6 +21,58 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
}
def resolve(): Statement[G] = indices.head.resolve()
+
+ def return_from_call(): GlobalIndex[G] = {
+ // Find innermost subroutine call
+ val stack: List[Index[G]] = indices.dropWhile {
+ case InvokeProcedureIndex(_, _) | InvokeMethodIndex(_, _) => false
+ case _ => true
+ }
+ // Find the next statement
+ // TODO: Does this always return exactly one next step?
+ GlobalIndex(stack.tail).make_step().head
+ }
+
+ def handle_exception(e: Expr[G]): GlobalIndex[G] = {
+ // Find innermost try-catch block of appropriate type
+ val stack: List[Index[G]] = indices.dropWhile {
+ case TryCatchFinallyIndex(stmt, _) => !stmt.catches.exists(c => c.decl.t.equals(e.t))
+ case _ => true
+ }
+ // Unhandled exception
+ if (stack.isEmpty) return GlobalIndex(stack)
+ // Return to exception handler and go to catch block
+ stack.head match {
+ case TryCatchFinallyIndex(stmt, _) => GlobalIndex(TryCatchFinallyIndex(stmt, stmt.catches.indexWhere(c => c.decl.t.equals(e.t)) + 2) +: stack.tail)
+ }
+ }
+
+ def handle_break(): GlobalIndex[G] = {
+ // Find innermost occurrence of either a loop or a switch statement
+ val stack: List[Index[G]] = indices.dropWhile {
+ case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true
+ case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) | SwitchIndex(_) => false
+ case _ => true
+ }
+ // Find the next statement
+ // TODO: Does this always return exactly one next step?
+ GlobalIndex(stack.tail).make_step().head
+ }
+
+ def continue_innermost_loop(): GlobalIndex[G] = {
+ // Find innermost loop that could be the target of continue
+ val stack: List[Index[G]] = indices.dropWhile {
+ case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true // If some godless heathen calls break in the init of a loop, don't consider that loop
+ case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) => false
+ case _ => true
+ }
+ stack.head match {
+ case PVLLoopIndex(pvl_loop, _) => GlobalIndex(PVLLoopIndex(pvl_loop, 1) +: stack.tail)
+ case LoopIndex(loop, _) => GlobalIndex(LoopIndex(loop, 1) +: stack.tail)
+ case UnresolvedSeqLoopIndex(unresolved_seq_loop, _) => GlobalIndex(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0) +: stack.tail)
+ case RangedForIndex(_) | SeqLoopIndex(_) => GlobalIndex(stack)
+ }
+ }
}
sealed trait Index[G] {
From d36e867bd9c543bf3ac56fd79a68f8e0d397bb38 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 13 Feb 2024 14:21:43 +0100
Subject: [PATCH 11/85] Added general support for statements hidden in
expressions with side effects
---
.../vct/rewrite/cfg/CFGGenerator.scala | 38 ++++++++++++-------
src/rewrite/vct/rewrite/cfg/CFGNode.scala | 7 +++-
src/rewrite/vct/rewrite/cfg/Index.scala | 25 +++++++++---
src/rewrite/vct/rewrite/cfg/Utils.scala | 31 +++++++++++++++
4 files changed, 80 insertions(+), 21 deletions(-)
create mode 100644 src/rewrite/vct/rewrite/cfg/Utils.scala
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index c2984b7531..7ffe5566b4 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -1,6 +1,8 @@
package vct.rewrite.cfg
import vct.col.ast._
+import vct.col.origin.Origin
+import vct.col.ref.{Ref, DirectRef}
import scala.collection.mutable
@@ -87,6 +89,16 @@ case class CFGGenerator[G]() {
mutable.Set(convert(contractedStatement, context.enter_scope(node)))
// ExceptionalStatement
case Eval(expr) => ??? // TODO: Implement side effects in expressions (see ResolveExpressionSideEffects.scala)
+ case Return(result) => mutable.Set(return_successor(context))
+ case Throw(obj) => mutable.Set(exception_successor(obj, context))
+ case Break(label) => label match {
+ case Some(ref) => ??? // TODO: Handle break label!
+ case None => mutable.Set(break_successor(context))
+ }
+ case Continue(label) => label match {
+ case Some(ref) => ??? // TODO: Handle continue label!
+ case None => mutable.Set(continue_successor(context))
+ }
// InvocationStatement
case InvokeProcedure(ref, args, _, _, givenMap, _) => {
if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
@@ -103,16 +115,6 @@ case class CFGGenerator[G]() {
else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
else sequential_successor(context)
}
- case Return(result) => mutable.Set(return_successor(context))
- case Throw(obj) => mutable.Set(exception_successor(obj, context))
- case Break(label) => label match {
- case Some(ref) => ??? // TODO: Handle break label!
- case None => mutable.Set(break_successor(context))
- }
- case Continue(label) => label match {
- case Some(ref) => ??? // TODO: Handle continue label!
- case None => mutable.Set(continue_successor(context))
- }
// CompositeStatement
case Block(statements) =>
mutable.Set(convert(statements.head, context.enter_scope(node)))
@@ -190,6 +192,8 @@ case class CFGGenerator[G]() {
private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGNode[G] = {
val new_index = index.handle_exception(exception)
+ // Terminate on unhandled exception
+ if (new_index.indices.isEmpty) return CFGNode(exception, mutable.Set())
convert(new_index.resolve(), new_index)
}
@@ -203,7 +207,15 @@ case class CFGGenerator[G]() {
convert(new_index.resolve(), new_index)
}
- private def find_all_cases(switch_body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = {
- ???
+ private def find_all_cases(body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = body match {
+ // Recursion on statements that can contain case statements
+ case Label(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
+ case Block(stmts) => mutable.Set(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
+ case Scope(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
+ // Recursion end
+ case c: SwitchCase[G] => mutable.Set((c, index))
+ case _ => mutable.Set() // TODO: Assuming that there are no cases in deeper structures (branches, loops etc.)
}
-}
+
+ private def handle_expression_successor(expr: Expr[G], index: GlobalIndex[G]): Option[CFGNode[G]] = ???
+}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/CFGNode.scala b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
index 6a6761b4eb..b6b0ce6974 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGNode.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
@@ -1,5 +1,8 @@
package vct.rewrite.cfg
-import vct.col.ast.Node
+import vct.col.ast.{Expr, Node}
-case class CFGNode[G](ast_node: Node[G], successors: scala.collection.mutable.Set[CFGNode[G]]) // TODO: Add condition information to edges
\ No newline at end of file
+import scala.collection.mutable
+
+case class CFGNode[G](ast_node: Node[G], successors: mutable.Set[CFGNode[G]]) // TODO: Add condition information to edges
+case class CFGEdge[G](target: CFGNode[G], condition: Option[Expr[G]])
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 66309ab22d..0f96c495c1 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -36,7 +36,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def handle_exception(e: Expr[G]): GlobalIndex[G] = {
// Find innermost try-catch block of appropriate type
val stack: List[Index[G]] = indices.dropWhile {
- case TryCatchFinallyIndex(stmt, _) => !stmt.catches.exists(c => c.decl.t.equals(e.t))
+ case TryCatchFinallyIndex(stmt, 0) => !stmt.catches.exists(c => c.decl.t.equals(e.t))
case _ => true
}
// Unhandled exception
@@ -50,7 +50,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def handle_break(): GlobalIndex[G] = {
// Find innermost occurrence of either a loop or a switch statement
val stack: List[Index[G]] = indices.dropWhile {
- case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true
+ case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true // If some godless heathen calls break in the init of a loop, don't consider that loop
case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) | SwitchIndex(_) => false
case _ => true
}
@@ -62,7 +62,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def continue_innermost_loop(): GlobalIndex[G] = {
// Find innermost loop that could be the target of continue
val stack: List[Index[G]] = indices.dropWhile {
- case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true // If some godless heathen calls break in the init of a loop, don't consider that loop
+ case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true
case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) => false
case _ => true
}
@@ -81,6 +81,7 @@ sealed trait Index[G] {
}
object Index {
+ def apply[G](instance_method: InstanceMethod[G], index: Int): Index[G] = InitialIndex(instance_method)
def apply[G](run_method: RunMethod[G], index: Int): Index[G] = RunMethodIndex(run_method)
def apply[G](pvl_branch: PVLBranch[G], index: Int): Index[G] = PVLBranchIndex(pvl_branch, index)
def apply[G](pvl_loop: PVLLoop[G], index: Int): Index[G] = PVLLoopIndex(pvl_loop, index)
@@ -115,6 +116,11 @@ object Index {
def apply[G](communicatex: CommunicateX[G], index: Int): Index[G] = CommunicateXIndex(communicatex)
}
+case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
+ override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def resolve(): Statement[G] = instance_method.body.get
+}
+
case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = run_method.body.get
@@ -155,9 +161,16 @@ case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
override def resolve(): Statement[G] = extract.contractedStatement
}
-case class EvalIndex[G](eval: Eval[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None) // TODO: Implement expressions!
- override def resolve(): Statement[G] = ??? // TODO: Implement expressions!
+case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement[G]]) extends Index[G] {
+ def this(eval: Eval[G], index: Int) = this(eval, index, Utils.find_all_subexpressions(eval.expr))
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < subexpressions.size - 1) Set(Some(EvalIndex(eval, index + 1)))
+ else Set(None)
+ }
+ override def resolve(): Statement[G] = subexpressions.apply(index)
+}
+object EvalIndex {
+ def apply[G](eval: Eval[G], index: Int): EvalIndex[G] = new EvalIndex(eval, index)
}
case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index: Int) extends Index[G] {
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
new file mode 100644
index 0000000000..5d2417e8aa
--- /dev/null
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -0,0 +1,31 @@
+package vct.rewrite.cfg
+
+import vct.col.ast._
+import vct.col.origin.Origin
+import vct.col.ref.{DirectRef, Ref}
+
+object Utils {
+
+ def find_all_subexpressions[G](expr: Expr[G]): Seq[Statement[G]] = expr match {
+ case pae @ PreAssignExpression(target, value) => Seq(Assign(target, value)(pae.blame)(pae.o))
+ case pae @ PostAssignExpression(target, value) => Seq(Assign(target, value)(pae.blame)(pae.o))
+ case With(pre, value) => ???
+ case Then(value, post) => ???
+ case mi @ MethodInvocation(obj, ref, args, outArgs, typeArgs, givenMap, yields) =>
+ Seq(InvokeMethod(obj, ref, args, outArgs, typeArgs, givenMap, yields)(mi.blame)(mi.o))
+ case ci @ ConstructorInvocation(ref, args, outArgs, typeArgs, givenMap, yields) =>
+ Seq(InvokeConstructor(ref, get_out_variable(ref.decl.cls, ci.o), args, outArgs, typeArgs, givenMap, yields)(ci.blame)(ci.o))
+ case pi @ ProcedureInvocation(ref, args, outArgs, typeArgs, givenMap, yields) =>
+ Seq(InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields)(pi.blame)(pi.o))
+ case no @ NewObject(cls) => Seq(Instantiate(cls, get_out_variable(cls, no.o))(no.o))
+ // TODO: Consider conditions for control flow graph
+ case Select(condition, whenTrue, whenFalse) => Seq(condition, whenTrue, whenFalse).flatMap(e => find_all_subexpressions(e))
+ case Implies(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
+ case And(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
+ case Or(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
+ //
+ case _ => expr.subnodes.collect{ case ex: Expr[G] => ex }.flatMap(e => find_all_subexpressions(e))
+ }
+
+ private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = Local(new DirectRef(new Variable(TClass(cls))(o)))(o)
+}
From 7aedcd456935c071c50de05311ae522731c3f825 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 13 Feb 2024 17:16:47 +0100
Subject: [PATCH 12/85] Improved support for expressions with side effects
---
.../vct/rewrite/cfg/CFGGenerator.scala | 84 ++++++++++---------
src/rewrite/vct/rewrite/cfg/Index.scala | 73 ++++++++++++++--
src/rewrite/vct/rewrite/cfg/Utils.scala | 12 +++
3 files changed, 120 insertions(+), 49 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 7ffe5566b4..786b38d62d 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -40,20 +40,22 @@ case class CFGGenerator[G]() {
}
def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGNode[G]] = node match {
- case PVLBranch(branches) =>
- mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2)))) // TODO: Implement support for statements in expressions
+ case PVLBranch(branches) => {
+ val eval: Eval[G] = Eval(branches.head._1)(branches.head._1.o)
+ mutable.Set(convert(eval, context.enter_scope(node)))
+ }
case PVLLoop(init, _, _, _, _) =>
mutable.Set(convert(init, context.enter_scope(node)))
// NonExecutableStatement
case LocalDecl(_) => sequential_successor(context)
- case SpecIgnoreStart() => sequential_successor(context) // What is this?
+ case SpecIgnoreStart() => sequential_successor(context) // TODO: What is this?
case SpecIgnoreEnd() => sequential_successor(context)
// NormallyCompletingStatement
- case Assign(target, value) => sequential_successor(context) // TODO: Implement support for statements in expressions
- case Send(_, _, res) => sequential_successor(context) // TODO: Implement support for statements in expressions
+ case Assign(target, value) => sequential_successor(context) // TODO: Implement support for multiple expressions in container statement
+ case Send(_, _, res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Recv(_) => sequential_successor(context)
case DefaultCase() => sequential_successor(context)
- case Case(pattern) => sequential_successor(context)
+ case Case(pattern) => sequential_successor(context) // TODO: Handle expression side effects in switch case conditions
case Label(_, stat) =>
mutable.Set(convert(stat, context.enter_scope(node)))
case Goto(lbl) => {
@@ -63,12 +65,12 @@ case class CFGGenerator[G]() {
case None => mutable.Set()
}
}
- case Exhale(res) => sequential_successor(context) // TODO: Can expressions in specifications be ignored?
- case Assert(res) => sequential_successor(context)
- case Refute(assn) => sequential_successor(context)
- case Inhale(res) => sequential_successor(context)
- case Assume(assn) => sequential_successor(context) // <--
- case Instantiate(_, out) => sequential_successor(context)
+ case Exhale(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
+ case Assert(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
+ case Refute(assn) => handle_expression_container(node, Eval(assn)(assn.o), context, sequential_successor(context))
+ case Inhale(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
+ case Assume(assn) => handle_expression_container(node, Eval(assn)(assn.o), context, sequential_successor(context))
+ case Instantiate(_, out) => handle_expression_container(node, Eval(out)(out.o), context, sequential_successor(context))
case Wait(_) => sequential_successor(context)
case Notify(_) => sequential_successor(context)
case Fork(obj) => {
@@ -79,17 +81,17 @@ case class CFGGenerator[G]() {
case Lock(_) => sequential_successor(context)
case Unlock(_) => sequential_successor(context)
case Commit(_) => sequential_successor(context)
- case Fold(res) => sequential_successor(context) // TODO: Can expressions in specifications be ignored?
- case Unfold(res) => sequential_successor(context)
- case WandApply(res) => sequential_successor(context) // <--
+ case Fold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
+ case Unfold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
+ case WandApply(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Havoc(_) => sequential_successor(context)
case FramedProof(pre, body, post) =>
- mutable.Set(convert(body, context.enter_scope(node))) // TODO: Can expressions in specifications be ignored?
+ ??? // TODO: Support multiple expressions in a container statement
case Extract(contractedStatement) =>
mutable.Set(convert(contractedStatement, context.enter_scope(node)))
// ExceptionalStatement
- case Eval(expr) => ??? // TODO: Implement side effects in expressions (see ResolveExpressionSideEffects.scala)
- case Return(result) => mutable.Set(return_successor(context))
+ case Eval(_) => sequential_successor(context.enter_scope(node)) // TODO: Is this correct?
+ case Return(result) => handle_expression_container(node, Eval(result)(result.o), context, mutable.Set(return_successor(context)))
case Throw(obj) => mutable.Set(exception_successor(obj, context))
case Break(label) => label match {
case Some(ref) => ??? // TODO: Handle break label!
@@ -100,7 +102,7 @@ case class CFGGenerator[G]() {
case None => mutable.Set(continue_successor(context))
}
// InvocationStatement
- case InvokeProcedure(ref, args, _, _, givenMap, _) => {
+ case InvokeProcedure(ref, args, _, _, _, _) => {
if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
else sequential_successor(context)
@@ -120,13 +122,15 @@ case class CFGGenerator[G]() {
mutable.Set(convert(statements.head, context.enter_scope(node)))
case Scope(_, body) =>
mutable.Set(convert(body, context.enter_scope(node)))
- case Branch(branches) =>
- mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2)))) // TODO: Handle statements in condition expressions!
+ case Branch(branches) => {
+ val eval: Eval[G] = Eval(branches.head._1)(branches.head._1.o)
+ mutable.Set(convert(eval, context.enter_scope(node)))
+ }
case IndetBranch(branches) =>
mutable.Set(branches.zipWithIndex.map(b => convert(b._1, context.enter_scope(node, b._2))))
- case Switch(expr, body) => { // TODO: Handle expressions
- val cases: mutable.Set[(SwitchCase[G], GlobalIndex[G])] = find_all_cases(body, context)
- cases.map(t => convert(t._1, t._2))
+ case Switch(expr, body) => {
+ val cases: mutable.Set[(SwitchCase[G], GlobalIndex[G])] = Utils.find_all_cases(body, context.enter_scope(node))
+ cases.map(t => convert(t._1, t._2)) // TODO: Handle side effects in switch statement conditions
}
case Loop(init, _, _, _, _) =>
mutable.Set(convert(init, context.enter_scope(node)))
@@ -151,7 +155,7 @@ case class CFGGenerator[G]() {
??? // TODO
// CStatement
case CDeclarationStatement(_) => sequential_successor(context)
- case CGoto(label) => ???
+ case CGoto(label) => ??? // I'm not dealing with string labels
// CPPStatement
case CPPDeclarationStatement(_) => sequential_successor(context)
case CPPLifetimeScope(body) =>
@@ -159,13 +163,13 @@ case class CFGGenerator[G]() {
case JavaLocalDeclarationStatement(_) => sequential_successor(context)
// SilverStatement
case SilverNewRef(_, _) => sequential_successor(context)
- case SilverFieldAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
- case SilverLocalAssign(_, value) => sequential_successor(context) // TODO: Statements in expressions
+ case SilverFieldAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
+ case SilverLocalAssign(_, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
// OTHER
case PVLCommunicate(_, _) => sequential_successor(context)
- case PVLSeqAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
+ case PVLSeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
case Communicate(_, _) => sequential_successor(context)
- case SeqAssign(_, _, value) => sequential_successor(context) // TODO: Statements in expressions
+ case SeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
case UnresolvedSeqBranch(branches) =>
mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2))))
case UnresolvedSeqLoop(cond, _, _) =>
@@ -182,6 +186,16 @@ case class CFGGenerator[G]() {
mutable.Set(convert(assign, context.enter_scope(node)))
}
+ private def handle_expression_container(statement: Statement[G],
+ expression: Eval[G],
+ context: GlobalIndex[G],
+ successors_to_statement: mutable.Set[CFGNode[G]]): mutable.Set[CFGNode[G]] = {
+ context.indices.head match {
+ case ExpressionContainerIndex(stmt, 1) if stmt == statement => successors_to_statement
+ case _ => mutable.Set(convert(expression, context.enter_scope(expression))) // TODO: Handle containers with multiple expressions
+ }
+ }
+
private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] =
mutable.Set(index.make_step().map(i => convert(i.resolve(), i)))
@@ -206,16 +220,4 @@ case class CFGGenerator[G]() {
val new_index = index.continue_innermost_loop()
convert(new_index.resolve(), new_index)
}
-
- private def find_all_cases(body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = body match {
- // Recursion on statements that can contain case statements
- case Label(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
- case Block(stmts) => mutable.Set(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
- case Scope(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
- // Recursion end
- case c: SwitchCase[G] => mutable.Set((c, index))
- case _ => mutable.Set() // TODO: Assuming that there are no cases in deeper structures (branches, loops etc.)
- }
-
- private def handle_expression_successor(expr: Expr[G], index: GlobalIndex[G]): Option[CFGNode[G]] = ???
}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 0f96c495c1..cdc03231a9 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -56,6 +56,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
}
// Find the next statement
// TODO: Does this always return exactly one next step?
+ // No, e.g.: another loop in the update statement of a loop
GlobalIndex(stack.tail).make_step().head
}
@@ -76,7 +77,20 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
}
sealed trait Index[G] {
+ /**
+ * Defines the set of possible next steps. An index in the returned set indicates that this index can replace the
+ * previous index at the top level of the index stack. A None value indicates that a step is possible, but it reaches
+ * outside the scope of this index to the index below.
+ *
+ * @return A set of all steps possible from the current index
+ */
def make_step(): Set[Option[Index[G]]]
+
+ /**
+ * Returns the statement that corresponds to the current index.
+ *
+ * @return The statement at the current index
+ */
def resolve(): Statement[G]
}
@@ -86,7 +100,7 @@ object Index {
def apply[G](pvl_branch: PVLBranch[G], index: Int): Index[G] = PVLBranchIndex(pvl_branch, index)
def apply[G](pvl_loop: PVLLoop[G], index: Int): Index[G] = PVLLoopIndex(pvl_loop, index)
def apply[G](label: Label[G], index: Int): Index[G] = LabelIndex(label)
- def apply[G](framed_proof: FramedProof[G], index: Int): Index[G] = FramedProofIndex(framed_proof)
+ def apply[G](framed_proof: FramedProof[G], index: Int): Index[G] = FramedProofIndex(framed_proof, index)
def apply[G](extract: Extract[G], index: Int): Index[G] = ExtractIndex(extract)
def apply[G](eval: Eval[G], index: Int): Index[G] = EvalIndex(eval, index)
def apply[G](invoke_procedure: InvokeProcedure[G], index: Int): Index[G] = InvokeProcedureIndex(invoke_procedure, index)
@@ -114,6 +128,8 @@ object Index {
def apply[G](seq_loop: SeqLoop[G], index: Int): Index[G] = SeqLoopIndex(seq_loop)
def apply[G](veymont_assign_expression: VeyMontAssignExpression[G], index: Int): Index[G] = VeyMontAssignExpressionIndex(veymont_assign_expression)
def apply[G](communicatex: CommunicateX[G], index: Int): Index[G] = CommunicateXIndex(communicatex)
+ def apply[G](assign: Assign[G], index: Int): Index[G] = AssignmentIndex(assign, index)
+ def apply[G](statement: Statement[G], index: Int): Index[G] = ExpressionContainerIndex(statement, index)
}
case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
@@ -126,9 +142,33 @@ case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
override def resolve(): Statement[G] = run_method.body.get
}
+case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) extends Index[G] {
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index == 0) Set(Some(ExpressionContainerIndex(statement, 1)))
+ else Set(None)
+ }
+ override def resolve(): Statement[G] = statement
+}
+
+// TODO: Is this really a good idea?
+case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
+ override def make_step(): Set[Option[Index[G]]] = ???
+ override def resolve(): Statement[G] = ???
+}
+
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
- override def resolve(): Statement[G] = pvl_branch.branches.apply(index)._2 // TODO: Handle expressions in branch conditions
+ override def make_step(): Set[Option[Index[G]]] = {
+ // Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
+ if (index % 2 == 0 && index < 2 * (pvl_branch.branches.size - 1))
+ Set(Some(PVLBranchIndex(pvl_branch, index + 2)), Some(PVLBranchIndex(pvl_branch, index + 1)))
+ else if (index == 2 * (pvl_branch.branches.size - 1))
+ Set(Some(PVLBranchIndex(pvl_branch, index + 1)), None)
+ else Set(None)
+ }
+ override def resolve(): Statement[G] = {
+ if (index % 2 == 0) Eval(pvl_branch.branches.apply(index / 2)._1)(pvl_branch.branches.apply(index / 2)._1.o)
+ else pvl_branch.branches.apply((index - 1) / 2)._2
+ }
}
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
@@ -151,9 +191,16 @@ case class LabelIndex[G](label: Label[G]) extends Index[G] {
override def resolve(): Statement[G] = label.stat
}
-case class FramedProofIndex[G](framed_proof: FramedProof[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
- override def resolve(): Statement[G] = framed_proof.body
+case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends Index[G] {
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < 2) Set(Some(FramedProofIndex(framed_proof, index + 1)))
+ else Set(None)
+ }
+ override def resolve(): Statement[G] = index match {
+ case 0 => Eval(framed_proof.pre)(framed_proof.pre.o)
+ case 1 => Eval(framed_proof.post)(framed_proof.post.o)
+ case 2 => framed_proof.body
+ }
}
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
@@ -220,8 +267,18 @@ case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
}
case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
- override def resolve(): Statement[G] = branch.branches.apply(index)._2
+ override def make_step(): Set[Option[Index[G]]] = {
+ // Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
+ if (index % 2 == 0 && index < 2 * (branch.branches.size - 1))
+ Set(Some(BranchIndex(branch, index + 2)), Some(BranchIndex(branch, index + 1)))
+ else if (index == 2 * (branch.branches.size - 1))
+ Set(Some(BranchIndex(branch, index + 1)), None)
+ else Set(None)
+ }
+ override def resolve(): Statement[G] = {
+ if (index % 2 == 0) Eval(branch.branches.apply(index / 2)._1)(branch.branches.apply(index / 2)._1.o)
+ else branch.branches.apply((index - 1) / 2)._2
+ }
}
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index 5d2417e8aa..e7c78340e4 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -4,6 +4,8 @@ import vct.col.ast._
import vct.col.origin.Origin
import vct.col.ref.{DirectRef, Ref}
+import scala.collection.mutable
+
object Utils {
def find_all_subexpressions[G](expr: Expr[G]): Seq[Statement[G]] = expr match {
@@ -28,4 +30,14 @@ object Utils {
}
private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = Local(new DirectRef(new Variable(TClass(cls))(o)))(o)
+
+ def find_all_cases[G](body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = body match {
+ // Recursion on statements that can contain case statements
+ case Label(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
+ case Block(stmts) => mutable.Set(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
+ case Scope(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
+ // Recursion end
+ case c: SwitchCase[G] => mutable.Set((c, index))
+ case _ => mutable.Set() // TODO: Assuming that there are no cases in deeper structures (branches, loops etc.)
+ }
}
From 344df78a54206ce01caf356768258e0ff753cc4a Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 14 Feb 2024 11:43:01 +0100
Subject: [PATCH 13/85] Continued support for expression side effects, some
syntax fixes, added condition information to edges
---
.../vct/rewrite/cfg/CFGGenerator.scala | 142 +++++++++---------
src/rewrite/vct/rewrite/cfg/CFGNode.scala | 2 +-
src/rewrite/vct/rewrite/cfg/Index.scala | 24 ++-
src/rewrite/vct/rewrite/cfg/Utils.scala | 4 +-
4 files changed, 94 insertions(+), 78 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 786b38d62d..98e359d0a1 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -1,9 +1,8 @@
package vct.rewrite.cfg
import vct.col.ast._
-import vct.col.origin.Origin
-import vct.col.ref.{Ref, DirectRef}
+import scala.collection.IterableOnce.iterableOnceExtensionMethods
import scala.collection.mutable
case class CFGGenerator[G]() {
@@ -12,10 +11,10 @@ case class CFGGenerator[G]() {
private val converted_nodes: mutable.Map[GlobalIndex[G], CFGNode[G]] = mutable.HashMap[GlobalIndex[G], CFGNode[G]]()
def generate(entry: InstanceMethod[G]): CFGNode[G] = {
- convert(entry.body.get, GlobalIndex[G])
+ convert(entry.body.get, GlobalIndex[G](List(InitialIndex(entry))))
}
- def convert(node: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
+ private def convert(node: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
// If a node has already been visited, then it should not be created again
if (converted_nodes.contains(context)) return converted_nodes(context)
// Create new node with its successors (if possible)
@@ -25,45 +24,42 @@ case class CFGGenerator[G]() {
case label: Label[G] => {
// For labels, add them to the label map and add them to any goto statements going to that label
found_labels.addOne((label.decl, cfg_node))
- searched_labels.getOrElse(label.decl, mutable.Set()).map(g => g.successors.addOne(cfg_node))
+ searched_labels.getOrElse(label.decl, mutable.Set()).map(g => g.successors.addOne(CFGEdge(cfg_node, None)))
}
- case goto: Goto[G] => {
- // For goto statements, if the label could not be resolved below, add the statement to the waiting list for the right label
+ case goto: Goto[G] =>
+ // For goto statements, if the label could not be resolved, add the statement to the waiting list for the right label
if (cfg_node.successors.isEmpty) {
if (searched_labels.contains(goto.lbl.decl)) searched_labels(goto.lbl.decl).addOne(cfg_node)
else searched_labels.addOne((goto.lbl.decl, mutable.Set(cfg_node)))
}
- }
}
converted_nodes.addOne((context, cfg_node))
cfg_node
}
- def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGNode[G]] = node match {
- case PVLBranch(branches) => {
- val eval: Eval[G] = Eval(branches.head._1)(branches.head._1.o)
- mutable.Set(convert(eval, context.enter_scope(node)))
- }
+ private def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = node match {
+ case PVLBranch(branches) =>
+ mutable.Set(CFGEdge(convert(Eval(branches.head._1)(branches.head._1.o), context.enter_scope(node)), None))
case PVLLoop(init, _, _, _, _) =>
- mutable.Set(convert(init, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(init, context.enter_scope(node)), None))
// NonExecutableStatement
case LocalDecl(_) => sequential_successor(context)
case SpecIgnoreStart() => sequential_successor(context) // TODO: What is this?
case SpecIgnoreEnd() => sequential_successor(context)
// NormallyCompletingStatement
- case Assign(target, value) => sequential_successor(context) // TODO: Implement support for multiple expressions in container statement
+ case Assign(target, _) => context.indices.head match {
+ case AssignmentIndex(a, _) if a == node => sequential_successor(context)
+ case _ => mutable.Set(CFGEdge(convert(Eval(target)(target.o), context.enter_scope(node)), None))
+ }
case Send(_, _, res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Recv(_) => sequential_successor(context)
case DefaultCase() => sequential_successor(context)
case Case(pattern) => sequential_successor(context) // TODO: Handle expression side effects in switch case conditions
case Label(_, stat) =>
- mutable.Set(convert(stat, context.enter_scope(node)))
- case Goto(lbl) => {
- val found_node: Option[CFGNode[G]] = found_labels.get(lbl.decl)
- found_node match {
- case Some(node) => mutable.Set(node)
- case None => mutable.Set()
- }
+ mutable.Set(CFGEdge(convert(stat, context.enter_scope(node)), None))
+ case Goto(lbl) => found_labels.get(lbl.decl) match {
+ case Some(node) => mutable.Set(CFGEdge(node, None))
+ case None => mutable.Set()
}
case Exhale(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Assert(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
@@ -74,8 +70,8 @@ case class CFGGenerator[G]() {
case Wait(_) => sequential_successor(context)
case Notify(_) => sequential_successor(context)
case Fork(obj) => {
- val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect { case r: RunMethod[G] => r }.head
- sequential_successor(context).addOne(convert(run_method.body.get, context.enter_scope(run_method)))
+ val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head
+ sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, context.enter_scope(run_method)), None))
}
case Join(_) => sequential_successor(context)
case Lock(_) => sequential_successor(context)
@@ -85,81 +81,80 @@ case class CFGGenerator[G]() {
case Unfold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case WandApply(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Havoc(_) => sequential_successor(context)
- case FramedProof(pre, body, post) =>
- ??? // TODO: Support multiple expressions in a container statement
+ case FramedProof(pre, _, _) =>
+ mutable.Set(CFGEdge(convert(Eval(pre)(pre.o), context.enter_scope(node)), None))
case Extract(contractedStatement) =>
- mutable.Set(convert(contractedStatement, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(contractedStatement, context.enter_scope(node)), None))
// ExceptionalStatement
- case Eval(_) => sequential_successor(context.enter_scope(node)) // TODO: Is this correct?
- case Return(result) => handle_expression_container(node, Eval(result)(result.o), context, mutable.Set(return_successor(context)))
- case Throw(obj) => mutable.Set(exception_successor(obj, context))
+ case Eval(_) => evaluate_first(context.enter_scope(node))
+ case Return(result) => handle_expression_container(node, Eval(result)(result.o), context, mutable.Set(CFGEdge(return_successor(context), None)))
+ case Throw(obj) => mutable.Set(CFGEdge(exception_successor(obj, context), None))
case Break(label) => label match {
case Some(ref) => ??? // TODO: Handle break label!
- case None => mutable.Set(break_successor(context))
+ case None => mutable.Set(CFGEdge(break_successor(context), None))
}
case Continue(label) => label match {
case Some(ref) => ??? // TODO: Handle continue label!
- case None => mutable.Set(continue_successor(context))
+ case None => mutable.Set(CFGEdge(continue_successor(context), None))
}
// InvocationStatement
- case InvokeProcedure(ref, args, _, _, _, _) => {
- if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
- else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
+ // TODO: Handle all the other possible expressions in invocations!
+ case InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields) => {
+ if (args.nonEmpty) mutable.Set(CFGEdge(convert(Eval(args.head)(args.head.o), context.enter_scope(node)), None))
+ else if (ref.decl.body.nonEmpty) mutable.Set(CFGEdge(convert(ref.decl.body.get, context.enter_scope(node)), None))
else sequential_successor(context)
}
case InvokeConstructor(ref, out, args, outArgs, typeArgs, givenMap, yields) => {
- if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
- else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
+ if (args.nonEmpty) mutable.Set(CFGEdge(convert(Eval(args.head)(args.head.o), context.enter_scope(node)), None))
+ else if (ref.decl.body.nonEmpty) mutable.Set(CFGEdge(convert(ref.decl.body.get, context.enter_scope(node)), None))
else sequential_successor(context)
}
case InvokeMethod(obj, ref, args, outArgs, typeArgs, givenMap, yields) => {
- if (args.nonEmpty) mutable.Set(convert(Eval(args.head)(args.head.o), context.enter_scope(node)))
- else if (ref.decl.body.nonEmpty) mutable.Set(convert(ref.decl.body.get, context.enter_scope(node)))
+ if (args.nonEmpty) mutable.Set(CFGEdge(convert(Eval(args.head)(args.head.o), context.enter_scope(node)), None))
+ else if (ref.decl.body.nonEmpty) mutable.Set(CFGEdge(convert(ref.decl.body.get, context.enter_scope(node)), None))
else sequential_successor(context)
}
// CompositeStatement
case Block(statements) =>
- mutable.Set(convert(statements.head, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(statements.head, context.enter_scope(node)), None))
case Scope(_, body) =>
- mutable.Set(convert(body, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case Branch(branches) => {
val eval: Eval[G] = Eval(branches.head._1)(branches.head._1.o)
- mutable.Set(convert(eval, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(eval, context.enter_scope(node)), None))
}
case IndetBranch(branches) =>
- mutable.Set(branches.zipWithIndex.map(b => convert(b._1, context.enter_scope(node, b._2))))
- case Switch(expr, body) => {
- val cases: mutable.Set[(SwitchCase[G], GlobalIndex[G])] = Utils.find_all_cases(body, context.enter_scope(node))
- cases.map(t => convert(t._1, t._2)) // TODO: Handle side effects in switch statement conditions
- }
+ mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
+ case Switch(expr, body) =>
+ Utils.find_all_cases(body, context.enter_scope(node)).map(t => CFGEdge(convert(t._1, t._2), None)) // TODO: Handle side effects in switch statement conditions
case Loop(init, _, _, _, _) =>
- mutable.Set(convert(init, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(init, context.enter_scope(node)), None))
case RangedFor(_, _, body) =>
- mutable.Set(convert(body, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case TryCatchFinally(body, _, _) =>
- mutable.Set(convert(body, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case Synchronized(_, body) =>
- mutable.Set(convert(body, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case ParInvariant(_, _, content) =>
- mutable.Set(convert(content, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
case ParAtomic(_, content) =>
- mutable.Set(convert(content, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
case ParBarrier(_, _, _, _, content) =>
- mutable.Set(convert(content, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
case ParStatement(_) => sequential_successor(context)
case VecBlock(_, _, _, content) =>
- mutable.Set(convert(content, context.enter_scope(node)))
- case WandPackage(res, proof) =>
- ??? // TODO
- case ModelDo(model, perm, after, action, impl) =>
- ??? // TODO
+ mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
+ case WandPackage(_, proof) =>
+ mutable.Set(CFGEdge(convert(proof, context.enter_scope(node)), None)) // TODO: Expression side effects
+ case ModelDo(_, _, _, _, impl) =>
+ mutable.Set(CFGEdge(convert(impl, context.enter_scope(node)), None)) // TODO: Expression side effects
// CStatement
case CDeclarationStatement(_) => sequential_successor(context)
case CGoto(label) => ??? // I'm not dealing with string labels
// CPPStatement
case CPPDeclarationStatement(_) => sequential_successor(context)
case CPPLifetimeScope(body) =>
- mutable.Set(convert(body, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case JavaLocalDeclarationStatement(_) => sequential_successor(context)
// SilverStatement
case SilverNewRef(_, _) => sequential_successor(context)
@@ -171,33 +166,38 @@ case class CFGGenerator[G]() {
case Communicate(_, _) => sequential_successor(context)
case SeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
case UnresolvedSeqBranch(branches) =>
- mutable.Set(branches.zipWithIndex.map(b => convert(b._1._2, context.enter_scope(node, b._2))))
+ mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1._2, context.enter_scope(node, b._2)), None)))
case UnresolvedSeqLoop(cond, _, _) =>
- mutable.Set(convert(Eval(cond)(cond.o), context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(Eval(cond)(cond.o), context.enter_scope(node)), None))
case SeqBranch(_, yes, no) => no match {
- case Some(stmt) => mutable.Set(convert(yes, context.enter_scope(node, 0)), convert(stmt, context.enter_scope(node, 1)))
- case None => mutable.Set(convert(yes, context.enter_scope(node)))
+ case Some(stmt) => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node, 0)), None), CFGEdge(convert(stmt, context.enter_scope(node, 1)), None))
+ case None => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None))
}
case SeqLoop(_, _, body) =>
- mutable.Set(convert(body, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case VeyMontAssignExpression(_, assign) =>
- mutable.Set(convert(assign, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(assign, context.enter_scope(node)), None))
case CommunicateX(_, _, _, assign) =>
- mutable.Set(convert(assign, context.enter_scope(node)))
+ mutable.Set(CFGEdge(convert(assign, context.enter_scope(node)), None))
}
private def handle_expression_container(statement: Statement[G],
expression: Eval[G],
context: GlobalIndex[G],
- successors_to_statement: mutable.Set[CFGNode[G]]): mutable.Set[CFGNode[G]] = {
+ successors_to_statement: mutable.Set[CFGEdge[G]]): mutable.Set[CFGEdge[G]] = {
context.indices.head match {
case ExpressionContainerIndex(stmt, 1) if stmt == statement => successors_to_statement
- case _ => mutable.Set(convert(expression, context.enter_scope(expression))) // TODO: Handle containers with multiple expressions
+ case _ => mutable.Set(CFGEdge(convert(expression, context.enter_scope(expression)), None))
}
}
- private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGNode[G]] =
- mutable.Set(index.make_step().map(i => convert(i.resolve(), i)))
+ private def evaluate_first(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
+ if (index.has_statement()) mutable.Set(CFGEdge(convert(index.resolve(), index), None))
+ else sequential_successor(index)
+ }
+
+ private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] =
+ mutable.LinkedHashSet.from(index.make_step().map(i => CFGEdge(convert(i.resolve(), i), None)))
private def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
val new_index: GlobalIndex[G] = index.return_from_call()
diff --git a/src/rewrite/vct/rewrite/cfg/CFGNode.scala b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
index b6b0ce6974..bb7e3094bf 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGNode.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
@@ -4,5 +4,5 @@ import vct.col.ast.{Expr, Node}
import scala.collection.mutable
-case class CFGNode[G](ast_node: Node[G], successors: mutable.Set[CFGNode[G]]) // TODO: Add condition information to edges
+case class CFGNode[G](ast_node: Node[G], successors: mutable.Set[CFGEdge[G]])
case class CFGEdge[G](target: CFGNode[G], condition: Option[Expr[G]])
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index cdc03231a9..5fae5ef9cb 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -22,6 +22,8 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def resolve(): Statement[G] = indices.head.resolve()
+ def has_statement(): Boolean = indices.head.has_statement()
+
def return_from_call(): GlobalIndex[G] = {
// Find innermost subroutine call
val stack: List[Index[G]] = indices.dropWhile {
@@ -56,7 +58,6 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
}
// Find the next statement
// TODO: Does this always return exactly one next step?
- // No, e.g.: another loop in the update statement of a loop
GlobalIndex(stack.tail).make_step().head
}
@@ -92,6 +93,13 @@ sealed trait Index[G] {
* @return The statement at the current index
*/
def resolve(): Statement[G]
+
+ /**
+ * Determines whether the index contains a statement.
+ *
+ * @return true if the index contains at least one statement, false otherwise
+ */
+ def has_statement(): Boolean = true
}
object Index {
@@ -150,10 +158,16 @@ case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) exte
override def resolve(): Statement[G] = statement
}
-// TODO: Is this really a good idea?
case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = ???
- override def resolve(): Statement[G] = ???
+ override def make_step(): Set[Option[Index[G]]] = {
+ if (index < 2) Set(Some(AssignmentIndex(assign, index + 1)))
+ else Set(None)
+ }
+ override def resolve(): Statement[G] = index match {
+ case 0 => Eval(assign.target)(assign.target.o)
+ case 1 => Eval(assign.value)(assign.value.o)
+ case 2 => assign
+ }
}
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
@@ -215,6 +229,7 @@ case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement
else Set(None)
}
override def resolve(): Statement[G] = subexpressions.apply(index)
+ override def has_statement(): Boolean = subexpressions.nonEmpty
}
object EvalIndex {
def apply[G](eval: Eval[G], index: Int): EvalIndex[G] = new EvalIndex(eval, index)
@@ -286,6 +301,7 @@ case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
}
+// TODO: Switch cases could be multiple context indices deep; this does not work with the single index for make_step()
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = switch.body
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index e7c78340e4..c2041028fc 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -29,12 +29,12 @@ object Utils {
case _ => expr.subnodes.collect{ case ex: Expr[G] => ex }.flatMap(e => find_all_subexpressions(e))
}
- private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = Local(new DirectRef(new Variable(TClass(cls))(o)))(o)
+ private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = Local(new DirectRef[G, Variable[G]](new Variable(TClass(cls))(o)))(o)
def find_all_cases[G](body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = body match {
// Recursion on statements that can contain case statements
case Label(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
- case Block(stmts) => mutable.Set(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
+ case Block(stmts) => mutable.LinkedHashSet.from(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
case Scope(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
// Recursion end
case c: SwitchCase[G] => mutable.Set((c, index))
From c4a7755e3178e6076f4d5172d5e225a79327929e Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 14 Feb 2024 13:05:25 +0100
Subject: [PATCH 14/85] Syntax fixes, project compiles
---
.../vct/rewrite/cfg/CFGGenerator.scala | 2 +-
src/rewrite/vct/rewrite/cfg/Index.scala | 87 ++++++++++---------
2 files changed, 48 insertions(+), 41 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 98e359d0a1..3fe85b5c68 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -197,7 +197,7 @@ case class CFGGenerator[G]() {
}
private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] =
- mutable.LinkedHashSet.from(index.make_step().map(i => CFGEdge(convert(i.resolve(), i), None)))
+ index.make_step().map(i => CFGEdge(convert(i.resolve(), i), None))
private def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
val new_index: GlobalIndex[G] = index.return_from_call()
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 5fae5ef9cb..a7ae3ef03c 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -2,19 +2,21 @@ package vct.rewrite.cfg
import vct.col.ast._
+import scala.collection.mutable
+
case class GlobalIndex[G](indices: List[Index[G]]) {
def enter_scope(node: Node[G], index: Int = 0): GlobalIndex[G] =
GlobalIndex(Index[G](node, index) :: indices)
- def make_step(): Set[GlobalIndex[G]] = {
- if (indices.isEmpty) return Set[GlobalIndex[G]]()
+ def make_step(): mutable.Set[GlobalIndex[G]] = {
+ if (indices.isEmpty) return mutable.Set[GlobalIndex[G]]()
val steps: Set[Option[Index[G]]] = indices.head.make_step()
- var res = Set[GlobalIndex[G]]()
+ val res = mutable.Set[GlobalIndex[G]]()
for (step <- steps) {
step match {
- case Some(index) => res = res ++ GlobalIndex(index +: indices.tail)
- case None => res = res ++ GlobalIndex(indices.tail).make_step()
+ case Some(index) => res.addOne(GlobalIndex(index +: indices.tail))
+ case None => res.addAll(GlobalIndex(indices.tail).make_step())
}
}
res
@@ -103,41 +105,46 @@ sealed trait Index[G] {
}
object Index {
- def apply[G](instance_method: InstanceMethod[G], index: Int): Index[G] = InitialIndex(instance_method)
- def apply[G](run_method: RunMethod[G], index: Int): Index[G] = RunMethodIndex(run_method)
- def apply[G](pvl_branch: PVLBranch[G], index: Int): Index[G] = PVLBranchIndex(pvl_branch, index)
- def apply[G](pvl_loop: PVLLoop[G], index: Int): Index[G] = PVLLoopIndex(pvl_loop, index)
- def apply[G](label: Label[G], index: Int): Index[G] = LabelIndex(label)
- def apply[G](framed_proof: FramedProof[G], index: Int): Index[G] = FramedProofIndex(framed_proof, index)
- def apply[G](extract: Extract[G], index: Int): Index[G] = ExtractIndex(extract)
- def apply[G](eval: Eval[G], index: Int): Index[G] = EvalIndex(eval, index)
- def apply[G](invoke_procedure: InvokeProcedure[G], index: Int): Index[G] = InvokeProcedureIndex(invoke_procedure, index)
- def apply[G](invoke_constructor: InvokeConstructor[G], index: Int): Index[G] = InvokeConstructorIndex(invoke_constructor, index)
- def apply[G](invoke_method: InvokeMethod[G], index: Int): Index[G] = InvokeMethodIndex(invoke_method, index)
- def apply[G](block: Block[G], index: Int): Index[G] = BlockIndex(block, index)
- def apply[G](scope: Scope[G], index: Int): Index[G] = ScopeIndex(scope)
- def apply[G](branch: Branch[G], index: Int): Index[G] = BranchIndex(branch, index)
- def apply[G](indet_branch: IndetBranch[G], index: Int): Index[G] = IndetBranchIndex(indet_branch, index)
- def apply[G](switch: Switch[G], index: Int): Index[G] = SwitchIndex(switch)
- def apply[G](loop: Loop[G], index: Int): Index[G] = LoopIndex(loop, index)
- def apply[G](ranged_for: RangedFor[G], index: Int): Index[G] = RangedForIndex(ranged_for)
- def apply[G](try_catch_finally: TryCatchFinally[G], index: Int): Index[G] = TryCatchFinallyIndex(try_catch_finally, index)
- def apply[G](synchronized: Synchronized[G], index: Int): Index[G] = SynchronizedIndex(synchronized)
- def apply[G](par_invariant: ParInvariant[G], index: Int): Index[G] = ParInvariantIndex(par_invariant)
- def apply[G](par_atomic: ParAtomic[G], index: Int): Index[G] = ParAtomicIndex(par_atomic)
- def apply[G](par_barrier: ParBarrier[G], index: Int): Index[G] = ParBarrierIndex(par_barrier)
- def apply[G](vec_block: VecBlock[G], index: Int): Index[G] = VecBlockIndex(vec_block)
- def apply[G](wand_package: WandPackage[G], index: Int): Index[G] = WandPackageIndex(wand_package)
- def apply[G](model_do: ModelDo[G], index: Int): Index[G] = ModelDoIndex(model_do)
- def apply[G](cpp_lifetime_scope: CPPLifetimeScope[G], index: Int): Index[G] = CPPLifetimeScopeIndex(cpp_lifetime_scope)
- def apply[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int): Index[G] = UnresolvedSeqBranchIndex(unresolved_seq_branch, index)
- def apply[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int): Index[G] = UnresolvedSeqLoopIndex(unresolved_seq_loop, index)
- def apply[G](seq_branch: SeqBranch[G], index: Int): Index[G] = SeqBranchIndex(seq_branch, index)
- def apply[G](seq_loop: SeqLoop[G], index: Int): Index[G] = SeqLoopIndex(seq_loop)
- def apply[G](veymont_assign_expression: VeyMontAssignExpression[G], index: Int): Index[G] = VeyMontAssignExpressionIndex(veymont_assign_expression)
- def apply[G](communicatex: CommunicateX[G], index: Int): Index[G] = CommunicateXIndex(communicatex)
- def apply[G](assign: Assign[G], index: Int): Index[G] = AssignmentIndex(assign, index)
- def apply[G](statement: Statement[G], index: Int): Index[G] = ExpressionContainerIndex(statement, index)
+ def from[G](node: Node[G], index: Int): Index[G] = node match {
+ case instance_method: InstanceMethod[G] => InitialIndex(instance_method)
+ case run_method: RunMethod[G] => RunMethodIndex(run_method)
+ case assign: Assign[G] => AssignmentIndex(assign, index)
+ case pvl_branch: PVLBranch[G] => PVLBranchIndex(pvl_branch, index)
+ case pvl_loop: PVLLoop[G] => PVLLoopIndex(pvl_loop, index)
+ case label: Label[G] => LabelIndex(label)
+ case framed_proof: FramedProof[G] => FramedProofIndex(framed_proof, index)
+ case extract: Extract[G] => ExtractIndex(extract)
+ case eval: Eval[G] => EvalIndex(eval, index)
+ case invoke_procedure: InvokeProcedure[G] => InvokeProcedureIndex(invoke_procedure, index)
+ case invoke_constructor: InvokeConstructor[G] => InvokeConstructorIndex(invoke_constructor, index)
+ case invoke_method: InvokeMethod[G] => InvokeMethodIndex(invoke_method, index)
+ case block: Block[G] => BlockIndex(block, index)
+ case scope: Scope[G] => ScopeIndex(scope)
+ case branch: Branch[G] => BranchIndex(branch, index)
+ case indet_branch: IndetBranch[G] => IndetBranchIndex(indet_branch, index)
+ case switch: Switch[G] => SwitchIndex(switch)
+ case loop: Loop[G] => LoopIndex(loop, index)
+ case ranged_for: RangedFor[G] => RangedForIndex(ranged_for)
+ case try_catch_finally: TryCatchFinally[G] => TryCatchFinallyIndex(try_catch_finally, index)
+ case synchronized: Synchronized[G] => SynchronizedIndex(synchronized)
+ case par_invariant: ParInvariant[G] => ParInvariantIndex(par_invariant)
+ case par_atomic: ParAtomic[G] => ParAtomicIndex(par_atomic)
+ case par_barrier: ParBarrier[G] => ParBarrierIndex(par_barrier)
+ case vec_block: VecBlock[G] => VecBlockIndex(vec_block)
+ case wand_package: WandPackage[G] => WandPackageIndex(wand_package)
+ case model_do: ModelDo[G] => ModelDoIndex(model_do)
+ case cpp_lifetime_scope: CPPLifetimeScope[G] => CPPLifetimeScopeIndex(cpp_lifetime_scope)
+ case unresolved_seq_branch: UnresolvedSeqBranch[G] => UnresolvedSeqBranchIndex(unresolved_seq_branch, index)
+ case unresolved_seq_loop: UnresolvedSeqLoop[G] => UnresolvedSeqLoopIndex(unresolved_seq_loop, index)
+ case seq_branch: SeqBranch[G] => SeqBranchIndex(seq_branch, index)
+ case seq_loop: SeqLoop[G] => SeqLoopIndex(seq_loop)
+ case veymont_assign_expression: VeyMontAssignExpression[G] => VeyMontAssignExpressionIndex(veymont_assign_expression)
+ case communicatex: CommunicateX[G] => CommunicateXIndex(communicatex)
+ case statement: Statement[G] => ExpressionContainerIndex(statement, index)
+ case _ => ???
+ }
+
+ def apply[G](node: Node[G], index: Int): Index[G] = from(node, index)
}
case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
From 4bbb2ab84dd000959304098e8cbeba1011bf0fd3 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 14 Feb 2024 14:33:58 +0100
Subject: [PATCH 15/85] Implemented GraphViz/DOT printer for control flow graph
---
src/rewrite/vct/rewrite/cfg/Printer.scala | 50 +++++++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 src/rewrite/vct/rewrite/cfg/Printer.scala
diff --git a/src/rewrite/vct/rewrite/cfg/Printer.scala b/src/rewrite/vct/rewrite/cfg/Printer.scala
new file mode 100644
index 0000000000..4c431125f5
--- /dev/null
+++ b/src/rewrite/vct/rewrite/cfg/Printer.scala
@@ -0,0 +1,50 @@
+package vct.rewrite.cfg
+
+import hre.io.RWFile
+import vct.col.ast.{BooleanValue, Expr, InstanceMethod}
+import vct.col.origin.Origin
+
+import java.io.Writer
+import java.nio.file.Path
+import scala.collection.mutable
+
+case class Printer[G]() {
+ private val node_names: mutable.Map[CFGNode[G], String] = mutable.HashMap[CFGNode[G], String]()
+ private val no_condition: Expr[G] = BooleanValue(value = true)(Origin(Seq()))
+ private var node_index: Int = 0
+
+ private def print_cfg(writer: Writer): Unit = {
+ writer.append("digraph {")
+ // Write all nodes
+ node_names.foreach(naming => writer.append(naming._2)
+ .append(s" [label=${"\""}")
+ .append(naming._1.ast_node.toInlineString)
+ .append(s"${"\""}];\n"))
+ // Write all edges
+ node_names.foreach(naming => naming._1.successors.foreach(edge => writer.append(naming._2)
+ .append(" -> ")
+ .append(node_names(edge.target))
+ .append(s" [label=${"\""}")
+ .append(edge.condition.getOrElse(no_condition).toInlineString)
+ .append(s"${"\""}];\n")))
+ writer.append("}")
+ }
+
+ def print_ast_as_cfg(entry_point: InstanceMethod[G], path: Path): Unit = {
+ val cfg_root: CFGNode[G] = CFGGenerator().generate(entry_point)
+ find_all_nodes(cfg_root)
+ RWFile(path.toFile).write(w => print_cfg(w))
+ }
+
+ private def find_all_nodes(node: CFGNode[G]): Unit = {
+ if (!node_names.contains(node)) {
+ // Give new name to node
+ val node_name: String = s"n$node_index"
+ node_index += 1
+ node_names.addOne((node, node_name))
+
+ // Recursively call for successors
+ node.successors.foreach(e => find_all_nodes(e.target))
+ }
+ }
+}
From c89a7286331967036c78e6b4f2422b48beb775de Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 14 Feb 2024 15:42:47 +0100
Subject: [PATCH 16/85] Started work on frontend support for CFG conversion
---
src/main/vct/main/Main.scala | 4 +++-
src/main/vct/main/stages/Stages.scala | 5 +++++
src/main/vct/options/Options.scala | 13 +++++++++++++
src/main/vct/options/types/Mode.scala | 1 +
src/rewrite/vct/rewrite/cfg/Printer.scala | 2 +-
5 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/src/main/vct/main/Main.scala b/src/main/vct/main/Main.scala
index 484800b44d..2f48e7f4e9 100644
--- a/src/main/vct/main/Main.scala
+++ b/src/main/vct/main/Main.scala
@@ -94,9 +94,11 @@ case object Main extends LazyLogging {
}
EXIT_CODE_SUCCESS
case Mode.VeyMont => VeyMont.runOptions(options)
- case Mode.VeSUV =>
+ case Mode.VeSUV => {
logger.info("Starting transformation")
Transform.runOptions(options)
+ }
+ case Mode.CFG =>
case Mode.BatchTest => ???
}
} finally {
diff --git a/src/main/vct/main/stages/Stages.scala b/src/main/vct/main/stages/Stages.scala
index 2be2b1151a..4f8e58fe39 100644
--- a/src/main/vct/main/stages/Stages.scala
+++ b/src/main/vct/main/stages/Stages.scala
@@ -70,4 +70,9 @@ case object Stages {
Parsing.ofOptions(options, blameProvider)
.thenRun(Output.vesuvOfOptions(options))
}
+
+ def cfgTransformationOfOptions(options: Options, blameProvider: BlameProvider): Stages[Seq[Readable], Unit] = {
+ Parsing.ofOptions(options, blameProvider)
+ .thenRun()
+ }
}
\ No newline at end of file
diff --git a/src/main/vct/options/Options.scala b/src/main/vct/options/Options.scala
index 6d55980cc6..287369c192 100644
--- a/src/main/vct/options/Options.scala
+++ b/src/main/vct/options/Options.scala
@@ -268,6 +268,16 @@ case object Options {
.action((path, c) => c.copy(vesuvOutput = path))
),
+ note(""),
+ note("Control flow graph"),
+ opt[Unit]("build-cfg")
+ .action((_, c) => c.copy(mode = Mode.CFG))
+ .text("Instead of verifying a program, build its control flow graph for further analysis")
+ .children(
+ opt[Path]("cfg-output").required().valueName("")
+ .action((path, c) => c.copy(cfgOutput = path))
+ ),
+
note(""),
note("Batch Testing Mode"),
opt[Unit]("test")
@@ -403,6 +413,9 @@ case class Options
// VeSUV options
vesuvOutput: Path = null,
+ // Control flow graph options
+ cfgOutput: Path = null,
+
// Batch test options
testDir: Path = null, // required
testFilterBackend: Option[Seq[Backend]] = None,
diff --git a/src/main/vct/options/types/Mode.scala b/src/main/vct/options/types/Mode.scala
index f7c07eb730..addf6b3c88 100644
--- a/src/main/vct/options/types/Mode.scala
+++ b/src/main/vct/options/types/Mode.scala
@@ -8,5 +8,6 @@ case object Mode {
case object Verify extends Mode
case object VeyMont extends Mode
case object VeSUV extends Mode
+ case object CFG extends Mode
case object BatchTest extends Mode
}
diff --git a/src/rewrite/vct/rewrite/cfg/Printer.scala b/src/rewrite/vct/rewrite/cfg/Printer.scala
index 4c431125f5..2053b8cbfb 100644
--- a/src/rewrite/vct/rewrite/cfg/Printer.scala
+++ b/src/rewrite/vct/rewrite/cfg/Printer.scala
@@ -14,7 +14,7 @@ case class Printer[G]() {
private var node_index: Int = 0
private def print_cfg(writer: Writer): Unit = {
- writer.append("digraph {")
+ writer.append("digraph {\n")
// Write all nodes
node_names.foreach(naming => writer.append(naming._2)
.append(s" [label=${"\""}")
From 1f994f66a1db97af2ef3d0c9690076aedc118b61 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 14 Feb 2024 16:29:05 +0100
Subject: [PATCH 17/85] Fixed implementation of subroutine invocations
---
src/main/vct/main/Main.scala | 2 +-
src/main/vct/main/stages/Stages.scala | 4 +-
.../vct/rewrite/cfg/CFGGenerator.scala | 22 +--
src/rewrite/vct/rewrite/cfg/Index.scala | 128 +++++++++++++++---
4 files changed, 118 insertions(+), 38 deletions(-)
diff --git a/src/main/vct/main/Main.scala b/src/main/vct/main/Main.scala
index 2f48e7f4e9..7bdb2f7d79 100644
--- a/src/main/vct/main/Main.scala
+++ b/src/main/vct/main/Main.scala
@@ -98,7 +98,7 @@ case object Main extends LazyLogging {
logger.info("Starting transformation")
Transform.runOptions(options)
}
- case Mode.CFG =>
+ case Mode.CFG => ???
case Mode.BatchTest => ???
}
} finally {
diff --git a/src/main/vct/main/stages/Stages.scala b/src/main/vct/main/stages/Stages.scala
index 4f8e58fe39..4f5714d4a8 100644
--- a/src/main/vct/main/stages/Stages.scala
+++ b/src/main/vct/main/stages/Stages.scala
@@ -71,8 +71,8 @@ case object Stages {
.thenRun(Output.vesuvOfOptions(options))
}
- def cfgTransformationOfOptions(options: Options, blameProvider: BlameProvider): Stages[Seq[Readable], Unit] = {
+ /*def cfgTransformationOfOptions(options: Options, blameProvider: BlameProvider): Stages[Seq[Readable], Unit] = {
Parsing.ofOptions(options, blameProvider)
.thenRun()
- }
+ }*/
}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 3fe85b5c68..c727ee1deb 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -2,7 +2,6 @@ package vct.rewrite.cfg
import vct.col.ast._
-import scala.collection.IterableOnce.iterableOnceExtensionMethods
import scala.collection.mutable
case class CFGGenerator[G]() {
@@ -11,7 +10,7 @@ case class CFGGenerator[G]() {
private val converted_nodes: mutable.Map[GlobalIndex[G], CFGNode[G]] = mutable.HashMap[GlobalIndex[G], CFGNode[G]]()
def generate(entry: InstanceMethod[G]): CFGNode[G] = {
- convert(entry.body.get, GlobalIndex[G](List(InitialIndex(entry))))
+ convert(entry.body.get, GlobalIndex[G](mutable.Seq(InitialIndex(entry))))
}
private def convert(node: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
@@ -98,22 +97,9 @@ case class CFGGenerator[G]() {
case None => mutable.Set(CFGEdge(continue_successor(context), None))
}
// InvocationStatement
- // TODO: Handle all the other possible expressions in invocations!
- case InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields) => {
- if (args.nonEmpty) mutable.Set(CFGEdge(convert(Eval(args.head)(args.head.o), context.enter_scope(node)), None))
- else if (ref.decl.body.nonEmpty) mutable.Set(CFGEdge(convert(ref.decl.body.get, context.enter_scope(node)), None))
- else sequential_successor(context)
- }
- case InvokeConstructor(ref, out, args, outArgs, typeArgs, givenMap, yields) => {
- if (args.nonEmpty) mutable.Set(CFGEdge(convert(Eval(args.head)(args.head.o), context.enter_scope(node)), None))
- else if (ref.decl.body.nonEmpty) mutable.Set(CFGEdge(convert(ref.decl.body.get, context.enter_scope(node)), None))
- else sequential_successor(context)
- }
- case InvokeMethod(obj, ref, args, outArgs, typeArgs, givenMap, yields) => {
- if (args.nonEmpty) mutable.Set(CFGEdge(convert(Eval(args.head)(args.head.o), context.enter_scope(node)), None))
- else if (ref.decl.body.nonEmpty) mutable.Set(CFGEdge(convert(ref.decl.body.get, context.enter_scope(node)), None))
- else sequential_successor(context)
- }
+ case InvokeProcedure(_, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
+ case InvokeConstructor(_, _, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
+ case InvokeMethod(_, _, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
// CompositeStatement
case Block(statements) =>
mutable.Set(CFGEdge(convert(statements.head, context.enter_scope(node)), None))
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index a7ae3ef03c..62c1b2c837 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -1,13 +1,14 @@
package vct.rewrite.cfg
import vct.col.ast._
+import vct.col.ref.Ref
import scala.collection.mutable
-case class GlobalIndex[G](indices: List[Index[G]]) {
+case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
def enter_scope(node: Node[G], index: Int = 0): GlobalIndex[G] =
- GlobalIndex(Index[G](node, index) :: indices)
+ GlobalIndex(indices.prepended(Index[G](node, index)))
def make_step(): mutable.Set[GlobalIndex[G]] = {
if (indices.isEmpty) return mutable.Set[GlobalIndex[G]]()
@@ -22,13 +23,13 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
res
}
- def resolve(): Statement[G] = indices.head.resolve()
+ def resolve(): Statement[G] = indices.dropWhile(i => !i.has_statement()).head.resolve()
def has_statement(): Boolean = indices.head.has_statement()
def return_from_call(): GlobalIndex[G] = {
// Find innermost subroutine call
- val stack: List[Index[G]] = indices.dropWhile {
+ val stack: mutable.Seq[Index[G]] = indices.dropWhile {
case InvokeProcedureIndex(_, _) | InvokeMethodIndex(_, _) => false
case _ => true
}
@@ -39,7 +40,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def handle_exception(e: Expr[G]): GlobalIndex[G] = {
// Find innermost try-catch block of appropriate type
- val stack: List[Index[G]] = indices.dropWhile {
+ val stack: mutable.Seq[Index[G]] = indices.dropWhile {
case TryCatchFinallyIndex(stmt, 0) => !stmt.catches.exists(c => c.decl.t.equals(e.t))
case _ => true
}
@@ -53,7 +54,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def handle_break(): GlobalIndex[G] = {
// Find innermost occurrence of either a loop or a switch statement
- val stack: List[Index[G]] = indices.dropWhile {
+ val stack: mutable.Seq[Index[G]] = indices.dropWhile {
case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true // If some godless heathen calls break in the init of a loop, don't consider that loop
case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) | SwitchIndex(_) => false
case _ => true
@@ -65,7 +66,7 @@ case class GlobalIndex[G](indices: List[Index[G]]) {
def continue_innermost_loop(): GlobalIndex[G] = {
// Find innermost loop that could be the target of continue
- val stack: List[Index[G]] = indices.dropWhile {
+ val stack: mutable.Seq[Index[G]] = indices.dropWhile {
case PVLLoopIndex(_, 0) | LoopIndex(_, 0) => true
case PVLLoopIndex(_, _) | LoopIndex(_, _) | RangedForIndex(_) | UnresolvedSeqLoopIndex(_, _) | SeqLoopIndex(_) => false
case _ => true
@@ -243,35 +244,128 @@ object EvalIndex {
}
case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index: Int) extends Index[G] {
+ // Order of operations:
+ // 1. args
+ // 2. given
+ // 3. outArgs
+ // 4. yields
+ // 5. procedure body
override def make_step(): Set[Option[Index[G]]] = {
- if (index < invoke_procedure.args.size) Set(Some(InvokeProcedureIndex(invoke_procedure, index + 1)))
+ val args: Seq[Expr[G]] = invoke_procedure.args
+ val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_procedure.givenMap
+ val outArgs: Seq[Expr[G]] = invoke_procedure.outArgs
+ val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_procedure.yields
+ if (index < args.size + givenMap.size + outArgs.size + yields.size - 1 ||
+ index == args.size + givenMap.size + outArgs.size + yields.size - 1 && invoke_procedure.ref.decl.body.nonEmpty)
+ Set(Some(InvokeProcedureIndex(invoke_procedure, index + 1)))
else Set(None)
}
override def resolve(): Statement[G] = {
- if (index < invoke_procedure.args.size) Eval(invoke_procedure.args.apply(index))(invoke_procedure.args.apply(index).o)
- else invoke_procedure.ref.decl.body.get
- }
+ val args: Seq[Expr[G]] = invoke_procedure.args
+ val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_procedure.givenMap
+ val outArgs: Seq[Expr[G]] = invoke_procedure.outArgs
+ val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_procedure.yields
+ if (index < args.size) {
+ val expr: Expr[G] = args.apply(index)
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size) {
+ val expr: Expr[G] = givenMap.apply(index - args.size)._2
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + outArgs.size) {
+ val expr: Expr[G] = outArgs.apply(index - args.size - givenMap.size)
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + outArgs.size + yields.size) {
+ val expr: Expr[G] = yields.apply(index - args.size - givenMap.size - outArgs.size)._1
+ Eval(expr)(expr.o)
+ } else invoke_procedure.ref.decl.body.get
+ }
+ override def has_statement(): Boolean =
+ invoke_procedure.args.nonEmpty ||
+ invoke_procedure.givenMap.nonEmpty ||
+ invoke_procedure.outArgs.nonEmpty ||
+ invoke_procedure.yields.nonEmpty ||
+ invoke_procedure.ref.decl.body.nonEmpty
}
case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], index: Int) extends Index[G] {
+ // Order of operations:
+ // 1. args
+ // 2. given
+ // 3. outArgs
+ // 4. yields
+ // 5. out
+ // 6. constructor body
override def make_step(): Set[Option[Index[G]]] = {
- if (index < invoke_constructor.args.size) Set(Some(InvokeConstructorIndex(invoke_constructor, index + 1)))
+ val args: Seq[Expr[G]] = invoke_constructor.args
+ val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_constructor.givenMap
+ val outArgs: Seq[Expr[G]] = invoke_constructor.outArgs
+ val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_constructor.yields
+ if (index < args.size + givenMap.size + outArgs.size + yields.size ||
+ index == args.size + givenMap.size + outArgs.size + yields.size && invoke_constructor.ref.decl.body.nonEmpty)
+ Set(Some(InvokeConstructorIndex(invoke_constructor, index + 1)))
else Set(None)
}
override def resolve(): Statement[G] = {
- if (index < invoke_constructor.args.size) Eval(invoke_constructor.args.apply(index))(invoke_constructor.args.apply(index).o)
- else invoke_constructor.ref.decl.body.get
+ val args: Seq[Expr[G]] = invoke_constructor.args
+ val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_constructor.givenMap
+ val outArgs: Seq[Expr[G]] = invoke_constructor.outArgs
+ val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_constructor.yields
+ if (index < args.size) {
+ val expr: Expr[G] = args.apply(index)
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size) {
+ val expr: Expr[G] = givenMap.apply(index - args.size)._2
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + outArgs.size) {
+ val expr: Expr[G] = outArgs.apply(index - args.size - givenMap.size)
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + outArgs.size + yields.size) {
+ val expr: Expr[G] = yields.apply(index - args.size - givenMap.size - outArgs.size)._1
+ Eval(expr)(expr.o)
+ } else if (index == args.size + givenMap.size + outArgs.size + yields.size) {
+ Eval(invoke_constructor.out)(invoke_constructor.out.o)
+ } else invoke_constructor.ref.decl.body.get
}
}
case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) extends Index[G] {
+ // Order of operations:
+ // 1. obj
+ // 2. args
+ // 3. given
+ // 4. outArgs
+ // 5. yields
+ // 6. method body
override def make_step(): Set[Option[Index[G]]] = {
- if (index < invoke_method.args.size) Set(Some(InvokeMethodIndex(invoke_method, index + 1)))
+ val args: Seq[Expr[G]] = invoke_method.args
+ val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_method.givenMap
+ val outArgs: Seq[Expr[G]] = invoke_method.outArgs
+ val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_method.yields
+ if (index < args.size + givenMap.size + outArgs.size + yields.size ||
+ index == args.size + givenMap.size + outArgs.size + yields.size && invoke_method.ref.decl.body.nonEmpty)
+ Set(Some(InvokeMethodIndex(invoke_method, index + 1)))
else Set(None)
}
override def resolve(): Statement[G] = {
- if (index < invoke_method.args.size) Eval(invoke_method.args.apply(index))(invoke_method.args.apply(index).o)
- else invoke_method.ref.decl.body.get
+ val args: Seq[Expr[G]] = invoke_method.args
+ val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_method.givenMap
+ val outArgs: Seq[Expr[G]] = invoke_method.outArgs
+ val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_method.yields
+ if (index == 0) {
+ Eval(invoke_method.obj)(invoke_method.obj.o)
+ } else if (index < args.size + 1) {
+ val expr: Expr[G] = args.apply(index - 1)
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + 1) {
+ val expr: Expr[G] = givenMap.apply(index - args.size - 1)._2
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + outArgs.size + 1) {
+ val expr: Expr[G] = outArgs.apply(index - args.size - givenMap.size - 1)
+ Eval(expr)(expr.o)
+ } else if (index < args.size + givenMap.size + outArgs.size + yields.size + 1) {
+ val expr: Expr[G] = yields.apply(index - args.size - givenMap.size - outArgs.size - 1)._1
+ Eval(expr)(expr.o)
+ } else invoke_method.ref.decl.body.get
}
}
From f9672e0a2c1ef53e6c46517cdddf7303fe3f43e3 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 15 Feb 2024 09:33:19 +0100
Subject: [PATCH 18/85] Renamed VeSUV pass to avoid confusion
---
src/main/vct/main/Main.scala | 6 ++----
src/main/vct/main/modes/{Transform.scala => VeSUV.scala} | 2 +-
2 files changed, 3 insertions(+), 5 deletions(-)
rename src/main/vct/main/modes/{Transform.scala => VeSUV.scala} (93%)
diff --git a/src/main/vct/main/Main.scala b/src/main/vct/main/Main.scala
index 7bdb2f7d79..a296fc497c 100644
--- a/src/main/vct/main/Main.scala
+++ b/src/main/vct/main/Main.scala
@@ -7,9 +7,7 @@ import hre.progress.Progress
import org.slf4j.LoggerFactory
import scopt.OParser
import vct.col.ast.Node
-import vct.main.modes.Verify
-import vct.main.modes.VeyMont
-import vct.main.modes.Transform
+import vct.main.modes.{VeSUV, Verify, VeyMont}
import vct.main.stages.Transformation
import vct.options.types.{Mode, Verbosity}
import vct.options.Options
@@ -96,7 +94,7 @@ case object Main extends LazyLogging {
case Mode.VeyMont => VeyMont.runOptions(options)
case Mode.VeSUV => {
logger.info("Starting transformation")
- Transform.runOptions(options)
+ VeSUV.runOptions(options)
}
case Mode.CFG => ???
case Mode.BatchTest => ???
diff --git a/src/main/vct/main/modes/Transform.scala b/src/main/vct/main/modes/VeSUV.scala
similarity index 93%
rename from src/main/vct/main/modes/Transform.scala
rename to src/main/vct/main/modes/VeSUV.scala
index 4630934795..ece778b0df 100644
--- a/src/main/vct/main/modes/Transform.scala
+++ b/src/main/vct/main/modes/VeSUV.scala
@@ -7,7 +7,7 @@ import vct.main.stages.Stages
import vct.options.Options
import vct.parsers.transform.ConstantBlameProvider
-case object Transform extends LazyLogging {
+case object VeSUV extends LazyLogging {
def runOptions(options: Options) : Int = {
val collector = BlameCollector()
val stages = Stages.vesuvOfOptions(options, ConstantBlameProvider(collector))
From 77e242a7aaaf8c1be592dd45c36a19fd62c569af Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 15 Feb 2024 10:27:26 +0100
Subject: [PATCH 19/85] Connected frontend to backend for control flow graph
printer
---
src/main/vct/main/Main.scala | 7 +++--
src/main/vct/main/modes/CFG.scala | 21 ++++++++++++++
src/main/vct/main/stages/PrintCFG.scala | 29 +++++++++++++++++++
src/main/vct/main/stages/Stages.scala | 7 +++--
.../cfg/{Printer.scala => CFGPrinter.scala} | 2 +-
5 files changed, 60 insertions(+), 6 deletions(-)
create mode 100644 src/main/vct/main/modes/CFG.scala
create mode 100644 src/main/vct/main/stages/PrintCFG.scala
rename src/rewrite/vct/rewrite/cfg/{Printer.scala => CFGPrinter.scala} (98%)
diff --git a/src/main/vct/main/Main.scala b/src/main/vct/main/Main.scala
index a296fc497c..9ea260cafb 100644
--- a/src/main/vct/main/Main.scala
+++ b/src/main/vct/main/Main.scala
@@ -7,7 +7,7 @@ import hre.progress.Progress
import org.slf4j.LoggerFactory
import scopt.OParser
import vct.col.ast.Node
-import vct.main.modes.{VeSUV, Verify, VeyMont}
+import vct.main.modes.{CFG, VeSUV, Verify, VeyMont}
import vct.main.stages.Transformation
import vct.options.types.{Mode, Verbosity}
import vct.options.Options
@@ -96,7 +96,10 @@ case object Main extends LazyLogging {
logger.info("Starting transformation")
VeSUV.runOptions(options)
}
- case Mode.CFG => ???
+ case Mode.CFG => {
+ logger.info("Starting control flow graph transformation")
+ CFG.runOptions(options)
+ }
case Mode.BatchTest => ???
}
} finally {
diff --git a/src/main/vct/main/modes/CFG.scala b/src/main/vct/main/modes/CFG.scala
new file mode 100644
index 0000000000..9fc7cf7eee
--- /dev/null
+++ b/src/main/vct/main/modes/CFG.scala
@@ -0,0 +1,21 @@
+package vct.main.modes
+
+import com.typesafe.scalalogging.LazyLogging
+import vct.col.origin.BlameCollector
+import vct.main.Main.{EXIT_CODE_ERROR, EXIT_CODE_SUCCESS}
+import vct.main.stages.Stages
+import vct.options.Options
+import vct.parsers.transform.ConstantBlameProvider
+
+case object CFG extends LazyLogging {
+ def runOptions(options: Options) : Int = {
+ val collector = BlameCollector()
+ val stages = Stages.cfgTransformationOfOptions(options, ConstantBlameProvider(collector))
+ stages.run(options.inputs) match {
+ case Left(_) => EXIT_CODE_ERROR
+ case Right(()) =>
+ logger.info("Transformation complete")
+ EXIT_CODE_SUCCESS
+ }
+ }
+}
diff --git a/src/main/vct/main/stages/PrintCFG.scala b/src/main/vct/main/stages/PrintCFG.scala
new file mode 100644
index 0000000000..2688fb1fc5
--- /dev/null
+++ b/src/main/vct/main/stages/PrintCFG.scala
@@ -0,0 +1,29 @@
+package vct.main.stages
+
+import hre.stages.Stage
+import sourcecode.Name
+import vct.col.ast.{InstanceMethod, Node}
+import vct.col.rewrite.Generation
+import vct.options.Options
+import vct.rewrite.cfg.CFGPrinter
+
+import java.nio.file.Path
+
+case object PrintCFG {
+ def ofOptions(options: Options): Stage[Node[_ <: Generation], Unit] = {
+ PrintCFG(options.cfgOutput)
+ }
+}
+
+case class PrintCFG(out: Path) extends Stage[Node[_ <: Generation], Unit] {
+
+ override def friendlyName: String = "Printing control flow graph"
+
+ override def progressWeight: Int = 0
+
+ override def run(in1: Node[_ <: Generation]): Unit = {
+ // TODO: Burn this and do it properly
+ val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] if m.o.getPreferredName.getOrElse(Name()).toString.equals("main") => m }.get
+ CFGPrinter().print_ast_as_cfg(main_method, out)
+ }
+}
diff --git a/src/main/vct/main/stages/Stages.scala b/src/main/vct/main/stages/Stages.scala
index 4f5714d4a8..78cab0cb4a 100644
--- a/src/main/vct/main/stages/Stages.scala
+++ b/src/main/vct/main/stages/Stages.scala
@@ -71,8 +71,9 @@ case object Stages {
.thenRun(Output.vesuvOfOptions(options))
}
- /*def cfgTransformationOfOptions(options: Options, blameProvider: BlameProvider): Stages[Seq[Readable], Unit] = {
+ def cfgTransformationOfOptions(options: Options, blameProvider: BlameProvider): Stages[Seq[Readable], Unit] = {
Parsing.ofOptions(options, blameProvider)
- .thenRun()
- }*/
+ .thenRun(Resolution.ofOptions(options, blameProvider))
+ .thenRun(PrintCFG.ofOptions(options))
+ }
}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Printer.scala b/src/rewrite/vct/rewrite/cfg/CFGPrinter.scala
similarity index 98%
rename from src/rewrite/vct/rewrite/cfg/Printer.scala
rename to src/rewrite/vct/rewrite/cfg/CFGPrinter.scala
index 2053b8cbfb..3a11ea2817 100644
--- a/src/rewrite/vct/rewrite/cfg/Printer.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGPrinter.scala
@@ -8,7 +8,7 @@ import java.io.Writer
import java.nio.file.Path
import scala.collection.mutable
-case class Printer[G]() {
+case class CFGPrinter[G]() {
private val node_names: mutable.Map[CFGNode[G], String] = mutable.HashMap[CFGNode[G], String]()
private val no_condition: Expr[G] = BooleanValue(value = true)(Origin(Seq()))
private var node_index: Int = 0
From 817d06760dc45595491e29f6ef2616505434df00 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 15 Feb 2024 11:32:39 +0100
Subject: [PATCH 20/85] Fixed small errors preventing execution
---
src/main/vct/main/stages/PrintCFG.scala | 3 ++-
src/rewrite/vct/rewrite/cfg/CFGGenerator.scala | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/main/vct/main/stages/PrintCFG.scala b/src/main/vct/main/stages/PrintCFG.scala
index 2688fb1fc5..3ed85f5d50 100644
--- a/src/main/vct/main/stages/PrintCFG.scala
+++ b/src/main/vct/main/stages/PrintCFG.scala
@@ -23,7 +23,8 @@ case class PrintCFG(out: Path) extends Stage[Node[_ <: Generation], Unit] {
override def run(in1: Node[_ <: Generation]): Unit = {
// TODO: Burn this and do it properly
- val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] if m.o.getPreferredName.getOrElse(Name()).toString.equals("main") => m }.get
+ // if m.o.getPreferredName.getOrElse(Name()).toString.equals("main")
+ val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] => m }.get
CFGPrinter().print_ast_as_cfg(main_method, out)
}
}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index c727ee1deb..1dd2af74d8 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -31,6 +31,7 @@ case class CFGGenerator[G]() {
if (searched_labels.contains(goto.lbl.decl)) searched_labels(goto.lbl.decl).addOne(cfg_node)
else searched_labels.addOne((goto.lbl.decl, mutable.Set(cfg_node)))
}
+ case _ =>
}
converted_nodes.addOne((context, cfg_node))
cfg_node
From dc5d57ba71c3d4a3b2679221595842d6d2f1e809 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 15 Feb 2024 13:48:52 +0100
Subject: [PATCH 21/85] Fixed an infinite recursion when referring back to
incompletely transformed nodes
---
.../vct/rewrite/cfg/CFGGenerator.scala | 5 +-
src/rewrite/vct/rewrite/cfg/Index.scala | 146 +++++++++++++++++-
2 files changed, 148 insertions(+), 3 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 1dd2af74d8..12f8da0efc 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -17,7 +17,9 @@ case class CFGGenerator[G]() {
// If a node has already been visited, then it should not be created again
if (converted_nodes.contains(context)) return converted_nodes(context)
// Create new node with its successors (if possible)
- val cfg_node: CFGNode[G] = CFGNode(node, find_successors(node, context))
+ val cfg_node: CFGNode[G] = CFGNode(node, mutable.Set())
+ converted_nodes.addOne((context, cfg_node))
+ cfg_node.successors.addAll(find_successors(node, context))
// Handle labels and goto statements
node match {
case label: Label[G] => {
@@ -33,7 +35,6 @@ case class CFGGenerator[G]() {
}
case _ =>
}
- converted_nodes.addOne((context, cfg_node))
cfg_node
}
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 62c1b2c837..56bce55266 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -7,6 +7,11 @@ import scala.collection.mutable
case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case GlobalIndex(other_indices) => indices.size == other_indices.size && indices.zip(other_indices).forall(t => t._1.equals(t._2))
+ case _ => false
+ }
+
def enter_scope(node: Node[G], index: Int = 0): GlobalIndex[G] =
GlobalIndex(indices.prepended(Index[G](node, index)))
@@ -142,7 +147,6 @@ object Index {
case veymont_assign_expression: VeyMontAssignExpression[G] => VeyMontAssignExpressionIndex(veymont_assign_expression)
case communicatex: CommunicateX[G] => CommunicateXIndex(communicatex)
case statement: Statement[G] => ExpressionContainerIndex(statement, index)
- case _ => ???
}
def apply[G](node: Node[G], index: Int): Index[G] = from(node, index)
@@ -151,11 +155,19 @@ object Index {
case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = instance_method.body.get
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case InitialIndex(m) => m.equals(instance_method)
+ case _ => false
+ }
}
case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = run_method.body.get
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case RunMethodIndex(m) => m.equals(run_method)
+ case _ => false
+ }
}
case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) extends Index[G] {
@@ -164,6 +176,10 @@ case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) exte
else Set(None)
}
override def resolve(): Statement[G] = statement
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ExpressionContainerIndex(s, i) => i == index && s.equals(statement)
+ case _ => false
+ }
}
case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
@@ -176,6 +192,10 @@ case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
case 1 => Eval(assign.value)(assign.value.o)
case 2 => assign
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case AssignmentIndex(a, i) => i == index && a.equals(assign)
+ case _ => false
+ }
}
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
@@ -191,6 +211,10 @@ case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index
if (index % 2 == 0) Eval(pvl_branch.branches.apply(index / 2)._1)(pvl_branch.branches.apply(index / 2)._1.o)
else pvl_branch.branches.apply((index - 1) / 2)._2
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case PVLBranchIndex(b, i) => i == index && b.equals(pvl_branch)
+ case _ => false
+ }
}
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
@@ -206,11 +230,19 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
case 2 => pvl_loop.body
case 3 => pvl_loop.update
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case PVLLoopIndex(l, i) => i == index && l.equals(pvl_loop)
+ case _ => false
+ }
}
case class LabelIndex[G](label: Label[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = label.stat
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case LabelIndex(l) => l.equals(label)
+ case _ => false
+ }
}
case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends Index[G] {
@@ -223,11 +255,19 @@ case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends
case 1 => Eval(framed_proof.post)(framed_proof.post.o)
case 2 => framed_proof.body
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case FramedProofIndex(p, i) => i == index && p.equals(framed_proof)
+ case _ => false
+ }
}
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = extract.contractedStatement
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ExtractIndex(e) => e.equals(extract)
+ case _ => false
+ }
}
case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement[G]]) extends Index[G] {
@@ -238,6 +278,10 @@ case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement
}
override def resolve(): Statement[G] = subexpressions.apply(index)
override def has_statement(): Boolean = subexpressions.nonEmpty
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case EvalIndex(e, i, _) => i == index && e.equals(eval)
+ case _ => false
+ }
}
object EvalIndex {
def apply[G](eval: Eval[G], index: Int): EvalIndex[G] = new EvalIndex(eval, index)
@@ -285,6 +329,10 @@ case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index:
invoke_procedure.outArgs.nonEmpty ||
invoke_procedure.yields.nonEmpty ||
invoke_procedure.ref.decl.body.nonEmpty
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case InvokeProcedureIndex(p, i) => i == index && p.equals(invoke_procedure)
+ case _ => false
+ }
}
case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], index: Int) extends Index[G] {
@@ -326,6 +374,10 @@ case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], i
Eval(invoke_constructor.out)(invoke_constructor.out.o)
} else invoke_constructor.ref.decl.body.get
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case InvokeConstructorIndex(c, i) => i == index && c.equals(invoke_constructor)
+ case _ => false
+ }
}
case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) extends Index[G] {
@@ -367,6 +419,10 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
Eval(expr)(expr.o)
} else invoke_method.ref.decl.body.get
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case InvokeMethodIndex(m, i) => i == index && m.equals(invoke_method)
+ case _ => false
+ }
}
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
@@ -375,11 +431,19 @@ case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
else Set(None)
}
override def resolve(): Statement[G] = block.statements.apply(index)
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case BlockIndex(b, i) => i == index && b.equals(block)
+ case _ => false
+ }
}
case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = scope.body
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ScopeIndex(s) => s.equals(scope)
+ case _ => false
+ }
}
case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
@@ -395,17 +459,29 @@ case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
if (index % 2 == 0) Eval(branch.branches.apply(index / 2)._1)(branch.branches.apply(index / 2)._1.o)
else branch.branches.apply((index - 1) / 2)._2
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case BranchIndex(b, i) => i == index && b.equals(branch)
+ case _ => false
+ }
}
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case IndetBranchIndex(b, i) => i == index && b.equals(indet_branch)
+ case _ => false
+ }
}
// TODO: Switch cases could be multiple context indices deep; this does not work with the single index for make_step()
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = switch.body
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case SwitchIndex(s) => s.equals(switch)
+ case _ => false
+ }
}
case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
@@ -421,11 +497,19 @@ case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
case 2 => loop.body
case 3 => loop.update
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case LoopIndex(l, i) => i == index && l.equals(loop)
+ case _ => false
+ }
}
case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = ranged_for.body
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case RangedForIndex(r) => r.equals(ranged_for)
+ case _ => false
+ }
}
case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
@@ -438,51 +522,91 @@ case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index:
case 1 => try_catch_finally.after
case _ => try_catch_finally.catches.apply(index - 2).body
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case TryCatchFinallyIndex(t, i) => i == index && t.equals(try_catch_finally)
+ case _ => false
+ }
}
case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = synchronized.body
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case SynchronizedIndex(s) => s.equals(synchronized)
+ case _ => false
+ }
}
case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = par_invariant.content
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ParInvariantIndex(p) => p.equals(par_invariant)
+ case _ => false
+ }
}
case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = par_atomic.content
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ParAtomicIndex(p) => p.equals(par_atomic)
+ case _ => false
+ }
}
case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = par_barrier.content
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ParBarrierIndex(p) => p.equals(par_barrier)
+ case _ => false
+ }
}
case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = vec_block.content
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case VecBlockIndex(v) => v.equals(vec_block)
+ case _ => false
+ }
}
case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = wand_package.proof
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case WandPackageIndex(w) => w.equals(wand_package)
+ case _ => false
+ }
}
case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = model_do.impl
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case ModelDoIndex(m) => m.equals(model_do)
+ case _ => false
+ }
}
case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = cpp_lifetime_scope.body
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case CPPLifetimeScopeIndex(c) => c.equals(cpp_lifetime_scope)
+ case _ => false
+ }
}
case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = unresolved_seq_branch.branches.apply(index)._2
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case UnresolvedSeqBranchIndex(u, i) => i == index && u.equals(unresolved_seq_branch)
+ case _ => false
+ }
}
case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
@@ -494,6 +618,10 @@ case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G],
case 0 => Eval(unresolved_seq_loop.cond)(unresolved_seq_loop.cond.o)
case 1 => unresolved_seq_loop.body
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case UnresolvedSeqLoopIndex(u, i) => i == index && u.equals(unresolved_seq_loop)
+ case _ => false
+ }
}
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
@@ -502,19 +630,35 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
}
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case SeqBranchIndex(s, i) => i == index && s.equals(seq_branch)
+ case _ => false
+ }
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = seq_loop.body
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case SeqLoopIndex(s) => s.equals(seq_loop)
+ case _ => false
+ }
}
case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = veymont_assign_expression.assign
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case VeyMontAssignExpressionIndex(v) => v.equals(veymont_assign_expression)
+ case _ => false
+ }
}
case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
override def make_step(): Set[Option[Index[G]]] = Set(None)
override def resolve(): Statement[G] = communicatex.assign
+ override def equals(obj: scala.Any): Boolean = obj match {
+ case CommunicateXIndex(c) => c.equals(communicatex)
+ case _ => false
+ }
}
\ No newline at end of file
From b7c114eeebd873d61cb357650aceb3f07d61542f Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 15 Feb 2024 15:15:56 +0100
Subject: [PATCH 22/85] Added path conditions to CFG edges
---
.../vct/rewrite/cfg/CFGGenerator.scala | 11 +-
src/rewrite/vct/rewrite/cfg/Index.scala | 168 +++++++++---------
src/rewrite/vct/rewrite/cfg/Utils.scala | 17 +-
3 files changed, 110 insertions(+), 86 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 12f8da0efc..e88421cf34 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -39,6 +39,7 @@ case class CFGGenerator[G]() {
}
private def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = node match {
+ // TODO: Can these be simplified using evaluate_first()?
case PVLBranch(branches) =>
mutable.Set(CFGEdge(convert(Eval(branches.head._1)(branches.head._1.o), context.enter_scope(node)), None))
case PVLLoop(init, _, _, _, _) =>
@@ -113,7 +114,7 @@ case class CFGGenerator[G]() {
}
case IndetBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
- case Switch(expr, body) =>
+ case Switch(expr, body) => // TODO: Add conditions to switch statement transitions
Utils.find_all_cases(body, context.enter_scope(node)).map(t => CFGEdge(convert(t._1, t._2), None)) // TODO: Handle side effects in switch statement conditions
case Loop(init, _, _, _, _) =>
mutable.Set(CFGEdge(convert(init, context.enter_scope(node)), None))
@@ -153,15 +154,15 @@ case class CFGGenerator[G]() {
case PVLSeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
case Communicate(_, _) => sequential_successor(context)
case SeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
- case UnresolvedSeqBranch(branches) =>
+ case UnresolvedSeqBranch(branches) => // TODO: Consider branch conditions
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1._2, context.enter_scope(node, b._2)), None)))
case UnresolvedSeqLoop(cond, _, _) =>
mutable.Set(CFGEdge(convert(Eval(cond)(cond.o), context.enter_scope(node)), None))
- case SeqBranch(_, yes, no) => no match {
+ case SeqBranch(_, yes, no) => no match { // TODO: What are the conditions here?
case Some(stmt) => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node, 0)), None), CFGEdge(convert(stmt, context.enter_scope(node, 1)), None))
case None => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None))
}
- case SeqLoop(_, _, body) =>
+ case SeqLoop(_, _, body) => // TODO: What are the conditions here?
mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
case VeyMontAssignExpression(_, assign) =>
mutable.Set(CFGEdge(convert(assign, context.enter_scope(node)), None))
@@ -185,7 +186,7 @@ case class CFGGenerator[G]() {
}
private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] =
- index.make_step().map(i => CFGEdge(convert(i.resolve(), i), None))
+ index.make_step().map(i => CFGEdge(convert(i._1.resolve(), i._1), i._2))
private def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
val new_index: GlobalIndex[G] = index.return_from_call()
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 56bce55266..f348251187 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -15,14 +15,14 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
def enter_scope(node: Node[G], index: Int = 0): GlobalIndex[G] =
GlobalIndex(indices.prepended(Index[G](node, index)))
- def make_step(): mutable.Set[GlobalIndex[G]] = {
- if (indices.isEmpty) return mutable.Set[GlobalIndex[G]]()
- val steps: Set[Option[Index[G]]] = indices.head.make_step()
- val res = mutable.Set[GlobalIndex[G]]()
+ def make_step(): mutable.Set[(GlobalIndex[G], Option[Expr[G]])] = {
+ if (indices.isEmpty) return mutable.Set()
+ val steps: Set[(Option[Index[G]], Option[Expr[G]])] = indices.head.make_step()
+ val res = mutable.Set[(GlobalIndex[G], Option[Expr[G]])]()
for (step <- steps) {
step match {
- case Some(index) => res.addOne(GlobalIndex(index +: indices.tail))
- case None => res.addAll(GlobalIndex(indices.tail).make_step())
+ case (Some(index), cond) => res.addOne((GlobalIndex(index +: indices.tail), cond))
+ case (None, cond) => res.addAll(GlobalIndex(indices.tail).make_step().map(tup => (tup._1, Utils.and(tup._2, cond))))
}
}
res
@@ -40,7 +40,7 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
}
// Find the next statement
// TODO: Does this always return exactly one next step?
- GlobalIndex(stack.tail).make_step().head
+ GlobalIndex(stack.tail).make_step().head._1
}
def handle_exception(e: Expr[G]): GlobalIndex[G] = {
@@ -66,7 +66,7 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
}
// Find the next statement
// TODO: Does this always return exactly one next step?
- GlobalIndex(stack.tail).make_step().head
+ GlobalIndex(stack.tail).make_step().head._1
}
def continue_innermost_loop(): GlobalIndex[G] = {
@@ -89,11 +89,12 @@ sealed trait Index[G] {
/**
* Defines the set of possible next steps. An index in the returned set indicates that this index can replace the
* previous index at the top level of the index stack. A None value indicates that a step is possible, but it reaches
- * outside the scope of this index to the index below.
+ * outside the scope of this index to the index below. Along with these steps, this method returns the conditions for
+ * such a step in the form of a boolean expression, or None if a transition is certain to occur.
*
- * @return A set of all steps possible from the current index
+ * @return A set of all steps possible from the current index, along with conditions for taking them
*/
- def make_step(): Set[Option[Index[G]]]
+ def make_step(): Set[(Option[Index[G]], Option[Expr[G]])]
/**
* Returns the statement that corresponds to the current index.
@@ -153,7 +154,7 @@ object Index {
}
case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = instance_method.body.get
override def equals(obj: scala.Any): Boolean = obj match {
case InitialIndex(m) => m.equals(instance_method)
@@ -162,8 +163,9 @@ case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G]
}
case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = run_method.body.get
+ override def has_statement(): Boolean = run_method.body.nonEmpty
override def equals(obj: scala.Any): Boolean = obj match {
case RunMethodIndex(m) => m.equals(run_method)
case _ => false
@@ -171,9 +173,9 @@ case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
}
case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
- if (index == 0) Set(Some(ExpressionContainerIndex(statement, 1)))
- else Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ if (index == 0) Set((Some(ExpressionContainerIndex(statement, 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = statement
override def equals(obj: scala.Any): Boolean = obj match {
@@ -183,9 +185,9 @@ case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) exte
}
case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
- if (index < 2) Set(Some(AssignmentIndex(assign, index + 1)))
- else Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ if (index < 2) Set((Some(AssignmentIndex(assign, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(assign.target)(assign.target.o)
@@ -199,13 +201,15 @@ case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
}
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
// Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
if (index % 2 == 0 && index < 2 * (pvl_branch.branches.size - 1))
- Set(Some(PVLBranchIndex(pvl_branch, index + 2)), Some(PVLBranchIndex(pvl_branch, index + 1)))
+ Set((Some(PVLBranchIndex(pvl_branch, index + 2)), Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))),
+ (Some(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)))
else if (index == 2 * (pvl_branch.branches.size - 1))
- Set(Some(PVLBranchIndex(pvl_branch, index + 1)), None)
- else Set(None)
+ Set((Some(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)),
+ (None, Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))))
+ else Set((None, None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(pvl_branch.branches.apply(index / 2)._1)(pvl_branch.branches.apply(index / 2)._1.o)
@@ -218,11 +222,11 @@ case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index
}
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = index match {
- case 0 => Set(Some(PVLLoopIndex(pvl_loop, 1)))
- case 1 => Set(Some(PVLLoopIndex(pvl_loop, 2)), None)
- case 2 => Set(Some(PVLLoopIndex(pvl_loop, 3)))
- case 3 => Set(Some(PVLLoopIndex(pvl_loop, 1)))
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = index match {
+ case 0 => Set((Some(PVLLoopIndex(pvl_loop, 1)), None))
+ case 1 => Set((Some(PVLLoopIndex(pvl_loop, 2)), Some(pvl_loop.cond)), (None, Some(Utils.negate(pvl_loop.cond))))
+ case 2 => Set((Some(PVLLoopIndex(pvl_loop, 3)), None))
+ case 3 => Set((Some(PVLLoopIndex(pvl_loop, 1)), None))
}
override def resolve(): Statement[G] = index match {
case 0 => pvl_loop.init
@@ -237,7 +241,7 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
}
case class LabelIndex[G](label: Label[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = label.stat
override def equals(obj: scala.Any): Boolean = obj match {
case LabelIndex(l) => l.equals(label)
@@ -246,9 +250,9 @@ case class LabelIndex[G](label: Label[G]) extends Index[G] {
}
case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
- if (index < 2) Set(Some(FramedProofIndex(framed_proof, index + 1)))
- else Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ if (index < 2) Set((Some(FramedProofIndex(framed_proof, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(framed_proof.pre)(framed_proof.pre.o)
@@ -262,7 +266,7 @@ case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends
}
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = extract.contractedStatement
override def equals(obj: scala.Any): Boolean = obj match {
case ExtractIndex(e) => e.equals(extract)
@@ -272,9 +276,10 @@ case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement[G]]) extends Index[G] {
def this(eval: Eval[G], index: Int) = this(eval, index, Utils.find_all_subexpressions(eval.expr))
- override def make_step(): Set[Option[Index[G]]] = {
- if (index < subexpressions.size - 1) Set(Some(EvalIndex(eval, index + 1)))
- else Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ // TODO: Consider conditions in expressions, too
+ if (index < subexpressions.size - 1) Set((Some(EvalIndex(eval, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = subexpressions.apply(index)
override def has_statement(): Boolean = subexpressions.nonEmpty
@@ -294,15 +299,15 @@ case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index:
// 3. outArgs
// 4. yields
// 5. procedure body
- override def make_step(): Set[Option[Index[G]]] = {
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
val args: Seq[Expr[G]] = invoke_procedure.args
val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_procedure.givenMap
val outArgs: Seq[Expr[G]] = invoke_procedure.outArgs
val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_procedure.yields
if (index < args.size + givenMap.size + outArgs.size + yields.size - 1 ||
index == args.size + givenMap.size + outArgs.size + yields.size - 1 && invoke_procedure.ref.decl.body.nonEmpty)
- Set(Some(InvokeProcedureIndex(invoke_procedure, index + 1)))
- else Set(None)
+ Set((Some(InvokeProcedureIndex(invoke_procedure, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_procedure.args
@@ -343,15 +348,15 @@ case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], i
// 4. yields
// 5. out
// 6. constructor body
- override def make_step(): Set[Option[Index[G]]] = {
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
val args: Seq[Expr[G]] = invoke_constructor.args
val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_constructor.givenMap
val outArgs: Seq[Expr[G]] = invoke_constructor.outArgs
val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_constructor.yields
if (index < args.size + givenMap.size + outArgs.size + yields.size ||
index == args.size + givenMap.size + outArgs.size + yields.size && invoke_constructor.ref.decl.body.nonEmpty)
- Set(Some(InvokeConstructorIndex(invoke_constructor, index + 1)))
- else Set(None)
+ Set((Some(InvokeConstructorIndex(invoke_constructor, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_constructor.args
@@ -388,15 +393,15 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
// 4. outArgs
// 5. yields
// 6. method body
- override def make_step(): Set[Option[Index[G]]] = {
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
val args: Seq[Expr[G]] = invoke_method.args
val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_method.givenMap
val outArgs: Seq[Expr[G]] = invoke_method.outArgs
val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_method.yields
if (index < args.size + givenMap.size + outArgs.size + yields.size ||
index == args.size + givenMap.size + outArgs.size + yields.size && invoke_method.ref.decl.body.nonEmpty)
- Set(Some(InvokeMethodIndex(invoke_method, index + 1)))
- else Set(None)
+ Set((Some(InvokeMethodIndex(invoke_method, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_method.args
@@ -426,9 +431,9 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
}
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
- if (index < block.statements.size - 1) Set(Some(BlockIndex(block, index + 1)))
- else Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ if (index < block.statements.size - 1) Set((Some(BlockIndex(block, index + 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = block.statements.apply(index)
override def equals(obj: scala.Any): Boolean = obj match {
@@ -438,7 +443,7 @@ case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
}
case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = scope.body
override def equals(obj: scala.Any): Boolean = obj match {
case ScopeIndex(s) => s.equals(scope)
@@ -447,13 +452,15 @@ case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
}
case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
// Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
if (index % 2 == 0 && index < 2 * (branch.branches.size - 1))
- Set(Some(BranchIndex(branch, index + 2)), Some(BranchIndex(branch, index + 1)))
+ Set((Some(BranchIndex(branch, index + 2)), Some(Utils.negate(branch.branches.apply(index / 2)._1))),
+ (Some(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)))
else if (index == 2 * (branch.branches.size - 1))
- Set(Some(BranchIndex(branch, index + 1)), None)
- else Set(None)
+ Set((Some(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)),
+ (None, Some(Utils.negate(branch.branches.apply(index / 2)._1))))
+ else Set((None, None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(branch.branches.apply(index / 2)._1)(branch.branches.apply(index / 2)._1.o)
@@ -466,7 +473,7 @@ case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
}
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
override def equals(obj: scala.Any): Boolean = obj match {
case IndetBranchIndex(b, i) => i == index && b.equals(indet_branch)
@@ -476,7 +483,7 @@ case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends
// TODO: Switch cases could be multiple context indices deep; this does not work with the single index for make_step()
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = switch.body
override def equals(obj: scala.Any): Boolean = obj match {
case SwitchIndex(s) => s.equals(switch)
@@ -485,11 +492,11 @@ case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
}
case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = index match {
- case 0 => Set(Some(LoopIndex(loop, 1)))
- case 1 => Set(Some(LoopIndex(loop, 2)), None)
- case 2 => Set(Some(LoopIndex(loop, 3)))
- case 3 => Set(Some(LoopIndex(loop, 1)))
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = index match {
+ case 0 => Set((Some(LoopIndex(loop, 1)), None))
+ case 1 => Set((Some(LoopIndex(loop, 2)), Some(loop.cond)), (None, Some(Utils.negate(loop.cond))))
+ case 2 => Set((Some(LoopIndex(loop, 3)), None))
+ case 3 => Set((Some(LoopIndex(loop, 1)), None))
}
override def resolve(): Statement[G] = index match {
case 0 => loop.init
@@ -504,7 +511,7 @@ case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
}
case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((Some(this), None), (None, None))
override def resolve(): Statement[G] = ranged_for.body
override def equals(obj: scala.Any): Boolean = obj match {
case RangedForIndex(r) => r.equals(ranged_for)
@@ -513,9 +520,9 @@ case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
}
case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = {
- if (index != 1) Set(Some(TryCatchFinallyIndex(try_catch_finally, 1)))
- else Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ if (index != 1) Set((Some(TryCatchFinallyIndex(try_catch_finally, 1)), None))
+ else Set((None, None))
}
override def resolve(): Statement[G] = index match {
case 0 => try_catch_finally.body
@@ -529,7 +536,7 @@ case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index:
}
case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = synchronized.body
override def equals(obj: scala.Any): Boolean = obj match {
case SynchronizedIndex(s) => s.equals(synchronized)
@@ -538,7 +545,7 @@ case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G]
}
case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = par_invariant.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParInvariantIndex(p) => p.equals(par_invariant)
@@ -547,7 +554,7 @@ case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G]
}
case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = par_atomic.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParAtomicIndex(p) => p.equals(par_atomic)
@@ -556,7 +563,7 @@ case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
}
case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = par_barrier.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParBarrierIndex(p) => p.equals(par_barrier)
@@ -565,7 +572,7 @@ case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
}
case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = vec_block.content
override def equals(obj: scala.Any): Boolean = obj match {
case VecBlockIndex(v) => v.equals(vec_block)
@@ -574,7 +581,7 @@ case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
}
case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = wand_package.proof
override def equals(obj: scala.Any): Boolean = obj match {
case WandPackageIndex(w) => w.equals(wand_package)
@@ -583,7 +590,7 @@ case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
}
case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = model_do.impl
override def equals(obj: scala.Any): Boolean = obj match {
case ModelDoIndex(m) => m.equals(model_do)
@@ -592,7 +599,7 @@ case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
}
case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = cpp_lifetime_scope.body
override def equals(obj: scala.Any): Boolean = obj match {
case CPPLifetimeScopeIndex(c) => c.equals(cpp_lifetime_scope)
@@ -601,7 +608,7 @@ case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) ext
}
case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = unresolved_seq_branch.branches.apply(index)._2
override def equals(obj: scala.Any): Boolean = obj match {
case UnresolvedSeqBranchIndex(u, i) => i == index && u.equals(unresolved_seq_branch)
@@ -610,9 +617,10 @@ case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranc
}
case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = index match {
- case 0 => Set(Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1)), None)
- case 1 => Set(Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0)))
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = index match {
+ case 0 => Set((Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1)), Some(unresolved_seq_loop.cond)),
+ (None, Some(Utils.negate(unresolved_seq_loop.cond))))
+ case 1 => Set((Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0)), None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(unresolved_seq_loop.cond)(unresolved_seq_loop.cond.o)
@@ -625,7 +633,7 @@ case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G],
}
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = index match {
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
@@ -637,7 +645,7 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = seq_loop.body
override def equals(obj: scala.Any): Boolean = obj match {
case SeqLoopIndex(s) => s.equals(seq_loop)
@@ -646,7 +654,7 @@ case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
}
case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = veymont_assign_expression.assign
override def equals(obj: scala.Any): Boolean = obj match {
case VeyMontAssignExpressionIndex(v) => v.equals(veymont_assign_expression)
@@ -655,7 +663,7 @@ case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAss
}
case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
- override def make_step(): Set[Option[Index[G]]] = Set(None)
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
override def resolve(): Statement[G] = communicatex.assign
override def equals(obj: scala.Any): Boolean = obj match {
case CommunicateXIndex(c) => c.equals(communicatex)
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index c2041028fc..0b8617334c 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -9,6 +9,7 @@ import scala.collection.mutable
object Utils {
def find_all_subexpressions[G](expr: Expr[G]): Seq[Statement[G]] = expr match {
+ // Expressions causing side effects
case pae @ PreAssignExpression(target, value) => Seq(Assign(target, value)(pae.blame)(pae.o))
case pae @ PostAssignExpression(target, value) => Seq(Assign(target, value)(pae.blame)(pae.o))
case With(pre, value) => ???
@@ -20,12 +21,13 @@ object Utils {
case pi @ ProcedureInvocation(ref, args, outArgs, typeArgs, givenMap, yields) =>
Seq(InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields)(pi.blame)(pi.o))
case no @ NewObject(cls) => Seq(Instantiate(cls, get_out_variable(cls, no.o))(no.o))
+ // Expressions that affect the edge condition
// TODO: Consider conditions for control flow graph
case Select(condition, whenTrue, whenFalse) => Seq(condition, whenTrue, whenFalse).flatMap(e => find_all_subexpressions(e))
case Implies(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
case And(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
case Or(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
- //
+ // General recursion case
case _ => expr.subnodes.collect{ case ex: Expr[G] => ex }.flatMap(e => find_all_subexpressions(e))
}
@@ -40,4 +42,17 @@ object Utils {
case c: SwitchCase[G] => mutable.Set((c, index))
case _ => mutable.Set() // TODO: Assuming that there are no cases in deeper structures (branches, loops etc.)
}
+
+ def negate[G](expr: Expr[G]): Expr[G] = expr match {
+ case Not(inner) => inner
+ case _ => Not(expr)(expr.o)
+ }
+
+ def and[G](expr1: Option[Expr[G]], expr2: Option[Expr[G]]): Option[Expr[G]] = expr1 match {
+ case None => expr2
+ case Some(e1) => expr2 match {
+ case None => expr1
+ case Some(e2) => Some(And(e1, e2)(e1.o)) // TODO: Ignore origin of e2?
+ }
+ }
}
From 7daac9ef88c55081d1e4573d144224e71f00d649 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 15 Feb 2024 17:02:40 +0100
Subject: [PATCH 23/85] Finding main method automatically, added support for
unresolved-containers, some code cleanup
---
src/main/vct/main/stages/PrintCFG.scala | 5 +-
.../vct/rewrite/cfg/CFGGenerator.scala | 79 +++++++------------
src/rewrite/vct/rewrite/cfg/Index.scala | 18 ++++-
3 files changed, 44 insertions(+), 58 deletions(-)
diff --git a/src/main/vct/main/stages/PrintCFG.scala b/src/main/vct/main/stages/PrintCFG.scala
index 3ed85f5d50..e4c63e2e48 100644
--- a/src/main/vct/main/stages/PrintCFG.scala
+++ b/src/main/vct/main/stages/PrintCFG.scala
@@ -22,9 +22,8 @@ case class PrintCFG(out: Path) extends Stage[Node[_ <: Generation], Unit] {
override def progressWeight: Int = 0
override def run(in1: Node[_ <: Generation]): Unit = {
- // TODO: Burn this and do it properly
- // if m.o.getPreferredName.getOrElse(Name()).toString.equals("main")
- val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] => m }.get
+ // TODO: Is there a better way to find a "main" method?
+ val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] if m.o.getPreferredName.get.snake.equals("main") => m }.get
CFGPrinter().print_ast_as_cfg(main_method, out)
}
}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index e88421cf34..03d6336a59 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -40,25 +40,22 @@ case class CFGGenerator[G]() {
private def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = node match {
// TODO: Can these be simplified using evaluate_first()?
- case PVLBranch(branches) =>
- mutable.Set(CFGEdge(convert(Eval(branches.head._1)(branches.head._1.o), context.enter_scope(node)), None))
- case PVLLoop(init, _, _, _, _) =>
- mutable.Set(CFGEdge(convert(init, context.enter_scope(node)), None))
+ case PVLBranch(_) => evaluate_first(context.enter_scope(node))
+ case PVLLoop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
// NonExecutableStatement
case LocalDecl(_) => sequential_successor(context)
case SpecIgnoreStart() => sequential_successor(context) // TODO: What is this?
case SpecIgnoreEnd() => sequential_successor(context)
// NormallyCompletingStatement
- case Assign(target, _) => context.indices.head match {
+ case Assign(_, _) => context.indices.head match {
case AssignmentIndex(a, _) if a == node => sequential_successor(context)
- case _ => mutable.Set(CFGEdge(convert(Eval(target)(target.o), context.enter_scope(node)), None))
+ case _ => evaluate_first(context.enter_scope(node))
}
case Send(_, _, res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Recv(_) => sequential_successor(context)
case DefaultCase() => sequential_successor(context)
case Case(pattern) => sequential_successor(context) // TODO: Handle expression side effects in switch case conditions
- case Label(_, stat) =>
- mutable.Set(CFGEdge(convert(stat, context.enter_scope(node)), None))
+ case Label(_, _) => evaluate_first(context.enter_scope(node))
case Goto(lbl) => found_labels.get(lbl.decl) match {
case Some(node) => mutable.Set(CFGEdge(node, None))
case None => mutable.Set()
@@ -83,10 +80,8 @@ case class CFGGenerator[G]() {
case Unfold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case WandApply(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Havoc(_) => sequential_successor(context)
- case FramedProof(pre, _, _) =>
- mutable.Set(CFGEdge(convert(Eval(pre)(pre.o), context.enter_scope(node)), None))
- case Extract(contractedStatement) =>
- mutable.Set(CFGEdge(convert(contractedStatement, context.enter_scope(node)), None))
+ case FramedProof(_, _, _) => evaluate_first(context.enter_scope(node))
+ case Extract(_) => evaluate_first(context.enter_scope(node))
// ExceptionalStatement
case Eval(_) => evaluate_first(context.enter_scope(node))
case Return(result) => handle_expression_container(node, Eval(result)(result.o), context, mutable.Set(CFGEdge(return_successor(context), None)))
@@ -104,46 +99,30 @@ case class CFGGenerator[G]() {
case InvokeConstructor(_, _, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
case InvokeMethod(_, _, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
// CompositeStatement
- case Block(statements) =>
- mutable.Set(CFGEdge(convert(statements.head, context.enter_scope(node)), None))
- case Scope(_, body) =>
- mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
- case Branch(branches) => {
- val eval: Eval[G] = Eval(branches.head._1)(branches.head._1.o)
- mutable.Set(CFGEdge(convert(eval, context.enter_scope(node)), None))
- }
+ case Block(_) => evaluate_first(context.enter_scope(node))
+ case Scope(_, _) => evaluate_first(context.enter_scope(node))
+ case Branch(_) => evaluate_first(context.enter_scope(node))
case IndetBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
case Switch(expr, body) => // TODO: Add conditions to switch statement transitions
Utils.find_all_cases(body, context.enter_scope(node)).map(t => CFGEdge(convert(t._1, t._2), None)) // TODO: Handle side effects in switch statement conditions
- case Loop(init, _, _, _, _) =>
- mutable.Set(CFGEdge(convert(init, context.enter_scope(node)), None))
- case RangedFor(_, _, body) =>
- mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
- case TryCatchFinally(body, _, _) =>
- mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
- case Synchronized(_, body) =>
- mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
- case ParInvariant(_, _, content) =>
- mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
- case ParAtomic(_, content) =>
- mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
- case ParBarrier(_, _, _, _, content) =>
- mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
+ case Loop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
+ case RangedFor(_, _, _) => evaluate_first(context.enter_scope(node))
+ case TryCatchFinally(_, _, _) => evaluate_first(context.enter_scope(node))
+ case Synchronized(_, _) => evaluate_first(context.enter_scope(node))
+ case ParInvariant(_, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
+ case ParAtomic(_, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
+ case ParBarrier(_, _, _, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
case ParStatement(_) => sequential_successor(context)
- case VecBlock(_, _, _, content) =>
- mutable.Set(CFGEdge(convert(content, context.enter_scope(node)), None)) // TODO: Expression side effects
- case WandPackage(_, proof) =>
- mutable.Set(CFGEdge(convert(proof, context.enter_scope(node)), None)) // TODO: Expression side effects
- case ModelDo(_, _, _, _, impl) =>
- mutable.Set(CFGEdge(convert(impl, context.enter_scope(node)), None)) // TODO: Expression side effects
+ case VecBlock(_, _, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
+ case WandPackage(_, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
+ case ModelDo(_, _, _, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
// CStatement
case CDeclarationStatement(_) => sequential_successor(context)
case CGoto(label) => ??? // I'm not dealing with string labels
// CPPStatement
case CPPDeclarationStatement(_) => sequential_successor(context)
- case CPPLifetimeScope(body) =>
- mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
+ case CPPLifetimeScope(_) => evaluate_first(context.enter_scope(node))
case JavaLocalDeclarationStatement(_) => sequential_successor(context)
// SilverStatement
case SilverNewRef(_, _) => sequential_successor(context)
@@ -154,20 +133,16 @@ case class CFGGenerator[G]() {
case PVLSeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
case Communicate(_, _) => sequential_successor(context)
case SeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
- case UnresolvedSeqBranch(branches) => // TODO: Consider branch conditions
+ case UnresolvedSeqBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1._2, context.enter_scope(node, b._2)), None)))
- case UnresolvedSeqLoop(cond, _, _) =>
- mutable.Set(CFGEdge(convert(Eval(cond)(cond.o), context.enter_scope(node)), None))
- case SeqBranch(_, yes, no) => no match { // TODO: What are the conditions here?
+ case UnresolvedSeqLoop(_, _, _) => evaluate_first(context.enter_scope(node))
+ case SeqBranch(_, yes, no) => no match { // TODO: What are the conditions here?
case Some(stmt) => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node, 0)), None), CFGEdge(convert(stmt, context.enter_scope(node, 1)), None))
case None => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None))
}
- case SeqLoop(_, _, body) => // TODO: What are the conditions here?
- mutable.Set(CFGEdge(convert(body, context.enter_scope(node)), None))
- case VeyMontAssignExpression(_, assign) =>
- mutable.Set(CFGEdge(convert(assign, context.enter_scope(node)), None))
- case CommunicateX(_, _, _, assign) =>
- mutable.Set(CFGEdge(convert(assign, context.enter_scope(node)), None))
+ case SeqLoop(_, _, _) => evaluate_first(context.enter_scope(node)) // TODO: What are the conditions here?
+ case VeyMontAssignExpression(_, _) => evaluate_first(context.enter_scope(node))
+ case CommunicateX(_, _, _, _) => evaluate_first(context.enter_scope(node))
}
private def handle_expression_container(statement: Statement[G],
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index f348251187..8c8d217f4f 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -608,8 +608,20 @@ case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) ext
}
case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
- override def resolve(): Statement[G] = unresolved_seq_branch.branches.apply(index)._2
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ // Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
+ if (index % 2 == 0 && index < 2 * (unresolved_seq_branch.branches.size - 1))
+ Set((Some(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 2)), Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))),
+ (Some(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)))
+ else if (index == 2 * (unresolved_seq_branch.branches.size - 1))
+ Set((Some(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)),
+ (None, Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))))
+ else Set((None, None))
+ }
+ override def resolve(): Statement[G] = {
+ if (index % 2 == 0) Eval(unresolved_seq_branch.branches.apply(index / 2)._1)(unresolved_seq_branch.branches.apply(index / 2)._1.o)
+ else unresolved_seq_branch.branches.apply((index - 1) / 2)._2
+ }
override def equals(obj: scala.Any): Boolean = obj match {
case UnresolvedSeqBranchIndex(u, i) => i == index && u.equals(unresolved_seq_branch)
case _ => false
@@ -645,7 +657,7 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((Some(this), None), (None, None))
override def resolve(): Statement[G] = seq_loop.body
override def equals(obj: scala.Any): Boolean = obj match {
case SeqLoopIndex(s) => s.equals(seq_loop)
From 2bc3a50df4f81c0719f9e698ebe5c78e80d22281 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 16 Feb 2024 09:34:52 +0100
Subject: [PATCH 24/85] Changed fork to create new stack; enabled terminal CFG
nodes
---
.../vct/rewrite/cfg/CFGGenerator.scala | 19 ++++++++++--------
src/rewrite/vct/rewrite/cfg/CFGNode.scala | 15 ++++++++++++--
src/rewrite/vct/rewrite/cfg/CFGPrinter.scala | 20 +++++++++----------
3 files changed, 34 insertions(+), 20 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 03d6336a59..af32f3e681 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -70,7 +70,8 @@ case class CFGGenerator[G]() {
case Notify(_) => sequential_successor(context)
case Fork(obj) => {
val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head
- sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, context.enter_scope(run_method)), None))
+ // Get the successor(s) of the fork statement as well as the new thread, starting with the run method
+ sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, GlobalIndex[G](mutable.Seq()).enter_scope(run_method)), None))
}
case Join(_) => sequential_successor(context)
case Lock(_) => sequential_successor(context)
@@ -160,27 +161,29 @@ case class CFGGenerator[G]() {
else sequential_successor(index)
}
- private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] =
- index.make_step().map(i => CFGEdge(convert(i._1.resolve(), i._1), i._2))
+ private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
+ if (index.indices.nonEmpty) index.make_step().map(i => CFGEdge(convert(i._1.resolve(), i._1), i._2))
+ else mutable.Set(CFGEdge(CFGTerminal(), None))
+ }
- private def return_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ private def return_successor(index: GlobalIndex[G]): CFGEntry[G] = {
val new_index: GlobalIndex[G] = index.return_from_call()
convert(new_index.resolve(), new_index)
}
- private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGNode[G] = {
+ private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGEntry[G] = {
val new_index = index.handle_exception(exception)
// Terminate on unhandled exception
- if (new_index.indices.isEmpty) return CFGNode(exception, mutable.Set())
+ if (new_index.indices.isEmpty) return CFGTerminal()
convert(new_index.resolve(), new_index)
}
- private def break_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ private def break_successor(index: GlobalIndex[G]): CFGEntry[G] = {
val new_index = index.handle_break()
convert(new_index.resolve(), new_index)
}
- private def continue_successor(index: GlobalIndex[G]): CFGNode[G] = {
+ private def continue_successor(index: GlobalIndex[G]): CFGEntry[G] = {
val new_index = index.continue_innermost_loop()
convert(new_index.resolve(), new_index)
}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGNode.scala b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
index bb7e3094bf..ca2587f035 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGNode.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGNode.scala
@@ -4,5 +4,16 @@ import vct.col.ast.{Expr, Node}
import scala.collection.mutable
-case class CFGNode[G](ast_node: Node[G], successors: mutable.Set[CFGEdge[G]])
-case class CFGEdge[G](target: CFGNode[G], condition: Option[Expr[G]])
\ No newline at end of file
+sealed trait CFGEntry[G] {
+ def get_successors: mutable.Set[CFGEdge[G]]
+}
+case class CFGNode[G](ast_node: Node[G], successors: mutable.Set[CFGEdge[G]]) extends CFGEntry[G] {
+ override def get_successors: mutable.Set[CFGEdge[G]] = successors
+ override def toString: String = ast_node.toInlineString
+}
+case class CFGTerminal[G]() extends CFGEntry[G] {
+ override def get_successors: mutable.Set[CFGEdge[G]] = mutable.Set()
+ override def toString: String = ""
+}
+
+case class CFGEdge[G](target: CFGEntry[G], condition: Option[Expr[G]])
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/CFGPrinter.scala b/src/rewrite/vct/rewrite/cfg/CFGPrinter.scala
index 3a11ea2817..465b81792d 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGPrinter.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGPrinter.scala
@@ -9,7 +9,7 @@ import java.nio.file.Path
import scala.collection.mutable
case class CFGPrinter[G]() {
- private val node_names: mutable.Map[CFGNode[G], String] = mutable.HashMap[CFGNode[G], String]()
+ private val node_names: mutable.Map[CFGEntry[G], String] = mutable.HashMap[CFGEntry[G], String]()
private val no_condition: Expr[G] = BooleanValue(value = true)(Origin(Seq()))
private var node_index: Int = 0
@@ -18,15 +18,15 @@ case class CFGPrinter[G]() {
// Write all nodes
node_names.foreach(naming => writer.append(naming._2)
.append(s" [label=${"\""}")
- .append(naming._1.ast_node.toInlineString)
+ .append(naming._1.toString)
.append(s"${"\""}];\n"))
// Write all edges
- node_names.foreach(naming => naming._1.successors.foreach(edge => writer.append(naming._2)
- .append(" -> ")
- .append(node_names(edge.target))
- .append(s" [label=${"\""}")
- .append(edge.condition.getOrElse(no_condition).toInlineString)
- .append(s"${"\""}];\n")))
+ node_names.foreach(naming => naming._1.get_successors.foreach(edge => writer.append(naming._2)
+ .append(" -> ")
+ .append(node_names(edge.target))
+ .append(s" [label=${"\""}")
+ .append(edge.condition.getOrElse(no_condition).toInlineString)
+ .append(s"${"\""}];\n")))
writer.append("}")
}
@@ -36,7 +36,7 @@ case class CFGPrinter[G]() {
RWFile(path.toFile).write(w => print_cfg(w))
}
- private def find_all_nodes(node: CFGNode[G]): Unit = {
+ private def find_all_nodes(node: CFGEntry[G]): Unit = {
if (!node_names.contains(node)) {
// Give new name to node
val node_name: String = s"n$node_index"
@@ -44,7 +44,7 @@ case class CFGPrinter[G]() {
node_names.addOne((node, node_name))
// Recursively call for successors
- node.successors.foreach(e => find_all_nodes(e.target))
+ node.get_successors.foreach(e => find_all_nodes(e.target))
}
}
}
From 585c3c737bd94c06e77a9effdd48362dfc874672 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 16 Feb 2024 10:39:03 +0100
Subject: [PATCH 25/85] Enable terminal node
---
.../vct/rewrite/cfg/CFGGenerator.scala | 33 ++++++++-----------
src/rewrite/vct/rewrite/cfg/Index.scala | 9 +++--
2 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index af32f3e681..2e73b2b0a5 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -157,34 +157,29 @@ case class CFGGenerator[G]() {
}
private def evaluate_first(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
- if (index.has_statement()) mutable.Set(CFGEdge(convert(index.resolve(), index), None))
+ if (index.has_statement()) mutable.Set(CFGEdge(resolve_index(index), None))
else sequential_successor(index)
}
private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
- if (index.indices.nonEmpty) index.make_step().map(i => CFGEdge(convert(i._1.resolve(), i._1), i._2))
+ if (index.indices.nonEmpty) index.make_step().map(i => CFGEdge(resolve_index(i._1), i._2))
else mutable.Set(CFGEdge(CFGTerminal(), None))
}
- private def return_successor(index: GlobalIndex[G]): CFGEntry[G] = {
- val new_index: GlobalIndex[G] = index.return_from_call()
- convert(new_index.resolve(), new_index)
- }
+ private def return_successor(index: GlobalIndex[G]): CFGEntry[G] =
+ resolve_index(index.return_from_call())
- private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGEntry[G] = {
- val new_index = index.handle_exception(exception)
- // Terminate on unhandled exception
- if (new_index.indices.isEmpty) return CFGTerminal()
- convert(new_index.resolve(), new_index)
- }
+ private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGEntry[G] =
+ resolve_index(index.handle_exception(exception))
- private def break_successor(index: GlobalIndex[G]): CFGEntry[G] = {
- val new_index = index.handle_break()
- convert(new_index.resolve(), new_index)
- }
+ private def break_successor(index: GlobalIndex[G]): CFGEntry[G] =
+ resolve_index(index.handle_break())
+
+ private def continue_successor(index: GlobalIndex[G]): CFGEntry[G] =
+ resolve_index(index.continue_innermost_loop())
- private def continue_successor(index: GlobalIndex[G]): CFGEntry[G] = {
- val new_index = index.continue_innermost_loop()
- convert(new_index.resolve(), new_index)
+ private def resolve_index(index: GlobalIndex[G]): CFGEntry[G] = index.resolve() match {
+ case Some(stmt) => convert(stmt, index)
+ case None => CFGTerminal()
}
}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 8c8d217f4f..7011b47325 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -16,7 +16,7 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
GlobalIndex(indices.prepended(Index[G](node, index)))
def make_step(): mutable.Set[(GlobalIndex[G], Option[Expr[G]])] = {
- if (indices.isEmpty) return mutable.Set()
+ if (indices.isEmpty) return mutable.Set((this, None))
val steps: Set[(Option[Index[G]], Option[Expr[G]])] = indices.head.make_step()
val res = mutable.Set[(GlobalIndex[G], Option[Expr[G]])]()
for (step <- steps) {
@@ -28,9 +28,12 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
res
}
- def resolve(): Statement[G] = indices.dropWhile(i => !i.has_statement()).head.resolve()
+ def resolve(): Option[Statement[G]] = indices.dropWhile(i => !i.has_statement()).headOption match{
+ case Some(idx) => Some(idx.resolve())
+ case None => None
+ }
- def has_statement(): Boolean = indices.head.has_statement()
+ def has_statement(): Boolean = indices.nonEmpty && indices.head.has_statement()
def return_from_call(): GlobalIndex[G] = {
// Find innermost subroutine call
From 8da6793c97fbd4cf3533eb18e03f2b104eb160e2 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 16 Feb 2024 11:49:31 +0100
Subject: [PATCH 26/85] Added support for edge conditions in expression side
effects
---
src/rewrite/vct/rewrite/cfg/Utils.scala | 35 ++++++++++++++++++++-----
1 file changed, 29 insertions(+), 6 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index 0b8617334c..8842d52ede 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -22,11 +22,34 @@ object Utils {
Seq(InvokeProcedure(ref, args, outArgs, typeArgs, givenMap, yields)(pi.blame)(pi.o))
case no @ NewObject(cls) => Seq(Instantiate(cls, get_out_variable(cls, no.o))(no.o))
// Expressions that affect the edge condition
- // TODO: Consider conditions for control flow graph
- case Select(condition, whenTrue, whenFalse) => Seq(condition, whenTrue, whenFalse).flatMap(e => find_all_subexpressions(e))
- case Implies(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
- case And(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
- case Or(left, right) => Seq(left, right).flatMap(e => find_all_subexpressions(e))
+ case Select(condition, whenTrue, whenFalse) => {
+ val statements: Seq[Statement[G]] = find_all_subexpressions(condition)
+ val true_stmts: Seq[Statement[G]] = find_all_subexpressions(whenTrue)
+ val false_stmts: Seq[Statement[G]] = find_all_subexpressions(whenFalse)
+ var branches: Seq[(Expr[G], Statement[G])] = Seq()
+ if (true_stmts.nonEmpty) branches = branches.appended((condition, Block(true_stmts)(whenTrue.o)))
+ if (false_stmts.nonEmpty) branches = branches.appended((negate(condition), Block(false_stmts)(whenFalse.o)))
+ if (branches.nonEmpty) statements ++ Seq(Branch(branches)(expr.o))
+ else statements
+ }
+ case Implies(left, right) => {
+ val left_stmts: Seq[Statement[G]] = find_all_subexpressions(left)
+ val right_stmts: Seq[Statement[G]] = find_all_subexpressions(right)
+ if (right_stmts.nonEmpty) left_stmts ++ Seq(Branch(Seq((left, Block(right_stmts)(right.o))))(expr.o))
+ else left_stmts
+ }
+ case And(left, right) => {
+ val left_stmts: Seq[Statement[G]] = find_all_subexpressions(left)
+ val right_stmts: Seq[Statement[G]] = find_all_subexpressions(right)
+ if (right_stmts.nonEmpty) left_stmts ++ Seq(Branch(Seq((left, Block(right_stmts)(right.o))))(expr.o))
+ else left_stmts
+ }
+ case Or(left, right) => {
+ val left_stmts: Seq[Statement[G]] = find_all_subexpressions(left)
+ val right_stmts: Seq[Statement[G]] = find_all_subexpressions(right)
+ if (right_stmts.nonEmpty) left_stmts ++ Seq(Branch(Seq((negate(left), Block(right_stmts)(right.o))))(expr.o))
+ else left_stmts
+ }
// General recursion case
case _ => expr.subnodes.collect{ case ex: Expr[G] => ex }.flatMap(e => find_all_subexpressions(e))
}
@@ -52,7 +75,7 @@ object Utils {
case None => expr2
case Some(e1) => expr2 match {
case None => expr1
- case Some(e2) => Some(And(e1, e2)(e1.o)) // TODO: Ignore origin of e2?
+ case Some(e2) => Some(And(e1, e2)(e1.o)) // TODO: Is the origin important?
}
}
}
From bd091dc42609be8e7ea3f99d181c823c6e077000 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 16 Feb 2024 14:21:37 +0100
Subject: [PATCH 27/85] Started work on CFG traversal for RASI generation
---
.../vct/rewrite/rasi/AbstractState.scala | 27 +++++++++++++++++++
.../vct/rewrite/rasi/ConcreteVariable.scala | 24 +++++++++++++++++
.../vct/rewrite/rasi/RASIGenerator.scala | 14 ++++++++++
src/rewrite/vct/rewrite/rasi/Utils.scala | 11 ++++++++
4 files changed, 76 insertions(+)
create mode 100644 src/rewrite/vct/rewrite/rasi/AbstractState.scala
create mode 100644 src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
create mode 100644 src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
create mode 100644 src/rewrite/vct/rewrite/rasi/Utils.scala
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
new file mode 100644
index 0000000000..d92f73d9d1
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -0,0 +1,27 @@
+package vct.rewrite.rasi
+
+import vct.col.ast._
+
+case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int]) {
+ def resolve_integer_expression(expr: Expr[G]): Int = expr match {
+ case CIntegerValue(value) => value.intValue
+ case IntegerValue(value) => value.intValue
+ case Local(ref) => ???
+ case DerefHeapVariable(ref) => ???
+ case Deref(obj, ref) => ???
+ case DerefPointer(pointer) => ???
+ case SizeOf(tname) => ???
+ case UMinus(arg) => -resolve_integer_expression(arg)
+ case AmbiguousMult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
+ case AmbiguousPlus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
+ case AmbiguousMinus(left, right) => resolve_integer_expression(left) - resolve_integer_expression(right)
+ case AmbiguousComputationalOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
+ case AmbiguousComputationalXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
+ case AmbiguousComputationalAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
+ case
+ }
+
+ def to_expression(): Expr[G] = {
+ ???
+ }
+}
diff --git a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
new file mode 100644
index 0000000000..24594169d2
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
@@ -0,0 +1,24 @@
+package vct.rewrite.rasi
+
+import vct.col.ast._
+
+trait ConcreteVariable[G] {
+ def is(expr: Expr[G]): Boolean
+ def extract_from_expression(expr: Expr[G]): Field[G] = expr match {
+ // TODO: Need to also consider the object!
+ case Deref(obj, ref) => ref.decl
+ }
+}
+
+case class FieldVariable[G](field: Field[G]) extends ConcreteVariable[G] {
+ override def is(expr: Expr[G]): Boolean = field.equals(extract_from_expression(expr))
+}
+
+case class IndexedVariable[G](field: Field[G], i: Int) extends ConcreteVariable[G] {
+ override def is(expr: Expr[G]): Boolean = expr match {
+ case AmbiguousSubscript(collection, index) => field.equals(extract_from_expression(collection)) && i == Utils.resolve_integer_expression(index)
+ case SeqSubscript(seq, index) => field.equals(extract_from_expression(seq)) && i == Utils.resolve_integer_expression(index)
+ case ArraySubscript(arr, index) => field.equals(extract_from_expression(arr)) && i == Utils.resolve_integer_expression(index)
+ case PointerSubscript(pointer, index) => field.equals(extract_from_expression(pointer)) && i == Utils.resolve_integer_expression(index)
+ }
+}
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
new file mode 100644
index 0000000000..82c65caf90
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -0,0 +1,14 @@
+package vct.rewrite.rasi
+
+import vct.col.ast.{BooleanValue, Expr, Or}
+import vct.rewrite.cfg.CFGNode
+
+case class RASIGenerator[G]() {
+ private def neutral_element: Expr[G] = ???
+
+ private def explore(node: CFGNode[G]): Set[AbstractState[G]] = ???
+
+ def generate_rasi(node: CFGNode[G]): Expr[G] = {
+ explore(node).map(a => a.to_expression()).fold(neutral_element)((e1, e2) => Or(e1, e2)(e1.o))
+ }
+}
diff --git a/src/rewrite/vct/rewrite/rasi/Utils.scala b/src/rewrite/vct/rewrite/rasi/Utils.scala
new file mode 100644
index 0000000000..7726531fdf
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/Utils.scala
@@ -0,0 +1,11 @@
+package vct.rewrite.rasi
+
+import vct.col.ast._
+
+case object Utils {
+ def resolve_integer_expression[G](expr: Expr[G]): Int = expr match {
+ case IntegerValue(value) => value.intValue
+ case Plus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
+
+ }
+}
From d43a762d1b38d37463061bf29aad1441a651fb0b Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 16 Feb 2024 16:19:27 +0100
Subject: [PATCH 28/85] Working on RASI generator
---
.../vct/rewrite/rasi/AbstractProcess.scala | 17 +++++
.../vct/rewrite/rasi/AbstractState.scala | 70 ++++++++++++++++++-
.../vct/rewrite/rasi/ConcreteVariable.scala | 3 +
.../vct/rewrite/rasi/RASIGenerator.scala | 27 +++++--
4 files changed, 110 insertions(+), 7 deletions(-)
create mode 100644 src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
new file mode 100644
index 0000000000..8dc4e4bf30
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -0,0 +1,17 @@
+package vct.rewrite.rasi
+
+import vct.col.ast._
+import vct.rewrite.cfg.{CFGEdge, CFGEntry, CFGNode, CFGTerminal}
+
+case class AbstractProcess[G](name: String) {
+ def get_next(node: CFGEntry[G], state: AbstractState[G]): Set[AbstractState[G]] = node match {
+ case CFGTerminal() => Set()
+ case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression (e.condition.get)).flatMap (e => process_cfg_edge(e, n)).toSet
+ }
+
+ private def process_cfg_edge(edge: CFGEdge[G], ast_node: Node[G]): Set[AbstractState[G]] = ast_node match {
+ // TODO: Implement!
+ case Assign(target, value) => ???
+ case _ => ???
+ }
+}
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index d92f73d9d1..f614c6c4ab 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -1,8 +1,13 @@
package vct.rewrite.rasi
import vct.col.ast._
+import vct.col.origin.{LabelContext, Origin}
+import vct.rewrite.cfg.CFGEntry
+
+case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes: Map[AbstractProcess[G], CFGEntry[G]]) {
+ def successors(): Set[AbstractState[G]] =
+ processes.flatMap(p => p._1.get_next(p._2, this)).toSet
-case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int]) {
def resolve_integer_expression(expr: Expr[G]): Int = expr match {
case CIntegerValue(value) => value.intValue
case IntegerValue(value) => value.intValue
@@ -12,16 +17,75 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int]) {
case DerefPointer(pointer) => ???
case SizeOf(tname) => ???
case UMinus(arg) => -resolve_integer_expression(arg)
+ case BitNot(arg) => ~resolve_integer_expression(arg)
case AmbiguousMult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
case AmbiguousPlus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
case AmbiguousMinus(left, right) => resolve_integer_expression(left) - resolve_integer_expression(right)
case AmbiguousComputationalOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
case AmbiguousComputationalXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
case AmbiguousComputationalAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
- case
+ case ComputationalOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
+ case ComputationalXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
+ case ComputationalAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
+ case Exp(left, right) => scala.math.pow(resolve_integer_expression(left), resolve_integer_expression(right)).intValue()
+ case Plus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
+ case Minus(left, right) => resolve_integer_expression(left) - resolve_integer_expression(right)
+ case Mult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
+ case FloorDiv(left, right) => resolve_integer_expression(left) / resolve_integer_expression(right)
+ case Mod(left, right) => resolve_integer_expression(left) % resolve_integer_expression(right)
+ case BitAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
+ case BitOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
+ case BitXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
+ case BitShl(left, right) => resolve_integer_expression(left) << resolve_integer_expression(right)
+ case BitShr(left, right) => resolve_integer_expression(left) >> resolve_integer_expression(right)
+ case BitUShr(left, right) => resolve_integer_expression(left) >>> resolve_integer_expression(right)
+ case Select(cond, ift, iff) => {
+ if (resolve_boolean_expression(cond)) resolve_integer_expression(ift)
+ else resolve_integer_expression(iff)
+ }
+ case AmbiguousSubscript(collection, index) => ???
+ case SeqSubscript(seq, index) => ???
+ case ArraySubscript(arr, index) => ???
+ case PointerSubscript(pointer, index) => ???
+ case Length(arr) => ???
+ case Size(obj) => ???
+ }
+
+ def resolve_boolean_expression(expr: Expr[G]): Boolean = expr match {
+ case BooleanValue(value) => value
+ case Local(ref) => ???
+ case DerefHeapVariable(ref) => ???
+ case Deref(obj, ref) => ???
+ case DerefPointer(pointer) => ???
+ case Not(arg) => !resolve_boolean_expression(arg)
+ case AmbiguousOr(left, right) => resolve_boolean_expression(left) || resolve_boolean_expression(right)
+ case And(left, right) => resolve_boolean_expression(left) && resolve_boolean_expression(right)
+ case Or(left, right) => resolve_boolean_expression(left) || resolve_boolean_expression(right)
+ case Implies(left, right) => (!resolve_boolean_expression(left)) || resolve_boolean_expression(right)
+ case Eq(left, right) => resolve_integer_expression(left) == resolve_integer_expression(right)
+ case Neq(left, right) => resolve_integer_expression(left) != resolve_integer_expression(right)
+ case AmbiguousGreater(left, right) => resolve_integer_expression(left) > resolve_integer_expression(right)
+ case AmbiguousLess(left, right) => resolve_integer_expression(left) < resolve_integer_expression(right)
+ case AmbiguousGreaterEq(left, right) => resolve_integer_expression(left) >= resolve_integer_expression(right)
+ case AmbiguousLessEq(left, right) => resolve_integer_expression(left) <= resolve_integer_expression(right)
+ case Greater(left, right) => resolve_integer_expression(left) > resolve_integer_expression(right)
+ case Less(left, right) => resolve_integer_expression(left) < resolve_integer_expression(right)
+ case GreaterEq(left, right) => resolve_integer_expression(left) >= resolve_integer_expression(right)
+ case LessEq(left, right) => resolve_integer_expression(left) <= resolve_integer_expression(right)
+ case c: SetComparison[G] => ???
+ case Select(cond, ift, iff) => {
+ if (resolve_boolean_expression(cond)) resolve_boolean_expression(ift)
+ else resolve_boolean_expression(iff)
+ }
+ case AmbiguousSubscript(collection, index) => ???
+ case SeqSubscript(seq, index) => ???
+ case ArraySubscript(arr, index) => ???
+ case PointerSubscript(pointer, index) => ???
}
+ private def neutral_element: Expr[G] = BooleanValue(value = true)(Origin(Seq(LabelContext("neutral element for and"))))
def to_expression(): Expr[G] = {
- ???
+ valuations.map(v => Eq(v._1.to_expression(), IntegerValue(v._2)(Origin(Seq(LabelContext("int value")))))(Origin(Seq(LabelContext("valuation")))))
+ .fold(neutral_element)((e1, e2) => And(e1, e2)(e1.o))
}
}
diff --git a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
index 24594169d2..0186656907 100644
--- a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
+++ b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
@@ -8,10 +8,12 @@ trait ConcreteVariable[G] {
// TODO: Need to also consider the object!
case Deref(obj, ref) => ref.decl
}
+ def to_expression(): Expr[G]
}
case class FieldVariable[G](field: Field[G]) extends ConcreteVariable[G] {
override def is(expr: Expr[G]): Boolean = field.equals(extract_from_expression(expr))
+ override def to_expression(): Expr[G] = ???
}
case class IndexedVariable[G](field: Field[G], i: Int) extends ConcreteVariable[G] {
@@ -21,4 +23,5 @@ case class IndexedVariable[G](field: Field[G], i: Int) extends ConcreteVariable[
case ArraySubscript(arr, index) => field.equals(extract_from_expression(arr)) && i == Utils.resolve_integer_expression(index)
case PointerSubscript(pointer, index) => field.equals(extract_from_expression(pointer)) && i == Utils.resolve_integer_expression(index)
}
+ override def to_expression(): Expr[G] = ???
}
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index 82c65caf90..33ac489584 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -1,14 +1,33 @@
package vct.rewrite.rasi
import vct.col.ast.{BooleanValue, Expr, Or}
+import vct.col.origin.{LabelContext, Origin}
import vct.rewrite.cfg.CFGNode
+import scala.collection.mutable
+
case class RASIGenerator[G]() {
- private def neutral_element: Expr[G] = ???
+ private val found_states: mutable.Set[AbstractState[G]] = mutable.LinkedHashSet()
+ private val current_branches: mutable.Set[AbstractState[G]] = mutable.LinkedHashSet()
+
+ private def neutral_element: Expr[G] = BooleanValue(value = false)(Origin(Seq(LabelContext("neutral element of or"))))
+ def generate_rasi(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Expr[G] = {
+ explore(node, vars)
+ found_states.map(a => a.to_expression()).fold(neutral_element)((e1, e2) => Or(e1, e2)(e1.o))
+ }
- private def explore(node: CFGNode[G]): Set[AbstractState[G]] = ???
+ private def explore(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Unit = {
+ val initial_state = AbstractState(get_initial_values(vars), Map((AbstractProcess("main"), node)))
+ found_states.addOne(initial_state)
+ current_branches.addOne(initial_state)
- def generate_rasi(node: CFGNode[G]): Expr[G] = {
- explore(node).map(a => a.to_expression()).fold(neutral_element)((e1, e2) => Or(e1, e2)(e1.o))
+ while (current_branches.nonEmpty) {
+ val curr = current_branches.head
+ current_branches.remove(curr)
+
+ curr.successors().foreach(s => if (!found_states.contains(s)) {found_states.addOne(curr); current_branches.addOne(curr)})
+ }
}
+
+ private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], Int] = ???
}
From 0d22af47d9bb64ce8d0379efeb3c285607cc6bde Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 19 Feb 2024 11:14:12 +0100
Subject: [PATCH 29/85] Added uncertain values to RASI generator
---
.../vct/rewrite/rasi/AbstractProcess.scala | 3 +-
.../vct/rewrite/rasi/AbstractState.scala | 4 +--
.../vct/rewrite/rasi/UncertainValue.scala | 36 +++++++++++++++++++
3 files changed, 40 insertions(+), 3 deletions(-)
create mode 100644 src/rewrite/vct/rewrite/rasi/UncertainValue.scala
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index 8dc4e4bf30..f8f8110c65 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -6,7 +6,8 @@ import vct.rewrite.cfg.{CFGEdge, CFGEntry, CFGNode, CFGTerminal}
case class AbstractProcess[G](name: String) {
def get_next(node: CFGEntry[G], state: AbstractState[G]): Set[AbstractState[G]] = node match {
case CFGTerminal() => Set()
- case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression (e.condition.get)).flatMap (e => process_cfg_edge(e, n)).toSet
+ case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression(e.condition.get))
+ .flatMap (e => process_cfg_edge(e, n)).toSet
}
private def process_cfg_edge(edge: CFGEdge[G], ast_node: Node[G]): Set[AbstractState[G]] = ast_node match {
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index f614c6c4ab..97fe5ad157 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -51,8 +51,8 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
case Size(obj) => ???
}
- def resolve_boolean_expression(expr: Expr[G]): Boolean = expr match {
- case BooleanValue(value) => value
+ def resolve_boolean_expression(expr: Expr[G]): UncertainBooleanValue = expr match {
+ case BooleanValue(value) => UncertainBooleanValue(value, !value)
case Local(ref) => ???
case DerefHeapVariable(ref) => ???
case Deref(obj, ref) => ???
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
new file mode 100644
index 0000000000..5b19f535be
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -0,0 +1,36 @@
+package vct.rewrite.rasi
+
+trait UncertainValue {
+ def can_be_equal(other: UncertainValue): Boolean
+ def can_be_unequal(other: UncertainValue): Boolean
+}
+
+case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) extends UncertainValue {
+ override def can_be_equal(other: UncertainValue): Boolean = other match {
+ case UncertainBooleanValue(t, f) => can_be_true && t || can_be_false && f
+ case _ => false
+ }
+
+ override def can_be_unequal(other: UncertainValue): Boolean = other match {
+ case UncertainBooleanValue(t, f) => can_be_true && f || can_be_false && t
+ case _ => true
+ }
+
+ def &&(other: UncertainBooleanValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_true && other.can_be_true, can_be_false || other.can_be_false)
+
+ def ||(other: UncertainBooleanValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false && other.can_be_false)
+
+ def !(): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_false, can_be_true)
+
+ def ^(other: UncertainBooleanValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_true && other.can_be_false || can_be_false && other.can_be_true, can_be_true && other.can_be_true || can_be_false && other.can_be_false)
+}
+
+case class UncertainIntegerValue() extends UncertainValue {
+ override def can_be_equal(other: UncertainValue): Boolean = ???
+
+ override def can_be_unequal(other: UncertainValue): Boolean = ???
+}
From 8f212d148ad917b51784126dbd91081ee340de57 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 19 Feb 2024 11:25:38 +0100
Subject: [PATCH 30/85] Finished expression side effects for With and Then
---
src/rewrite/vct/rewrite/cfg/CFGGenerator.scala | 1 -
src/rewrite/vct/rewrite/cfg/Utils.scala | 11 ++++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 2e73b2b0a5..805e411ed3 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -39,7 +39,6 @@ case class CFGGenerator[G]() {
}
private def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = node match {
- // TODO: Can these be simplified using evaluate_first()?
case PVLBranch(_) => evaluate_first(context.enter_scope(node))
case PVLLoop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
// NonExecutableStatement
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index 8842d52ede..197849e05a 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -12,8 +12,8 @@ object Utils {
// Expressions causing side effects
case pae @ PreAssignExpression(target, value) => Seq(Assign(target, value)(pae.blame)(pae.o))
case pae @ PostAssignExpression(target, value) => Seq(Assign(target, value)(pae.blame)(pae.o))
- case With(pre, value) => ???
- case Then(value, post) => ???
+ case With(pre, value) => pre +: find_all_subexpressions(value)
+ case Then(value, post) => find_all_subexpressions(value) :+ post
case mi @ MethodInvocation(obj, ref, args, outArgs, typeArgs, givenMap, yields) =>
Seq(InvokeMethod(obj, ref, args, outArgs, typeArgs, givenMap, yields)(mi.blame)(mi.o))
case ci @ ConstructorInvocation(ref, args, outArgs, typeArgs, givenMap, yields) =>
@@ -35,19 +35,19 @@ object Utils {
case Implies(left, right) => {
val left_stmts: Seq[Statement[G]] = find_all_subexpressions(left)
val right_stmts: Seq[Statement[G]] = find_all_subexpressions(right)
- if (right_stmts.nonEmpty) left_stmts ++ Seq(Branch(Seq((left, Block(right_stmts)(right.o))))(expr.o))
+ if (right_stmts.nonEmpty) left_stmts :+ Branch(Seq((left, Block(right_stmts)(right.o))))(expr.o)
else left_stmts
}
case And(left, right) => {
val left_stmts: Seq[Statement[G]] = find_all_subexpressions(left)
val right_stmts: Seq[Statement[G]] = find_all_subexpressions(right)
- if (right_stmts.nonEmpty) left_stmts ++ Seq(Branch(Seq((left, Block(right_stmts)(right.o))))(expr.o))
+ if (right_stmts.nonEmpty) left_stmts :+ Branch(Seq((left, Block(right_stmts)(right.o))))(expr.o)
else left_stmts
}
case Or(left, right) => {
val left_stmts: Seq[Statement[G]] = find_all_subexpressions(left)
val right_stmts: Seq[Statement[G]] = find_all_subexpressions(right)
- if (right_stmts.nonEmpty) left_stmts ++ Seq(Branch(Seq((negate(left), Block(right_stmts)(right.o))))(expr.o))
+ if (right_stmts.nonEmpty) left_stmts :+ Branch(Seq((negate(left), Block(right_stmts)(right.o))))(expr.o)
else left_stmts
}
// General recursion case
@@ -57,6 +57,7 @@ object Utils {
private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = Local(new DirectRef[G, Variable[G]](new Variable(TClass(cls))(o)))(o)
def find_all_cases[G](body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = body match {
+ case Switch(_, _) => mutable.Set()
// Recursion on statements that can contain case statements
case Label(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
case Block(stmts) => mutable.LinkedHashSet.from(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
From e2e5cf0cdc2aa471af71ea1cd68af5981692aee2 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 19 Feb 2024 14:35:20 +0100
Subject: [PATCH 31/85] Started with uncertainty arithmetic; made compilable
---
.../vct/rewrite/rasi/AbstractProcess.scala | 2 +-
.../vct/rewrite/rasi/AbstractState.scala | 51 ++++----
.../vct/rewrite/rasi/ConcreteVariable.scala | 4 +-
src/rewrite/vct/rewrite/rasi/Interval.scala | 119 ++++++++++++++++++
.../vct/rewrite/rasi/RASIGenerator.scala | 3 +-
.../vct/rewrite/rasi/UncertainValue.scala | 59 ++++++++-
src/rewrite/vct/rewrite/rasi/Utils.scala | 8 +-
7 files changed, 209 insertions(+), 37 deletions(-)
create mode 100644 src/rewrite/vct/rewrite/rasi/Interval.scala
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index f8f8110c65..313d563db0 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -6,7 +6,7 @@ import vct.rewrite.cfg.{CFGEdge, CFGEntry, CFGNode, CFGTerminal}
case class AbstractProcess[G](name: String) {
def get_next(node: CFGEntry[G], state: AbstractState[G]): Set[AbstractState[G]] = node match {
case CFGTerminal() => Set()
- case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression(e.condition.get))
+ case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression(e.condition.get).can_be_true)
.flatMap (e => process_cfg_edge(e, n)).toSet
}
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index 97fe5ad157..f2a592bda0 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -4,44 +4,49 @@ import vct.col.ast._
import vct.col.origin.{LabelContext, Origin}
import vct.rewrite.cfg.CFGEntry
-case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes: Map[AbstractProcess[G], CFGEntry[G]]) {
+import scala.collection.immutable.HashMap
+
+case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes: HashMap[AbstractProcess[G], CFGEntry[G]]) {
def successors(): Set[AbstractState[G]] =
processes.flatMap(p => p._1.get_next(p._2, this)).toSet
- def resolve_integer_expression(expr: Expr[G]): Int = expr match {
- case CIntegerValue(value) => value.intValue
- case IntegerValue(value) => value.intValue
+ def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
+ case CIntegerValue(value) => UncertainIntegerValue.single(value.intValue)
+ case IntegerValue(value) => UncertainIntegerValue.single(value.intValue)
case Local(ref) => ???
case DerefHeapVariable(ref) => ???
case Deref(obj, ref) => ???
case DerefPointer(pointer) => ???
case SizeOf(tname) => ???
case UMinus(arg) => -resolve_integer_expression(arg)
- case BitNot(arg) => ~resolve_integer_expression(arg)
case AmbiguousMult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
case AmbiguousPlus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
case AmbiguousMinus(left, right) => resolve_integer_expression(left) - resolve_integer_expression(right)
- case AmbiguousComputationalOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
- case AmbiguousComputationalXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
- case AmbiguousComputationalAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
- case ComputationalOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
- case ComputationalXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
- case ComputationalAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
- case Exp(left, right) => scala.math.pow(resolve_integer_expression(left), resolve_integer_expression(right)).intValue()
+ case Exp(left, right) => resolve_integer_expression(left).pow(resolve_integer_expression(right))
case Plus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
case Minus(left, right) => resolve_integer_expression(left) - resolve_integer_expression(right)
case Mult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
case FloorDiv(left, right) => resolve_integer_expression(left) / resolve_integer_expression(right)
case Mod(left, right) => resolve_integer_expression(left) % resolve_integer_expression(right)
- case BitAnd(left, right) => resolve_integer_expression(left) & resolve_integer_expression(right)
- case BitOr(left, right) => resolve_integer_expression(left) | resolve_integer_expression(right)
- case BitXor(left, right) => resolve_integer_expression(left) ^ resolve_integer_expression(right)
- case BitShl(left, right) => resolve_integer_expression(left) << resolve_integer_expression(right)
- case BitShr(left, right) => resolve_integer_expression(left) >> resolve_integer_expression(right)
- case BitUShr(left, right) => resolve_integer_expression(left) >>> resolve_integer_expression(right)
+ // TODO: Support bit operations
+ case BitNot(arg) => ??? // ~resolve_integer_expression(arg)
+ case AmbiguousComputationalOr(left, right) => ??? // resolve_integer_expression(left) | resolve_integer_expression(right)
+ case AmbiguousComputationalXor(left, right) => ??? // resolve_integer_expression(left) ^ resolve_integer_expression(right)
+ case AmbiguousComputationalAnd(left, right) => ??? // resolve_integer_expression(left) & resolve_integer_expression(right)
+ case ComputationalOr(left, right) => ??? // resolve_integer_expression(left) | resolve_integer_expression(right)
+ case ComputationalXor(left, right) => ??? // resolve_integer_expression(left) ^ resolve_integer_expression(right)
+ case ComputationalAnd(left, right) => ??? // resolve_integer_expression(left) & resolve_integer_expression(right)
+ case BitAnd(left, right) => ??? // resolve_integer_expression(left) & resolve_integer_expression(right)
+ case BitOr(left, right) => ??? // resolve_integer_expression(left) | resolve_integer_expression(right)
+ case BitXor(left, right) => ??? // resolve_integer_expression(left) ^ resolve_integer_expression(right)
+ case BitShl(left, right) => ??? // resolve_integer_expression(left) << resolve_integer_expression(right)
+ case BitShr(left, right) => ??? // resolve_integer_expression(left) >> resolve_integer_expression(right)
+ case BitUShr(left, right) => ??? // resolve_integer_expression(left) >>> resolve_integer_expression(right)
case Select(cond, ift, iff) => {
- if (resolve_boolean_expression(cond)) resolve_integer_expression(ift)
- else resolve_integer_expression(iff)
+ var value: UncertainIntegerValue = UncertainIntegerValue.empty()
+ if (resolve_boolean_expression(cond).can_be_true) value = value.union(resolve_integer_expression(ift))
+ if (resolve_boolean_expression(cond).can_be_false) value = value.union(resolve_integer_expression(iff))
+ value
}
case AmbiguousSubscript(collection, index) => ???
case SeqSubscript(seq, index) => ???
@@ -74,8 +79,10 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
case LessEq(left, right) => resolve_integer_expression(left) <= resolve_integer_expression(right)
case c: SetComparison[G] => ???
case Select(cond, ift, iff) => {
- if (resolve_boolean_expression(cond)) resolve_boolean_expression(ift)
- else resolve_boolean_expression(iff)
+ var value: UncertainBooleanValue = UncertainBooleanValue(can_be_true = false, can_be_false = false)
+ if (resolve_boolean_expression(cond).can_be_true) value = value.union(resolve_boolean_expression(ift))
+ if (resolve_boolean_expression(cond).can_be_false) value = value.union(resolve_boolean_expression(iff))
+ value
}
case AmbiguousSubscript(collection, index) => ???
case SeqSubscript(seq, index) => ???
diff --git a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
index 0186656907..5b9a49f598 100644
--- a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
+++ b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
@@ -17,11 +17,11 @@ case class FieldVariable[G](field: Field[G]) extends ConcreteVariable[G] {
}
case class IndexedVariable[G](field: Field[G], i: Int) extends ConcreteVariable[G] {
- override def is(expr: Expr[G]): Boolean = expr match {
+ override def is(expr: Expr[G]): Boolean = ??? /*expr match {
case AmbiguousSubscript(collection, index) => field.equals(extract_from_expression(collection)) && i == Utils.resolve_integer_expression(index)
case SeqSubscript(seq, index) => field.equals(extract_from_expression(seq)) && i == Utils.resolve_integer_expression(index)
case ArraySubscript(arr, index) => field.equals(extract_from_expression(arr)) && i == Utils.resolve_integer_expression(index)
case PointerSubscript(pointer, index) => field.equals(extract_from_expression(pointer)) && i == Utils.resolve_integer_expression(index)
- }
+ }*/
override def to_expression(): Expr[G] = ???
}
diff --git a/src/rewrite/vct/rewrite/rasi/Interval.scala b/src/rewrite/vct/rewrite/rasi/Interval.scala
new file mode 100644
index 0000000000..c13e292db1
--- /dev/null
+++ b/src/rewrite/vct/rewrite/rasi/Interval.scala
@@ -0,0 +1,119 @@
+package vct.rewrite.rasi
+
+trait Interval {
+ def empty(): Boolean
+ def non_empty(): Boolean = !empty()
+ def intersection(other: Interval): Interval
+ def union(other: Interval): Interval
+ def complement(): Interval
+ def +(other: Interval): Interval
+ def -(other: Interval): Interval
+ def *(other: Interval): Interval
+ def /(other: Interval): Interval
+ def %(other: Interval): Interval
+ def unary_- : Interval
+ def pow(other:Interval): Interval
+ def sub_intervals(): Seq[Interval] = Seq(this)
+}
+
+case object EmptyInterval extends Interval {
+ override def empty(): Boolean = true
+ override def intersection(other: Interval): Interval = this
+ override def union(other: Interval): Interval = other
+ override def complement(): Interval = UnboundedInterval
+ override def +(other: Interval): Interval = this
+ override def -(other: Interval): Interval = this
+ override def *(other: Interval): Interval = this
+ override def /(other: Interval): Interval = this
+ override def %(other: Interval): Interval = this
+ override def unary_- : Interval = this
+ override def pow(other: Interval): Interval = this
+}
+
+case class MultiInterval(intervals: Seq[Interval]) extends Interval {
+ override def empty(): Boolean = intervals.isEmpty || intervals.forall(i => i.empty())
+ override def intersection(other: Interval): Interval = MultiInterval(intervals.map(i => i.intersection(other)))
+ override def union(other: Interval): Interval = ???
+ override def complement(): Interval = ???
+ override def +(other: Interval): Interval = ???
+ override def -(other: Interval): Interval = ???
+ override def *(other: Interval): Interval = ???
+ override def /(other: Interval): Interval = ???
+ override def %(other: Interval): Interval = ???
+ override def unary_- : Interval = MultiInterval(intervals.map(i => -i))
+ override def pow(other: Interval): Interval = ???
+ override def sub_intervals(): Seq[Interval] = intervals.flatMap(i => i.sub_intervals())
+}
+
+case class BoundedInterval(lower: Int, upper: Int) extends Interval {
+ override def empty(): Boolean = lower > upper
+ override def intersection(other: Interval): Interval = ???
+ override def union(other: Interval): Interval = ???
+ override def complement(): Interval =
+ MultiInterval(Seq(UpperBoundedInterval(lower - 1), LowerBoundedInterval(upper + 1)))
+ override def +(other: Interval): Interval = ???
+ override def -(other: Interval): Interval = ???
+ override def *(other: Interval): Interval = ???
+ override def /(other: Interval): Interval = ???
+ override def %(other: Interval): Interval = ???
+ override def unary_- : Interval = BoundedInterval(-upper, -lower)
+ override def pow(other: Interval): Interval = ???
+}
+
+case class LowerBoundedInterval(lower: Int) extends Interval {
+ override def empty(): Boolean = false
+ override def intersection(other: Interval): Interval = ???
+ override def union(other: Interval): Interval = ???
+ override def complement(): Interval = UpperBoundedInterval(lower - 1)
+ override def +(other: Interval): Interval = ???
+ override def -(other: Interval): Interval = ???
+ override def *(other: Interval): Interval = ???
+ override def /(other: Interval): Interval = ???
+ override def %(other: Interval): Interval = ???
+ override def unary_- : Interval = UpperBoundedInterval(-lower)
+ override def pow(other: Interval): Interval = ???
+}
+
+case class UpperBoundedInterval(upper: Int) extends Interval {
+ override def empty(): Boolean = false
+ override def intersection(other: Interval): Interval = ???
+ override def union(other: Interval): Interval = ???
+ override def complement(): Interval = LowerBoundedInterval(upper + 1)
+ override def +(other: Interval): Interval = ???
+ override def -(other: Interval): Interval = ???
+ override def *(other: Interval): Interval = ???
+ override def /(other: Interval): Interval = ???
+ override def %(other: Interval): Interval = ???
+ override def unary_- : Interval = LowerBoundedInterval(-upper)
+ override def pow(other: Interval): Interval = ???
+}
+
+case object UnboundedInterval extends Interval {
+ override def empty(): Boolean = false
+ override def intersection(other: Interval): Interval = other
+ override def union(other: Interval): Interval = this
+ override def complement(): Interval = EmptyInterval
+ override def +(other: Interval): Interval = this
+ override def -(other: Interval): Interval = this
+ override def *(other: Interval): Interval = this
+ override def /(other: Interval): Interval = this
+ override def %(other: Interval): Interval = other match {
+ case UnboundedInterval | EmptyInterval => other
+ case mi: MultiInterval => {
+ val intvs = mi.sub_intervals()
+ if (intvs.collect{case LowerBoundedInterval(_) | UpperBoundedInterval(_) | UnboundedInterval => 0}.nonEmpty)
+ return this
+ val max = intvs.map{case EmptyInterval => 0; case BoundedInterval(lower, upper) => Utils.absmax(lower, upper)}.max - 1
+ if (max <= 0) EmptyInterval
+ else BoundedInterval(-max, max)
+ }
+ case BoundedInterval(lower, upper) => {
+ val max = Utils.absmax(lower, upper) - 1
+ BoundedInterval(-max, max)
+ }
+ case LowerBoundedInterval(_) => this
+ case UpperBoundedInterval(_) => this
+ }
+ override def unary_- : Interval = this
+ override def pow(other: Interval): Interval = this
+}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index 33ac489584..c00f457c31 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -4,6 +4,7 @@ import vct.col.ast.{BooleanValue, Expr, Or}
import vct.col.origin.{LabelContext, Origin}
import vct.rewrite.cfg.CFGNode
+import scala.collection.immutable.HashMap
import scala.collection.mutable
case class RASIGenerator[G]() {
@@ -17,7 +18,7 @@ case class RASIGenerator[G]() {
}
private def explore(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Unit = {
- val initial_state = AbstractState(get_initial_values(vars), Map((AbstractProcess("main"), node)))
+ val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G]("main"), node)))
found_states.addOne(initial_state)
current_branches.addOne(initial_state)
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index 5b19f535be..58de293393 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -16,21 +16,72 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
case _ => true
}
+ def union(other: UncertainBooleanValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false || other.can_be_false)
+
+ def &(other: UncertainBooleanValue): UncertainBooleanValue = this && other
+
def &&(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true && other.can_be_true, can_be_false || other.can_be_false)
+ def |(other: UncertainBooleanValue): UncertainBooleanValue = this || other
+
def ||(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false && other.can_be_false)
- def !(): UncertainBooleanValue =
+ def unary_! : UncertainBooleanValue =
UncertainBooleanValue(can_be_false, can_be_true)
def ^(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true && other.can_be_false || can_be_false && other.can_be_true, can_be_true && other.can_be_true || can_be_false && other.can_be_false)
+
+ def ==(other: UncertainBooleanValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_true && other.can_be_true || can_be_false && other.can_be_false, can_be_true && other.can_be_false || can_be_false && other.can_be_true)
+
+ def !=(other: UncertainBooleanValue): UncertainBooleanValue = this ^ other
}
-case class UncertainIntegerValue() extends UncertainValue {
- override def can_be_equal(other: UncertainValue): Boolean = ???
+case class UncertainIntegerValue(value: Interval) extends UncertainValue {
+ override def can_be_equal(other: UncertainValue): Boolean = other match {
+ case UncertainIntegerValue(v) => value.intersection(v).non_empty()
+ case _ => false
+ }
+
+ override def can_be_unequal(other: UncertainValue): Boolean = other match {
+ case UncertainIntegerValue(v) => value.intersection(v).complement().non_empty()
+ case _ => true
+ }
+
+ def union(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value.union(other.value))
+
+ def ==(other: UncertainIntegerValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_equal(other), can_be_unequal(other))
+ def !=(other: UncertainIntegerValue): UncertainBooleanValue =
+ UncertainBooleanValue(can_be_unequal(other), can_be_equal(other))
+ def >=(other: UncertainIntegerValue): UncertainBooleanValue = ???
+ def <=(other: UncertainIntegerValue): UncertainBooleanValue = ???
+ def >(other: UncertainIntegerValue): UncertainBooleanValue = ???
+ def <(other: UncertainIntegerValue): UncertainBooleanValue = ???
- override def can_be_unequal(other: UncertainValue): Boolean = ???
+ def unary_- : UncertainIntegerValue =
+ UncertainIntegerValue(-value)
+ def +(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value + other.value)
+ def -(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value - other.value)
+ def *(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value * other.value)
+ def /(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value / other.value)
+ def %(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value % other.value)
+ def pow(other: UncertainIntegerValue): UncertainIntegerValue =
+ UncertainIntegerValue(value.pow(other.value))
}
+case object UncertainIntegerValue {
+ def empty(): UncertainIntegerValue = UncertainIntegerValue(EmptyInterval)
+ def uncertain(): UncertainIntegerValue = UncertainIntegerValue(UnboundedInterval)
+ def above(int: Int): UncertainIntegerValue = UncertainIntegerValue(LowerBoundedInterval(int))
+ def single(int: Int): UncertainIntegerValue = UncertainIntegerValue(BoundedInterval(int, int))
+}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/rasi/Utils.scala b/src/rewrite/vct/rewrite/rasi/Utils.scala
index 7726531fdf..73b203c984 100644
--- a/src/rewrite/vct/rewrite/rasi/Utils.scala
+++ b/src/rewrite/vct/rewrite/rasi/Utils.scala
@@ -1,11 +1,5 @@
package vct.rewrite.rasi
-import vct.col.ast._
-
case object Utils {
- def resolve_integer_expression[G](expr: Expr[G]): Int = expr match {
- case IntegerValue(value) => value.intValue
- case Plus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
-
- }
+ def absmax(a: Int, b: Int): Int = Seq(-a, a, -b, b).max
}
From e789ad9ee3e43e5755c3f96405931a2130e735a5 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 19 Feb 2024 16:44:33 +0100
Subject: [PATCH 32/85] Made options for index switches more explicit
---
src/rewrite/vct/rewrite/cfg/Index.scala | 176 +++++++++++++-----------
1 file changed, 93 insertions(+), 83 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 7011b47325..03a74434c6 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -5,6 +5,16 @@ import vct.col.ref.Ref
import scala.collection.mutable
+sealed abstract class NextIndex[G] {
+ def get: Index[G]
+}
+case class Step[G](value: Index[G]) extends NextIndex[G] {
+ override def get: Index[G] = value
+}
+case class Outgoing[G]() extends NextIndex[G] {
+ override def get: Index[G] = throw new NoSuchElementException("Trying to access outgoing edge")
+}
+
case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
override def equals(obj: scala.Any): Boolean = obj match {
@@ -17,12 +27,12 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
def make_step(): mutable.Set[(GlobalIndex[G], Option[Expr[G]])] = {
if (indices.isEmpty) return mutable.Set((this, None))
- val steps: Set[(Option[Index[G]], Option[Expr[G]])] = indices.head.make_step()
+ val steps: Set[(NextIndex[G], Option[Expr[G]])] = indices.head.make_step()
val res = mutable.Set[(GlobalIndex[G], Option[Expr[G]])]()
for (step <- steps) {
step match {
- case (Some(index), cond) => res.addOne((GlobalIndex(index +: indices.tail), cond))
- case (None, cond) => res.addAll(GlobalIndex(indices.tail).make_step().map(tup => (tup._1, Utils.and(tup._2, cond))))
+ case (Step(index), cond) => res.addOne((GlobalIndex(index +: indices.tail), cond))
+ case (Outgoing, cond) => res.addAll(GlobalIndex(indices.tail).make_step().map(tup => (tup._1, Utils.and(tup._2, cond))))
}
}
res
@@ -97,7 +107,7 @@ sealed trait Index[G] {
*
* @return A set of all steps possible from the current index, along with conditions for taking them
*/
- def make_step(): Set[(Option[Index[G]], Option[Expr[G]])]
+ def make_step(): Set[(NextIndex[G], Option[Expr[G]])]
/**
* Returns the statement that corresponds to the current index.
@@ -157,7 +167,7 @@ object Index {
}
case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = instance_method.body.get
override def equals(obj: scala.Any): Boolean = obj match {
case InitialIndex(m) => m.equals(instance_method)
@@ -166,7 +176,7 @@ case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G]
}
case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = run_method.body.get
override def has_statement(): Boolean = run_method.body.nonEmpty
override def equals(obj: scala.Any): Boolean = obj match {
@@ -176,9 +186,9 @@ case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
}
case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
- if (index == 0) Set((Some(ExpressionContainerIndex(statement, 1)), None))
- else Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
+ if (index == 0) Set((Step(ExpressionContainerIndex(statement, 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = statement
override def equals(obj: scala.Any): Boolean = obj match {
@@ -188,9 +198,9 @@ case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) exte
}
case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
- if (index < 2) Set((Some(AssignmentIndex(assign, index + 1)), None))
- else Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
+ if (index < 2) Set((Step(AssignmentIndex(assign, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(assign.target)(assign.target.o)
@@ -204,15 +214,15 @@ case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
}
case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
// Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
if (index % 2 == 0 && index < 2 * (pvl_branch.branches.size - 1))
- Set((Some(PVLBranchIndex(pvl_branch, index + 2)), Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))),
- (Some(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)))
+ Set((Step(PVLBranchIndex(pvl_branch, index + 2)), Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))),
+ (Step(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)))
else if (index == 2 * (pvl_branch.branches.size - 1))
- Set((Some(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)),
- (None, Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))))
- else Set((None, None))
+ Set((Step(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)),
+ (Outgoing, Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(pvl_branch.branches.apply(index / 2)._1)(pvl_branch.branches.apply(index / 2)._1.o)
@@ -225,11 +235,11 @@ case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index
}
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = index match {
- case 0 => Set((Some(PVLLoopIndex(pvl_loop, 1)), None))
- case 1 => Set((Some(PVLLoopIndex(pvl_loop, 2)), Some(pvl_loop.cond)), (None, Some(Utils.negate(pvl_loop.cond))))
- case 2 => Set((Some(PVLLoopIndex(pvl_loop, 3)), None))
- case 3 => Set((Some(PVLLoopIndex(pvl_loop, 1)), None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = index match {
+ case 0 => Set((Step(PVLLoopIndex(pvl_loop, 1)), None))
+ case 1 => Set((Step(PVLLoopIndex(pvl_loop, 2)), Some(pvl_loop.cond)), (Outgoing, Some(Utils.negate(pvl_loop.cond))))
+ case 2 => Set((Step(PVLLoopIndex(pvl_loop, 3)), None))
+ case 3 => Set((Step(PVLLoopIndex(pvl_loop, 1)), None))
}
override def resolve(): Statement[G] = index match {
case 0 => pvl_loop.init
@@ -244,7 +254,7 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
}
case class LabelIndex[G](label: Label[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = label.stat
override def equals(obj: scala.Any): Boolean = obj match {
case LabelIndex(l) => l.equals(label)
@@ -253,9 +263,9 @@ case class LabelIndex[G](label: Label[G]) extends Index[G] {
}
case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
- if (index < 2) Set((Some(FramedProofIndex(framed_proof, index + 1)), None))
- else Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
+ if (index < 2) Set((Step(FramedProofIndex(framed_proof, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(framed_proof.pre)(framed_proof.pre.o)
@@ -269,7 +279,7 @@ case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends
}
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = extract.contractedStatement
override def equals(obj: scala.Any): Boolean = obj match {
case ExtractIndex(e) => e.equals(extract)
@@ -279,10 +289,10 @@ case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement[G]]) extends Index[G] {
def this(eval: Eval[G], index: Int) = this(eval, index, Utils.find_all_subexpressions(eval.expr))
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
// TODO: Consider conditions in expressions, too
- if (index < subexpressions.size - 1) Set((Some(EvalIndex(eval, index + 1)), None))
- else Set((None, None))
+ if (index < subexpressions.size - 1) Set((Step(EvalIndex(eval, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = subexpressions.apply(index)
override def has_statement(): Boolean = subexpressions.nonEmpty
@@ -302,15 +312,15 @@ case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index:
// 3. outArgs
// 4. yields
// 5. procedure body
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
val args: Seq[Expr[G]] = invoke_procedure.args
val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_procedure.givenMap
val outArgs: Seq[Expr[G]] = invoke_procedure.outArgs
val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_procedure.yields
if (index < args.size + givenMap.size + outArgs.size + yields.size - 1 ||
index == args.size + givenMap.size + outArgs.size + yields.size - 1 && invoke_procedure.ref.decl.body.nonEmpty)
- Set((Some(InvokeProcedureIndex(invoke_procedure, index + 1)), None))
- else Set((None, None))
+ Set((Step(InvokeProcedureIndex(invoke_procedure, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_procedure.args
@@ -351,15 +361,15 @@ case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], i
// 4. yields
// 5. out
// 6. constructor body
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
val args: Seq[Expr[G]] = invoke_constructor.args
val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_constructor.givenMap
val outArgs: Seq[Expr[G]] = invoke_constructor.outArgs
val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_constructor.yields
if (index < args.size + givenMap.size + outArgs.size + yields.size ||
index == args.size + givenMap.size + outArgs.size + yields.size && invoke_constructor.ref.decl.body.nonEmpty)
- Set((Some(InvokeConstructorIndex(invoke_constructor, index + 1)), None))
- else Set((None, None))
+ Set((Step(InvokeConstructorIndex(invoke_constructor, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_constructor.args
@@ -396,15 +406,15 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
// 4. outArgs
// 5. yields
// 6. method body
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
val args: Seq[Expr[G]] = invoke_method.args
val givenMap: Seq[(Ref[G, Variable[G]], Expr[G])] = invoke_method.givenMap
val outArgs: Seq[Expr[G]] = invoke_method.outArgs
val yields: Seq[(Expr[G], Ref[G, Variable[G]])] = invoke_method.yields
if (index < args.size + givenMap.size + outArgs.size + yields.size ||
index == args.size + givenMap.size + outArgs.size + yields.size && invoke_method.ref.decl.body.nonEmpty)
- Set((Some(InvokeMethodIndex(invoke_method, index + 1)), None))
- else Set((None, None))
+ Set((Step(InvokeMethodIndex(invoke_method, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_method.args
@@ -434,9 +444,9 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
}
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
- if (index < block.statements.size - 1) Set((Some(BlockIndex(block, index + 1)), None))
- else Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
+ if (index < block.statements.size - 1) Set((Step(BlockIndex(block, index + 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = block.statements.apply(index)
override def equals(obj: scala.Any): Boolean = obj match {
@@ -446,7 +456,7 @@ case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
}
case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = scope.body
override def equals(obj: scala.Any): Boolean = obj match {
case ScopeIndex(s) => s.equals(scope)
@@ -455,15 +465,15 @@ case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
}
case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
// Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
if (index % 2 == 0 && index < 2 * (branch.branches.size - 1))
- Set((Some(BranchIndex(branch, index + 2)), Some(Utils.negate(branch.branches.apply(index / 2)._1))),
- (Some(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)))
+ Set((Step(BranchIndex(branch, index + 2)), Some(Utils.negate(branch.branches.apply(index / 2)._1))),
+ (Step(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)))
else if (index == 2 * (branch.branches.size - 1))
- Set((Some(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)),
- (None, Some(Utils.negate(branch.branches.apply(index / 2)._1))))
- else Set((None, None))
+ Set((Step(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)),
+ (Outgoing, Some(Utils.negate(branch.branches.apply(index / 2)._1))))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(branch.branches.apply(index / 2)._1)(branch.branches.apply(index / 2)._1.o)
@@ -476,7 +486,7 @@ case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
}
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
override def equals(obj: scala.Any): Boolean = obj match {
case IndetBranchIndex(b, i) => i == index && b.equals(indet_branch)
@@ -486,7 +496,7 @@ case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends
// TODO: Switch cases could be multiple context indices deep; this does not work with the single index for make_step()
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = switch.body
override def equals(obj: scala.Any): Boolean = obj match {
case SwitchIndex(s) => s.equals(switch)
@@ -495,11 +505,11 @@ case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
}
case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = index match {
- case 0 => Set((Some(LoopIndex(loop, 1)), None))
- case 1 => Set((Some(LoopIndex(loop, 2)), Some(loop.cond)), (None, Some(Utils.negate(loop.cond))))
- case 2 => Set((Some(LoopIndex(loop, 3)), None))
- case 3 => Set((Some(LoopIndex(loop, 1)), None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = index match {
+ case 0 => Set((Step(LoopIndex(loop, 1)), None))
+ case 1 => Set((Step(LoopIndex(loop, 2)), Some(loop.cond)), (Outgoing, Some(Utils.negate(loop.cond))))
+ case 2 => Set((Step(LoopIndex(loop, 3)), None))
+ case 3 => Set((Step(LoopIndex(loop, 1)), None))
}
override def resolve(): Statement[G] = index match {
case 0 => loop.init
@@ -514,7 +524,7 @@ case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
}
case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((Some(this), None), (None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing, None))
override def resolve(): Statement[G] = ranged_for.body
override def equals(obj: scala.Any): Boolean = obj match {
case RangedForIndex(r) => r.equals(ranged_for)
@@ -523,9 +533,9 @@ case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
}
case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
- if (index != 1) Set((Some(TryCatchFinallyIndex(try_catch_finally, 1)), None))
- else Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
+ if (index != 1) Set((Step(TryCatchFinallyIndex(try_catch_finally, 1)), None))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = index match {
case 0 => try_catch_finally.body
@@ -539,7 +549,7 @@ case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index:
}
case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = synchronized.body
override def equals(obj: scala.Any): Boolean = obj match {
case SynchronizedIndex(s) => s.equals(synchronized)
@@ -548,7 +558,7 @@ case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G]
}
case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = par_invariant.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParInvariantIndex(p) => p.equals(par_invariant)
@@ -557,7 +567,7 @@ case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G]
}
case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = par_atomic.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParAtomicIndex(p) => p.equals(par_atomic)
@@ -566,7 +576,7 @@ case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
}
case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = par_barrier.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParBarrierIndex(p) => p.equals(par_barrier)
@@ -575,7 +585,7 @@ case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
}
case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = vec_block.content
override def equals(obj: scala.Any): Boolean = obj match {
case VecBlockIndex(v) => v.equals(vec_block)
@@ -584,7 +594,7 @@ case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
}
case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = wand_package.proof
override def equals(obj: scala.Any): Boolean = obj match {
case WandPackageIndex(w) => w.equals(wand_package)
@@ -593,7 +603,7 @@ case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
}
case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = model_do.impl
override def equals(obj: scala.Any): Boolean = obj match {
case ModelDoIndex(m) => m.equals(model_do)
@@ -602,7 +612,7 @@ case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
}
case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = cpp_lifetime_scope.body
override def equals(obj: scala.Any): Boolean = obj match {
case CPPLifetimeScopeIndex(c) => c.equals(cpp_lifetime_scope)
@@ -611,15 +621,15 @@ case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) ext
}
case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = {
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
// Indices 0, 2, 4, ... are the conditions, indices 1, 3, 5, ... are the branch bodies
if (index % 2 == 0 && index < 2 * (unresolved_seq_branch.branches.size - 1))
- Set((Some(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 2)), Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))),
- (Some(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)))
+ Set((Step(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 2)), Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))),
+ (Step(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)))
else if (index == 2 * (unresolved_seq_branch.branches.size - 1))
- Set((Some(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)),
- (None, Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))))
- else Set((None, None))
+ Set((Step(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)),
+ (Outgoing, Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))))
+ else Set((Outgoing, None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(unresolved_seq_branch.branches.apply(index / 2)._1)(unresolved_seq_branch.branches.apply(index / 2)._1.o)
@@ -632,10 +642,10 @@ case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranc
}
case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = index match {
- case 0 => Set((Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1)), Some(unresolved_seq_loop.cond)),
- (None, Some(Utils.negate(unresolved_seq_loop.cond))))
- case 1 => Set((Some(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0)), None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = index match {
+ case 0 => Set((Step(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1)), Some(unresolved_seq_loop.cond)),
+ (Outgoing, Some(Utils.negate(unresolved_seq_loop.cond))))
+ case 1 => Set((Step(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0)), None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(unresolved_seq_loop.cond)(unresolved_seq_loop.cond.o)
@@ -648,7 +658,7 @@ case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G],
}
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = index match {
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
@@ -660,7 +670,7 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((Some(this), None), (None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing, None))
override def resolve(): Statement[G] = seq_loop.body
override def equals(obj: scala.Any): Boolean = obj match {
case SeqLoopIndex(s) => s.equals(seq_loop)
@@ -669,7 +679,7 @@ case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
}
case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = veymont_assign_expression.assign
override def equals(obj: scala.Any): Boolean = obj match {
case VeyMontAssignExpressionIndex(v) => v.equals(veymont_assign_expression)
@@ -678,7 +688,7 @@ case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAss
}
case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
- override def make_step(): Set[(Option[Index[G]], Option[Expr[G]])] = Set((None, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
override def resolve(): Statement[G] = communicatex.assign
override def equals(obj: scala.Any): Boolean = obj match {
case CommunicateXIndex(c) => c.equals(communicatex)
From 465798cfcab0d9aeba4c9bc3ba6e20549b9b884e Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 19 Feb 2024 16:52:08 +0100
Subject: [PATCH 33/85] Syntax fixes
---
src/rewrite/vct/rewrite/cfg/Index.scala | 78 ++++++++++++-------------
1 file changed, 39 insertions(+), 39 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 03a74434c6..f4a0108be4 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -32,7 +32,7 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
for (step <- steps) {
step match {
case (Step(index), cond) => res.addOne((GlobalIndex(index +: indices.tail), cond))
- case (Outgoing, cond) => res.addAll(GlobalIndex(indices.tail).make_step().map(tup => (tup._1, Utils.and(tup._2, cond))))
+ case (Outgoing(), cond) => res.addAll(GlobalIndex(indices.tail).make_step().map(tup => (tup._1, Utils.and(tup._2, cond))))
}
}
res
@@ -167,7 +167,7 @@ object Index {
}
case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = instance_method.body.get
override def equals(obj: scala.Any): Boolean = obj match {
case InitialIndex(m) => m.equals(instance_method)
@@ -176,7 +176,7 @@ case class InitialIndex[G](instance_method: InstanceMethod[G]) extends Index[G]
}
case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = run_method.body.get
override def has_statement(): Boolean = run_method.body.nonEmpty
override def equals(obj: scala.Any): Boolean = obj match {
@@ -188,7 +188,7 @@ case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
if (index == 0) Set((Step(ExpressionContainerIndex(statement, 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = statement
override def equals(obj: scala.Any): Boolean = obj match {
@@ -200,7 +200,7 @@ case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) exte
case class AssignmentIndex[G](assign: Assign[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
if (index < 2) Set((Step(AssignmentIndex(assign, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(assign.target)(assign.target.o)
@@ -221,8 +221,8 @@ case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index
(Step(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)))
else if (index == 2 * (pvl_branch.branches.size - 1))
Set((Step(PVLBranchIndex(pvl_branch, index + 1)), Some(pvl_branch.branches.apply(index / 2)._1)),
- (Outgoing, Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))))
- else Set((Outgoing, None))
+ (Outgoing(), Some(Utils.negate(pvl_branch.branches.apply(index / 2)._1))))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(pvl_branch.branches.apply(index / 2)._1)(pvl_branch.branches.apply(index / 2)._1.o)
@@ -237,7 +237,7 @@ case class PVLBranchIndex[G](pvl_branch: PVLBranch[G], index: Int) extends Index
case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = index match {
case 0 => Set((Step(PVLLoopIndex(pvl_loop, 1)), None))
- case 1 => Set((Step(PVLLoopIndex(pvl_loop, 2)), Some(pvl_loop.cond)), (Outgoing, Some(Utils.negate(pvl_loop.cond))))
+ case 1 => Set((Step(PVLLoopIndex(pvl_loop, 2)), Some(pvl_loop.cond)), (Outgoing(), Some(Utils.negate(pvl_loop.cond))))
case 2 => Set((Step(PVLLoopIndex(pvl_loop, 3)), None))
case 3 => Set((Step(PVLLoopIndex(pvl_loop, 1)), None))
}
@@ -254,7 +254,7 @@ case class PVLLoopIndex[G](pvl_loop: PVLLoop[G], index: Int) extends Index[G] {
}
case class LabelIndex[G](label: Label[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = label.stat
override def equals(obj: scala.Any): Boolean = obj match {
case LabelIndex(l) => l.equals(label)
@@ -265,7 +265,7 @@ case class LabelIndex[G](label: Label[G]) extends Index[G] {
case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
if (index < 2) Set((Step(FramedProofIndex(framed_proof, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = index match {
case 0 => Eval(framed_proof.pre)(framed_proof.pre.o)
@@ -279,7 +279,7 @@ case class FramedProofIndex[G](framed_proof: FramedProof[G], index: Int) extends
}
case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = extract.contractedStatement
override def equals(obj: scala.Any): Boolean = obj match {
case ExtractIndex(e) => e.equals(extract)
@@ -292,7 +292,7 @@ case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
// TODO: Consider conditions in expressions, too
if (index < subexpressions.size - 1) Set((Step(EvalIndex(eval, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = subexpressions.apply(index)
override def has_statement(): Boolean = subexpressions.nonEmpty
@@ -320,7 +320,7 @@ case class InvokeProcedureIndex[G](invoke_procedure: InvokeProcedure[G], index:
if (index < args.size + givenMap.size + outArgs.size + yields.size - 1 ||
index == args.size + givenMap.size + outArgs.size + yields.size - 1 && invoke_procedure.ref.decl.body.nonEmpty)
Set((Step(InvokeProcedureIndex(invoke_procedure, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_procedure.args
@@ -369,7 +369,7 @@ case class InvokeConstructorIndex[G](invoke_constructor: InvokeConstructor[G], i
if (index < args.size + givenMap.size + outArgs.size + yields.size ||
index == args.size + givenMap.size + outArgs.size + yields.size && invoke_constructor.ref.decl.body.nonEmpty)
Set((Step(InvokeConstructorIndex(invoke_constructor, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_constructor.args
@@ -414,7 +414,7 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
if (index < args.size + givenMap.size + outArgs.size + yields.size ||
index == args.size + givenMap.size + outArgs.size + yields.size && invoke_method.ref.decl.body.nonEmpty)
Set((Step(InvokeMethodIndex(invoke_method, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = {
val args: Seq[Expr[G]] = invoke_method.args
@@ -446,7 +446,7 @@ case class InvokeMethodIndex[G](invoke_method: InvokeMethod[G], index: Int) exte
case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
if (index < block.statements.size - 1) Set((Step(BlockIndex(block, index + 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = block.statements.apply(index)
override def equals(obj: scala.Any): Boolean = obj match {
@@ -456,7 +456,7 @@ case class BlockIndex[G](block: Block[G], index: Int) extends Index[G] {
}
case class ScopeIndex[G](scope: Scope[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = scope.body
override def equals(obj: scala.Any): Boolean = obj match {
case ScopeIndex(s) => s.equals(scope)
@@ -472,8 +472,8 @@ case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
(Step(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)))
else if (index == 2 * (branch.branches.size - 1))
Set((Step(BranchIndex(branch, index + 1)), Some(branch.branches.apply(index / 2)._1)),
- (Outgoing, Some(Utils.negate(branch.branches.apply(index / 2)._1))))
- else Set((Outgoing, None))
+ (Outgoing(), Some(Utils.negate(branch.branches.apply(index / 2)._1))))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(branch.branches.apply(index / 2)._1)(branch.branches.apply(index / 2)._1.o)
@@ -486,7 +486,7 @@ case class BranchIndex[G](branch: Branch[G], index: Int) extends Index[G] {
}
case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = indet_branch.branches.apply(index)
override def equals(obj: scala.Any): Boolean = obj match {
case IndetBranchIndex(b, i) => i == index && b.equals(indet_branch)
@@ -496,7 +496,7 @@ case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends
// TODO: Switch cases could be multiple context indices deep; this does not work with the single index for make_step()
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = switch.body
override def equals(obj: scala.Any): Boolean = obj match {
case SwitchIndex(s) => s.equals(switch)
@@ -507,7 +507,7 @@ case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = index match {
case 0 => Set((Step(LoopIndex(loop, 1)), None))
- case 1 => Set((Step(LoopIndex(loop, 2)), Some(loop.cond)), (Outgoing, Some(Utils.negate(loop.cond))))
+ case 1 => Set((Step(LoopIndex(loop, 2)), Some(loop.cond)), (Outgoing(), Some(Utils.negate(loop.cond))))
case 2 => Set((Step(LoopIndex(loop, 3)), None))
case 3 => Set((Step(LoopIndex(loop, 1)), None))
}
@@ -524,7 +524,7 @@ case class LoopIndex[G](loop: Loop[G], index: Int) extends Index[G] {
}
case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing(), None))
override def resolve(): Statement[G] = ranged_for.body
override def equals(obj: scala.Any): Boolean = obj match {
case RangedForIndex(r) => r.equals(ranged_for)
@@ -535,7 +535,7 @@ case class RangedForIndex[G](ranged_for: RangedFor[G]) extends Index[G] {
case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
if (index != 1) Set((Step(TryCatchFinallyIndex(try_catch_finally, 1)), None))
- else Set((Outgoing, None))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = index match {
case 0 => try_catch_finally.body
@@ -549,7 +549,7 @@ case class TryCatchFinallyIndex[G](try_catch_finally: TryCatchFinally[G], index:
}
case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = synchronized.body
override def equals(obj: scala.Any): Boolean = obj match {
case SynchronizedIndex(s) => s.equals(synchronized)
@@ -558,7 +558,7 @@ case class SynchronizedIndex[G](synchronized: Synchronized[G]) extends Index[G]
}
case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = par_invariant.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParInvariantIndex(p) => p.equals(par_invariant)
@@ -567,7 +567,7 @@ case class ParInvariantIndex[G](par_invariant: ParInvariant[G]) extends Index[G]
}
case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = par_atomic.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParAtomicIndex(p) => p.equals(par_atomic)
@@ -576,7 +576,7 @@ case class ParAtomicIndex[G](par_atomic: ParAtomic[G]) extends Index[G] {
}
case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = par_barrier.content
override def equals(obj: scala.Any): Boolean = obj match {
case ParBarrierIndex(p) => p.equals(par_barrier)
@@ -585,7 +585,7 @@ case class ParBarrierIndex[G](par_barrier: ParBarrier[G]) extends Index[G] {
}
case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = vec_block.content
override def equals(obj: scala.Any): Boolean = obj match {
case VecBlockIndex(v) => v.equals(vec_block)
@@ -594,7 +594,7 @@ case class VecBlockIndex[G](vec_block: VecBlock[G]) extends Index[G] {
}
case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = wand_package.proof
override def equals(obj: scala.Any): Boolean = obj match {
case WandPackageIndex(w) => w.equals(wand_package)
@@ -603,7 +603,7 @@ case class WandPackageIndex[G](wand_package: WandPackage[G]) extends Index[G] {
}
case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = model_do.impl
override def equals(obj: scala.Any): Boolean = obj match {
case ModelDoIndex(m) => m.equals(model_do)
@@ -612,7 +612,7 @@ case class ModelDoIndex[G](model_do: ModelDo[G]) extends Index[G] {
}
case class CPPLifetimeScopeIndex[G](cpp_lifetime_scope: CPPLifetimeScope[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = cpp_lifetime_scope.body
override def equals(obj: scala.Any): Boolean = obj match {
case CPPLifetimeScopeIndex(c) => c.equals(cpp_lifetime_scope)
@@ -628,8 +628,8 @@ case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranc
(Step(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)))
else if (index == 2 * (unresolved_seq_branch.branches.size - 1))
Set((Step(UnresolvedSeqBranchIndex(unresolved_seq_branch, index + 1)), Some(unresolved_seq_branch.branches.apply(index / 2)._1)),
- (Outgoing, Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))))
- else Set((Outgoing, None))
+ (Outgoing(), Some(Utils.negate(unresolved_seq_branch.branches.apply(index / 2)._1))))
+ else Set((Outgoing(), None))
}
override def resolve(): Statement[G] = {
if (index % 2 == 0) Eval(unresolved_seq_branch.branches.apply(index / 2)._1)(unresolved_seq_branch.branches.apply(index / 2)._1.o)
@@ -644,7 +644,7 @@ case class UnresolvedSeqBranchIndex[G](unresolved_seq_branch: UnresolvedSeqBranc
case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = index match {
case 0 => Set((Step(UnresolvedSeqLoopIndex(unresolved_seq_loop, 1)), Some(unresolved_seq_loop.cond)),
- (Outgoing, Some(Utils.negate(unresolved_seq_loop.cond))))
+ (Outgoing(), Some(Utils.negate(unresolved_seq_loop.cond))))
case 1 => Set((Step(UnresolvedSeqLoopIndex(unresolved_seq_loop, 0)), None))
}
override def resolve(): Statement[G] = index match {
@@ -658,7 +658,7 @@ case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G],
}
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = index match {
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
@@ -670,7 +670,7 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing(), None))
override def resolve(): Statement[G] = seq_loop.body
override def equals(obj: scala.Any): Boolean = obj match {
case SeqLoopIndex(s) => s.equals(seq_loop)
@@ -679,7 +679,7 @@ case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
}
case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAssignExpression[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = veymont_assign_expression.assign
override def equals(obj: scala.Any): Boolean = obj match {
case VeyMontAssignExpressionIndex(v) => v.equals(veymont_assign_expression)
@@ -688,7 +688,7 @@ case class VeyMontAssignExpressionIndex[G](veymont_assign_expression: VeyMontAss
}
case class CommunicateXIndex[G](communicatex: CommunicateX[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing, None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = communicatex.assign
override def equals(obj: scala.Any): Boolean = obj match {
case CommunicateXIndex(c) => c.equals(communicatex)
From 8212df849a9c11c07744ba31bcfe4e173b2647f9 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 20 Feb 2024 11:57:15 +0100
Subject: [PATCH 34/85] Added expression evaluations to lock and process
related statements
---
src/rewrite/vct/rewrite/cfg/CFGGenerator.scala | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 805e411ed3..54facc86e7 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -65,21 +65,21 @@ case class CFGGenerator[G]() {
case Inhale(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Assume(assn) => handle_expression_container(node, Eval(assn)(assn.o), context, sequential_successor(context))
case Instantiate(_, out) => handle_expression_container(node, Eval(out)(out.o), context, sequential_successor(context))
- case Wait(_) => sequential_successor(context)
- case Notify(_) => sequential_successor(context)
+ case Wait(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
+ case Notify(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
case Fork(obj) => {
val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head
// Get the successor(s) of the fork statement as well as the new thread, starting with the run method
sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, GlobalIndex[G](mutable.Seq()).enter_scope(run_method)), None))
}
- case Join(_) => sequential_successor(context)
- case Lock(_) => sequential_successor(context)
- case Unlock(_) => sequential_successor(context)
- case Commit(_) => sequential_successor(context)
+ case Join(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
+ case Lock(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
+ case Unlock(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
+ case Commit(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
case Fold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Unfold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case WandApply(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Havoc(_) => sequential_successor(context)
+ case Havoc(loc) => handle_expression_container(node, Eval(loc)(loc.o), context, sequential_successor(context))
case FramedProof(_, _, _) => evaluate_first(context.enter_scope(node))
case Extract(_) => evaluate_first(context.enter_scope(node))
// ExceptionalStatement
From 39cb088c09c08ff35386aec0911ff0ab7189cbbb Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 20 Feb 2024 11:57:51 +0100
Subject: [PATCH 35/85] Implemented some logic for interval arithmetic, started
on implementation of abstract process logic
---
.../vct/rewrite/rasi/AbstractProcess.scala | 65 +++-
.../vct/rewrite/rasi/AbstractState.scala | 16 +-
src/rewrite/vct/rewrite/rasi/Interval.scala | 313 +++++++++++++++---
.../vct/rewrite/rasi/UncertainValue.scala | 33 +-
src/rewrite/vct/rewrite/rasi/Utils.scala | 6 +-
5 files changed, 375 insertions(+), 58 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index 313d563db0..c4daa5cf9d 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -7,12 +7,69 @@ case class AbstractProcess[G](name: String) {
def get_next(node: CFGEntry[G], state: AbstractState[G]): Set[AbstractState[G]] = node match {
case CFGTerminal() => Set()
case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression(e.condition.get).can_be_true)
- .flatMap (e => process_cfg_edge(e, n)).toSet
+ .map(e => process_cfg_edge(e, n, state)).toSet
}
- private def process_cfg_edge(edge: CFGEdge[G], ast_node: Node[G]): Set[AbstractState[G]] = ast_node match {
+ private def process_cfg_edge(edge: CFGEdge[G], ast_node: Node[G], state: AbstractState[G]): AbstractState[G] = ast_node match {
// TODO: Implement!
- case Assign(target, value) => ???
- case _ => ???
+ case Assign(target, value) => state.with_valuation(target, state.resolve_integer_expression(value).try_to_resolve().get)
+ case Exhale(res) => ???
+ case Assert(res) => ???
+ case Refute(assn) => ???
+ case Inhale(res) => ???
+ case Assume(assn) => ???
+ case Instantiate(_, out) => ???
+ case Wait(_) => ???
+ case Notify(_) => ???
+ case Fork(obj) => ??? // TODO: This needs to be decided in the outer method...
+ case Join(_) => ???
+ case Lock(_) => ???
+ case Unlock(_) => ???
+ case Commit(_) => ???
+ case Fold(res) => ???
+ case Unfold(res) => ???
+ case WandApply(res) => ???
+ case Havoc(_) => ???
+ case FramedProof(_, _, _) => ???
+ case Extract(_) => ???
+ case Eval(_) => ???
+ case Return(result) => ???
+ case Throw(obj) => ???
+ case Break(label) => ???
+ case Continue(label) => ???
+ case InvokeProcedure(_, _, _, _, _, _) => ???
+ case InvokeConstructor(_, _, _, _, _, _, _) => ???
+ case InvokeMethod(_, _, _, _, _, _, _) => ???
+ case Block(_) => ???
+ case Scope(_, _) => ???
+ case Branch(_) => ???
+ case IndetBranch(branches) => ???
+ case Switch(expr, body) => ???
+ case Loop(_, _, _, _, _) => ???
+ case RangedFor(_, _, _) => ???
+ case TryCatchFinally(_, _, _) => ???
+ case Synchronized(_, _) => ???
+ case ParInvariant(_, _, _) => ???
+ case ParAtomic(_, _) => ???
+ case ParBarrier(_, _, _, _, _) => ???
+ case ParStatement(_) => ???
+ case VecBlock(_, _, _, _) => ???
+ case WandPackage(_, _) => ???
+ case ModelDo(_, _, _, _, _) => ???
+ case CDeclarationStatement(_) => ???
+ case CGoto(label) => ???
+ case CPPDeclarationStatement(_) => ???
+ case CPPLifetimeScope(_) => ???
+ case JavaLocalDeclarationStatement(_) => ???
+ case SilverNewRef(_, _) => ???
+ case SilverFieldAssign(_, _, value) => ???
+ case SilverLocalAssign(_, value) => ???
+ case PVLCommunicate(_, _) => ???
+ case PVLSeqAssign(_, _, value) => ???
+ case Communicate(_, _) => ???
+ case SeqAssign(_, _, value) => ???
+ case UnresolvedSeqBranch(branches) => ???
+ case UnresolvedSeqLoop(_, _, _) => ???
+ case _ => state.with_process_at(this, edge.target)
}
}
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index f2a592bda0..9bce02bf29 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -10,6 +10,14 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
def successors(): Set[AbstractState[G]] =
processes.flatMap(p => p._1.get_next(p._2, this)).toSet
+ def with_process_at(process: AbstractProcess[G], position: CFGEntry[G]): AbstractState[G] =
+ AbstractState(valuations, processes + (process -> position))
+
+ def with_valuation(variable: Expr[G], value: Int): AbstractState[G] = variable_from_expr(variable) match {
+ case Some(concrete_variable) => AbstractState(valuations + (concrete_variable -> value), processes)
+ case None => this
+ }
+
def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
case CIntegerValue(value) => UncertainIntegerValue.single(value.intValue)
case IntegerValue(value) => UncertainIntegerValue.single(value.intValue)
@@ -17,7 +25,7 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
case DerefHeapVariable(ref) => ???
case Deref(obj, ref) => ???
case DerefPointer(pointer) => ???
- case SizeOf(tname) => ???
+ case SizeOf(tname) => UncertainIntegerValue.above(0) // TODO: Can we use more information about sizeof?
case UMinus(arg) => -resolve_integer_expression(arg)
case AmbiguousMult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
case AmbiguousPlus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
@@ -52,8 +60,8 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
case SeqSubscript(seq, index) => ???
case ArraySubscript(arr, index) => ???
case PointerSubscript(pointer, index) => ???
- case Length(arr) => ???
- case Size(obj) => ???
+ case Length(arr) => UncertainIntegerValue.above(0) // TODO: Use contextual information from the global invariant
+ case Size(obj) => UncertainIntegerValue.above(0) // here as well
}
def resolve_boolean_expression(expr: Expr[G]): UncertainBooleanValue = expr match {
@@ -90,6 +98,8 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
case PointerSubscript(pointer, index) => ???
}
+ private def variable_from_expr(variable: Expr[G]): Option[ConcreteVariable[G]] = ???
+
private def neutral_element: Expr[G] = BooleanValue(value = true)(Origin(Seq(LabelContext("neutral element for and"))))
def to_expression(): Expr[G] = {
valuations.map(v => Eq(v._1.to_expression(), IntegerValue(v._2)(Origin(Seq(LabelContext("int value")))))(Origin(Seq(LabelContext("valuation")))))
diff --git a/src/rewrite/vct/rewrite/rasi/Interval.scala b/src/rewrite/vct/rewrite/rasi/Interval.scala
index c13e292db1..b75315d690 100644
--- a/src/rewrite/vct/rewrite/rasi/Interval.scala
+++ b/src/rewrite/vct/rewrite/rasi/Interval.scala
@@ -1,100 +1,337 @@
package vct.rewrite.rasi
-trait Interval {
+import vct.col.ast._
+
+sealed abstract class IntervalSize {
+ def get: Int
+ def +(other: IntervalSize): IntervalSize = this match {
+ case Infinite() => Infinite()
+ case Finite(v1) => other match {
+ case Infinite() => Infinite()
+ case Finite(v2) => Finite(v1 + v2)
+ }
+ }
+ def >=(other: IntervalSize): Boolean = this match {
+ case Infinite() => true
+ case Finite(v1) => other match {
+ case Infinite() => false
+ case Finite(v2) => v1 >= v2
+ }
+ }
+}
+case class Finite(value: Int) extends IntervalSize {
+ override def get: Int = value
+}
+case class Infinite() extends IntervalSize {
+ override def get: Int = throw new NoSuchElementException("Accessing infinite interval size element")
+}
+
+sealed abstract class Interval {
def empty(): Boolean
def non_empty(): Boolean = !empty()
+ def size(): IntervalSize
def intersection(other: Interval): Interval
def union(other: Interval): Interval
def complement(): Interval
+ def below_max(): Interval
+ def above_min(): Interval
def +(other: Interval): Interval
- def -(other: Interval): Interval
- def *(other: Interval): Interval
+ def -(other: Interval): Interval = this.+(-other)
+ def *(other: Interval): Interval // TODO: Modeling the multiple of an interval as an interval is imprecise
def /(other: Interval): Interval
def %(other: Interval): Interval
def unary_- : Interval
def pow(other:Interval): Interval
- def sub_intervals(): Seq[Interval] = Seq(this)
+ def sub_intervals(): Set[Interval] = Set(this)
+ def try_to_resolve(): Option[Int]
+ def to_expression[G](variable: Expr[G]): Expr[G] // TODO: Use proper origin
}
case object EmptyInterval extends Interval {
override def empty(): Boolean = true
+ override def size(): IntervalSize = Finite(0)
override def intersection(other: Interval): Interval = this
override def union(other: Interval): Interval = other
override def complement(): Interval = UnboundedInterval
+ override def below_max(): Interval = this
+ override def above_min(): Interval = this
override def +(other: Interval): Interval = this
- override def -(other: Interval): Interval = this
override def *(other: Interval): Interval = this
override def /(other: Interval): Interval = this
override def %(other: Interval): Interval = this
override def unary_- : Interval = this
override def pow(other: Interval): Interval = this
+ override def try_to_resolve(): Option[Int] = None
+ override def to_expression[G](variable: Expr[G]): Expr[G] = BooleanValue(value = false)(variable.o)
}
-case class MultiInterval(intervals: Seq[Interval]) extends Interval {
+case class MultiInterval(intervals: Set[Interval]) extends Interval {
override def empty(): Boolean = intervals.isEmpty || intervals.forall(i => i.empty())
- override def intersection(other: Interval): Interval = MultiInterval(intervals.map(i => i.intersection(other)))
- override def union(other: Interval): Interval = ???
- override def complement(): Interval = ???
- override def +(other: Interval): Interval = ???
- override def -(other: Interval): Interval = ???
- override def *(other: Interval): Interval = ???
- override def /(other: Interval): Interval = ???
- override def %(other: Interval): Interval = ???
+ override def size(): IntervalSize = intervals.map(i => i.size()).fold(Finite(0))((s1, s2) => s1 + s2)
+ override def intersection(other: Interval): Interval = MultiInterval(MultiInterval(intervals.map(i => i.intersection(other))).sub_intervals())
+ override def union(other: Interval): Interval = {
+ val (intersecting, non_intersecting) = intervals.partition(i => i.intersection(other).non_empty())
+ // Merge together intervals that are connected by the new interval
+ val new_intervals = non_intersecting + intersecting.fold(other)((i1, i2) => i1.union(i2))
+ // It could be that all intervals are now connected into one
+ if (new_intervals.size > 1) MultiInterval(new_intervals)
+ else new_intervals.head
+ }
+ override def complement(): Interval = intervals.fold(UnboundedInterval)((i1, i2) => i1.intersection(i2.complement()))
+ override def below_max(): Interval = intervals.fold(EmptyInterval)((i1, i2) => i1.union(i2.below_max()))
+ override def above_min(): Interval = intervals.fold(EmptyInterval)((i1, i2) => i1.union(i2.above_min()))
+ override def +(other: Interval): Interval = {
+ val new_intervals = intervals.map(i => i + other)
+ // It could be that all intervals are now connected into one
+ if (new_intervals.size > 1) MultiInterval(new_intervals)
+ else new_intervals.head
+ }
+ override def *(other: Interval): Interval = {
+ val new_intervals = intervals.map(i => i * other)
+ // It could be that all intervals are now connected into one
+ if (new_intervals.size > 1) MultiInterval(new_intervals)
+ else new_intervals.head
+ }
+ override def /(other: Interval): Interval = {
+ var new_intervals = intervals.map(i => i / other)
+ new_intervals = merge_intersecting(new_intervals)
+ // It could be that all intervals are now connected into one
+ if (new_intervals.size > 1) MultiInterval(new_intervals)
+ else new_intervals.head
+ }
+ override def %(other: Interval): Interval = {
+ var new_intervals = intervals.map(i => i % other)
+ new_intervals = merge_intersecting(new_intervals)
+ // It could be that all intervals are now connected into one
+ if (new_intervals.size > 1) MultiInterval(new_intervals)
+ else new_intervals.head
+ }
override def unary_- : Interval = MultiInterval(intervals.map(i => -i))
- override def pow(other: Interval): Interval = ???
- override def sub_intervals(): Seq[Interval] = intervals.flatMap(i => i.sub_intervals())
+ override def pow(other: Interval): Interval = {
+ val new_intervals = intervals.map(i => i.pow(other))
+ // It could be that all intervals are now connected into one
+ if (new_intervals.size > 1) MultiInterval(new_intervals)
+ else new_intervals.head
+ }
+ override def sub_intervals(): Set[Interval] = intervals.flatMap(i => i.sub_intervals())
+ override def try_to_resolve(): Option[Int] = {
+ if (intervals.count(i => i != EmptyInterval) == 1) intervals.filter(i => i != EmptyInterval).head.try_to_resolve()
+ else None
+ }
+ override def to_expression[G](variable: Expr[G]): Expr[G] =
+ intervals.map(i => i.to_expression(variable)).fold(BooleanValue[G](value = false)(variable.o))((e1, e2) => Or(e1, e2)(variable.o))
+
+ private def merge_intersecting(is: Set[Interval]): Set[Interval] = {
+ ???
+ }
}
case class BoundedInterval(lower: Int, upper: Int) extends Interval {
override def empty(): Boolean = lower > upper
- override def intersection(other: Interval): Interval = ???
- override def union(other: Interval): Interval = ???
+ override def size(): IntervalSize = Finite(scala.math.max(upper - lower + 1, 0))
+ override def intersection(other: Interval): Interval = other match {
+ case EmptyInterval => other
+ case mi: MultiInterval => mi.intersection(this)
+ case BoundedInterval(low, up) =>
+ if (up <= upper && up >= lower || low <= upper && low >= lower) BoundedInterval(scala.math.max(low, lower), scala.math.min(up, upper))
+ else EmptyInterval
+ case LowerBoundedInterval(low) =>
+ if (upper >= low) BoundedInterval(scala.math.max(low, lower), upper)
+ else EmptyInterval
+ case UpperBoundedInterval(up) =>
+ if (lower <= up) BoundedInterval(lower, scala.math.min(up, upper))
+ else EmptyInterval
+ case UnboundedInterval => this
+ }
+ override def union(other: Interval): Interval = other match {
+ case EmptyInterval => this
+ case mi: MultiInterval => mi.union(this)
+ case BoundedInterval(low, up) =>
+ if (up <= upper && up >= lower || low <= upper && low >= lower) BoundedInterval(scala.math.min(low, lower), scala.math.max(up, upper))
+ else MultiInterval(Set(this, other))
+ case LowerBoundedInterval(low) =>
+ if (upper >= low) LowerBoundedInterval(scala.math.min(low, lower))
+ else MultiInterval(Set(this, other))
+ case UpperBoundedInterval(up) =>
+ if (lower <= up) UpperBoundedInterval(scala.math.max(up, upper))
+ else MultiInterval(Set(this, other))
+ case UnboundedInterval => other
+ }
override def complement(): Interval =
- MultiInterval(Seq(UpperBoundedInterval(lower - 1), LowerBoundedInterval(upper + 1)))
- override def +(other: Interval): Interval = ???
- override def -(other: Interval): Interval = ???
- override def *(other: Interval): Interval = ???
- override def /(other: Interval): Interval = ???
+ MultiInterval(Set(UpperBoundedInterval(lower - 1), LowerBoundedInterval(upper + 1)))
+ override def below_max(): Interval = UpperBoundedInterval(upper)
+ override def above_min(): Interval = LowerBoundedInterval(lower)
+ override def +(other: Interval): Interval = other match {
+ case EmptyInterval | UnboundedInterval => other
+ case mi: MultiInterval => mi.+(this)
+ case BoundedInterval(low, up) => BoundedInterval(lower + low, upper + up)
+ case LowerBoundedInterval(low) => LowerBoundedInterval(lower + low)
+ case UpperBoundedInterval(up) => UpperBoundedInterval(upper + up)
+ }
+ override def *(other: Interval): Interval = other match {
+ case EmptyInterval | UnboundedInterval => other
+ case mi: MultiInterval => mi.*(this)
+ case BoundedInterval(low, up) => BoundedInterval(Utils.prod_min(low, up, lower, upper), Utils.prod_max(low, up, lower, upper))
+ case LowerBoundedInterval(low) =>
+ if (lower < 0 && upper > 0) UnboundedInterval
+ else if (lower >= 0) LowerBoundedInterval(scala.math.min(low * upper, low * lower))
+ else UpperBoundedInterval(scala.math.max(low * upper, low * lower))
+ case UpperBoundedInterval(up) =>
+ if (lower < 0 && upper > 0) UnboundedInterval
+ else if (lower >= 0) UpperBoundedInterval(scala.math.max(up * lower, up * upper))
+ else LowerBoundedInterval(scala.math.min(up * lower, up * upper))
+ }
+ override def /(other: Interval): Interval = other match {
+ case EmptyInterval => other
+ case MultiInterval(intervals) => ???
+ case BoundedInterval(low, up) => ???
+ case LowerBoundedInterval(low) => ???
+ case UpperBoundedInterval(up) => ???
+ case UnboundedInterval => BoundedInterval(-Utils.abs_max(lower, upper), Utils.abs_max(lower, upper))
+ }
override def %(other: Interval): Interval = ???
override def unary_- : Interval = BoundedInterval(-upper, -lower)
- override def pow(other: Interval): Interval = ???
+ override def pow(other: Interval): Interval = other match {
+ case EmptyInterval => other
+ case MultiInterval(intervals) => ???
+ case BoundedInterval(low, up) => ???
+ case LowerBoundedInterval(low) => ???
+ case UpperBoundedInterval(up) => ???
+ case UnboundedInterval =>
+ if (lower < 0) other
+ else LowerBoundedInterval(0)
+ }
+ override def try_to_resolve(): Option[Int] = {
+ if (lower == upper) Some(upper)
+ else None
+ }
+ override def to_expression[G](variable: Expr[G]): Expr[G] = {
+ if (lower == upper) Eq(variable, IntegerValue(upper)(variable.o))(variable.o)
+ else And(LessEq(variable, IntegerValue(upper)(variable.o))(variable.o), GreaterEq(variable, IntegerValue(lower)(variable.o))(variable.o))(variable.o)
+ }
}
case class LowerBoundedInterval(lower: Int) extends Interval {
override def empty(): Boolean = false
- override def intersection(other: Interval): Interval = ???
- override def union(other: Interval): Interval = ???
+ override def size(): IntervalSize = Infinite()
+ override def intersection(other: Interval): Interval = other match {
+ case EmptyInterval => other
+ case mi: MultiInterval => mi.intersection(this)
+ case BoundedInterval(low, up) =>
+ if (up >= lower) BoundedInterval(scala.math.max(lower, low), up)
+ else EmptyInterval
+ case LowerBoundedInterval(low) => LowerBoundedInterval(scala.math.max(low, lower))
+ case UpperBoundedInterval(up) =>
+ if (up >= lower) BoundedInterval(lower, up)
+ else EmptyInterval
+ case UnboundedInterval => this
+ }
+ override def union(other: Interval): Interval = other match {
+ case EmptyInterval => this
+ case mi: MultiInterval => mi.union(this)
+ case BoundedInterval(low, up) =>
+ if (up >= lower) LowerBoundedInterval(scala.math.min(low, lower))
+ else MultiInterval(Set(other, this))
+ case LowerBoundedInterval(low) => LowerBoundedInterval(scala.math.min(low, lower))
+ case UpperBoundedInterval(up) =>
+ if (up >= lower) UnboundedInterval
+ else MultiInterval(Set(other, this))
+ case UnboundedInterval => other
+ }
override def complement(): Interval = UpperBoundedInterval(lower - 1)
- override def +(other: Interval): Interval = ???
- override def -(other: Interval): Interval = ???
- override def *(other: Interval): Interval = ???
+ override def below_max(): Interval = UnboundedInterval
+ override def above_min(): Interval = this
+ override def +(other: Interval): Interval = other match {
+ case EmptyInterval | UnboundedInterval => other
+ case mi: MultiInterval => mi.+(this)
+ case BoundedInterval(low, _) => LowerBoundedInterval(lower + low)
+ case LowerBoundedInterval(low) => LowerBoundedInterval(lower + low)
+ case UpperBoundedInterval(_) => UnboundedInterval
+ }
+ override def *(other: Interval): Interval = other match {
+ case EmptyInterval | UnboundedInterval => other
+ case mi: MultiInterval => mi.*(this)
+ case bi: BoundedInterval => bi.*(this)
+ case LowerBoundedInterval(low) =>
+ if (low < 0 || lower < 0) UnboundedInterval
+ else LowerBoundedInterval(low * lower)
+ case UpperBoundedInterval(up) =>
+ if (lower < 0 || up > 0) UnboundedInterval
+ else UpperBoundedInterval(up * lower)
+ }
override def /(other: Interval): Interval = ???
override def %(other: Interval): Interval = ???
override def unary_- : Interval = UpperBoundedInterval(-lower)
override def pow(other: Interval): Interval = ???
+ override def try_to_resolve(): Option[Int] = None
+ override def to_expression[G](variable: Expr[G]): Expr[G] = GreaterEq(variable, IntegerValue(lower)(variable.o))(variable.o)
}
case class UpperBoundedInterval(upper: Int) extends Interval {
override def empty(): Boolean = false
- override def intersection(other: Interval): Interval = ???
- override def union(other: Interval): Interval = ???
+ override def size(): IntervalSize = Infinite()
+ override def intersection(other: Interval): Interval = other match {
+ case EmptyInterval => other
+ case mi: MultiInterval => mi.intersection(this)
+ case BoundedInterval(low, up) =>
+ if (low <= upper) BoundedInterval(low, scala.math.min(up, upper))
+ else EmptyInterval
+ case LowerBoundedInterval(low) =>
+ if (low <= upper) BoundedInterval(low, upper)
+ else EmptyInterval
+ case UpperBoundedInterval(up) => UpperBoundedInterval(scala.math.min(up, upper))
+ case UnboundedInterval => this
+ }
+ override def union(other: Interval): Interval = other match {
+ case EmptyInterval => this
+ case mi: MultiInterval => mi.union(this)
+ case BoundedInterval(low, up) =>
+ if (low <= upper) UpperBoundedInterval(scala.math.max(upper, up))
+ else MultiInterval(Set(this, other))
+ case LowerBoundedInterval(low) =>
+ if (low <= upper) UnboundedInterval
+ else MultiInterval(Set(this, other))
+ case UpperBoundedInterval(up) => UpperBoundedInterval(scala.math.max(upper, up))
+ case UnboundedInterval => other
+ }
override def complement(): Interval = LowerBoundedInterval(upper + 1)
- override def +(other: Interval): Interval = ???
- override def -(other: Interval): Interval = ???
- override def *(other: Interval): Interval = ???
+ override def below_max(): Interval = this
+ override def above_min(): Interval = UnboundedInterval
+ override def +(other: Interval): Interval = other match {
+ case EmptyInterval | UnboundedInterval => other
+ case mi: MultiInterval => mi.+(this)
+ case BoundedInterval(_, up) => UpperBoundedInterval(upper + up)
+ case LowerBoundedInterval(_) => UnboundedInterval
+ case UpperBoundedInterval(up) => UpperBoundedInterval(upper + up)
+ }
+ override def *(other: Interval): Interval = other match {
+ case EmptyInterval | UnboundedInterval => other
+ case mi: MultiInterval => mi.*(this)
+ case bi: BoundedInterval => bi.*(this)
+ case LowerBoundedInterval(low) =>
+ if (low < 0 || upper > 0) UnboundedInterval
+ else UpperBoundedInterval(low * upper)
+ case UpperBoundedInterval(up) =>
+ if (up > 0 || upper > 0) UnboundedInterval
+ else LowerBoundedInterval(up * upper)
+ }
override def /(other: Interval): Interval = ???
override def %(other: Interval): Interval = ???
override def unary_- : Interval = LowerBoundedInterval(-upper)
override def pow(other: Interval): Interval = ???
+ override def try_to_resolve(): Option[Int] = None
+ override def to_expression[G](variable: Expr[G]): Expr[G] = LessEq(variable, IntegerValue(upper)(variable.o))(variable.o)
}
case object UnboundedInterval extends Interval {
override def empty(): Boolean = false
+ override def size(): IntervalSize = Infinite()
override def intersection(other: Interval): Interval = other
override def union(other: Interval): Interval = this
override def complement(): Interval = EmptyInterval
+ override def below_max(): Interval = this
+ override def above_min(): Interval = this
override def +(other: Interval): Interval = this
- override def -(other: Interval): Interval = this
override def *(other: Interval): Interval = this
override def /(other: Interval): Interval = this
override def %(other: Interval): Interval = other match {
@@ -103,12 +340,12 @@ case object UnboundedInterval extends Interval {
val intvs = mi.sub_intervals()
if (intvs.collect{case LowerBoundedInterval(_) | UpperBoundedInterval(_) | UnboundedInterval => 0}.nonEmpty)
return this
- val max = intvs.map{case EmptyInterval => 0; case BoundedInterval(lower, upper) => Utils.absmax(lower, upper)}.max - 1
+ val max = intvs.map{case EmptyInterval => 0; case BoundedInterval(lower, upper) => Utils.abs_max(lower, upper)}.max - 1
if (max <= 0) EmptyInterval
else BoundedInterval(-max, max)
}
case BoundedInterval(lower, upper) => {
- val max = Utils.absmax(lower, upper) - 1
+ val max = Utils.abs_max(lower, upper) - 1
BoundedInterval(-max, max)
}
case LowerBoundedInterval(_) => this
@@ -116,4 +353,6 @@ case object UnboundedInterval extends Interval {
}
override def unary_- : Interval = this
override def pow(other: Interval): Interval = this
+ override def try_to_resolve(): Option[Int] = None
+ override def to_expression[G](variable: Expr[G]): Expr[G] = BooleanValue(value = true)(variable.o)
}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index 58de293393..a433e2f1ea 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -16,28 +16,29 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
case _ => true
}
+ def try_to_resolve(): Option[Boolean] = {
+ if (can_be_true && !can_be_false) Some(true)
+ else if (can_be_false && !can_be_true) Some(false)
+ else None
+ }
+
def union(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false || other.can_be_false)
def &(other: UncertainBooleanValue): UncertainBooleanValue = this && other
-
def &&(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true && other.can_be_true, can_be_false || other.can_be_false)
-
def |(other: UncertainBooleanValue): UncertainBooleanValue = this || other
-
def ||(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false && other.can_be_false)
-
def unary_! : UncertainBooleanValue =
UncertainBooleanValue(can_be_false, can_be_true)
-
def ^(other: UncertainBooleanValue): UncertainBooleanValue =
- UncertainBooleanValue(can_be_true && other.can_be_false || can_be_false && other.can_be_true, can_be_true && other.can_be_true || can_be_false && other.can_be_false)
-
+ UncertainBooleanValue(can_be_true && other.can_be_false || can_be_false && other.can_be_true,
+ can_be_true && other.can_be_true || can_be_false && other.can_be_false)
def ==(other: UncertainBooleanValue): UncertainBooleanValue =
- UncertainBooleanValue(can_be_true && other.can_be_true || can_be_false && other.can_be_false, can_be_true && other.can_be_false || can_be_false && other.can_be_true)
-
+ UncertainBooleanValue(can_be_true && other.can_be_true || can_be_false && other.can_be_false,
+ can_be_true && other.can_be_false || can_be_false && other.can_be_true)
def !=(other: UncertainBooleanValue): UncertainBooleanValue = this ^ other
}
@@ -52,6 +53,8 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
case _ => true
}
+ def try_to_resolve(): Option[Int] = value.try_to_resolve()
+
def union(other: UncertainIntegerValue): UncertainIntegerValue =
UncertainIntegerValue(value.union(other.value))
@@ -59,10 +62,14 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
UncertainBooleanValue(can_be_equal(other), can_be_unequal(other))
def !=(other: UncertainIntegerValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_unequal(other), can_be_equal(other))
- def >=(other: UncertainIntegerValue): UncertainBooleanValue = ???
- def <=(other: UncertainIntegerValue): UncertainBooleanValue = ???
- def >(other: UncertainIntegerValue): UncertainBooleanValue = ???
- def <(other: UncertainIntegerValue): UncertainBooleanValue = ???
+ def >=(other: UncertainIntegerValue): UncertainBooleanValue =
+ UncertainBooleanValue(value.below_max().intersection(other.value).non_empty(),
+ value.above_min().intersection(other.value).size() >= Finite(1))
+ def <=(other: UncertainIntegerValue): UncertainBooleanValue =
+ UncertainBooleanValue(value.above_min().intersection(other.value).non_empty(),
+ value.below_max().intersection(other.value).size() >= Finite(1))
+ def >(other: UncertainIntegerValue): UncertainBooleanValue = !(this <= other)
+ def <(other: UncertainIntegerValue): UncertainBooleanValue = !(this >= other)
def unary_- : UncertainIntegerValue =
UncertainIntegerValue(-value)
diff --git a/src/rewrite/vct/rewrite/rasi/Utils.scala b/src/rewrite/vct/rewrite/rasi/Utils.scala
index 73b203c984..c421bc35ff 100644
--- a/src/rewrite/vct/rewrite/rasi/Utils.scala
+++ b/src/rewrite/vct/rewrite/rasi/Utils.scala
@@ -1,5 +1,9 @@
package vct.rewrite.rasi
case object Utils {
- def absmax(a: Int, b: Int): Int = Seq(-a, a, -b, b).max
+ def abs_max(a: Int, b: Int): Int = Seq(-a, a, -b, b).max
+
+ def prod_max(a1: Int, a2: Int, b1: Int, b2: Int): Int = Seq(a1 * b1, a1 * b2, a2 * b1, a2 * b2).max
+
+ def prod_min(a1: Int, a2: Int, b1: Int, b2: Int): Int = Seq(a1 * b1, a1 * b2, a2 * b1, a2 * b2).min
}
From 88dcc4033bf21a7e75bd3e9882236248306e4c8f Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 20 Feb 2024 16:37:17 +0100
Subject: [PATCH 36/85] Started implementing switch statements for CFG
---
.../vct/rewrite/cfg/CFGGenerator.scala | 35 ++++++++++++----
src/rewrite/vct/rewrite/cfg/Utils.scala | 41 +++++++++++++++----
2 files changed, 60 insertions(+), 16 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 54facc86e7..0883a48b28 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -43,7 +43,7 @@ case class CFGGenerator[G]() {
case PVLLoop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
// NonExecutableStatement
case LocalDecl(_) => sequential_successor(context)
- case SpecIgnoreStart() => sequential_successor(context) // TODO: What is this?
+ case SpecIgnoreStart() => sequential_successor(context)
case SpecIgnoreEnd() => sequential_successor(context)
// NormallyCompletingStatement
case Assign(_, _) => context.indices.head match {
@@ -104,19 +104,18 @@ case class CFGGenerator[G]() {
case Branch(_) => evaluate_first(context.enter_scope(node))
case IndetBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
- case Switch(expr, body) => // TODO: Add conditions to switch statement transitions
- Utils.find_all_cases(body, context.enter_scope(node)).map(t => CFGEdge(convert(t._1, t._2), None)) // TODO: Handle side effects in switch statement conditions
+ case s: Switch[G] => handle_switch_statement(s, context)
case Loop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
case RangedFor(_, _, _) => evaluate_first(context.enter_scope(node))
case TryCatchFinally(_, _, _) => evaluate_first(context.enter_scope(node))
case Synchronized(_, _) => evaluate_first(context.enter_scope(node))
- case ParInvariant(_, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
- case ParAtomic(_, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
- case ParBarrier(_, _, _, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
+ case ParInvariant(_, _, _) => evaluate_first(context.enter_scope(node))
+ case ParAtomic(_, _) => evaluate_first(context.enter_scope(node))
+ case ParBarrier(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
case ParStatement(_) => sequential_successor(context)
- case VecBlock(_, _, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
- case WandPackage(_, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
- case ModelDo(_, _, _, _, _) => evaluate_first(context.enter_scope(node)) // TODO: Expression side effects
+ case VecBlock(_, _, _, _) => evaluate_first(context.enter_scope(node))
+ case WandPackage(_, _) => evaluate_first(context.enter_scope(node))
+ case ModelDo(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
// CStatement
case CDeclarationStatement(_) => sequential_successor(context)
case CGoto(label) => ??? // I'm not dealing with string labels
@@ -165,6 +164,24 @@ case class CFGGenerator[G]() {
else mutable.Set(CFGEdge(CFGTerminal(), None))
}
+ private def handle_switch_statement(switch: Switch[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
+ val switches: Seq[(SwitchCase[G], GlobalIndex[G])] = Utils.find_all_cases(switch.body, context.enter_scope(switch))
+ var conds: Seq[(Eval[G], GlobalIndex[G])] = Seq()
+ var default: Option[(DefaultCase[G], GlobalIndex[G])] = None
+ for (s <- switches) {
+ s._1 match {
+ case c @ Case(pattern) => {
+ val cond = Eq(switch.expr, pattern)(c.o)
+ // Collect only up to default case
+ if (default.isEmpty) conds = conds :+ (Eval(cond)(cond.o), s._2)
+ }
+ case c @ DefaultCase() => default = Some((c, s._2))
+ }
+ }
+ // TODO: Finish implementation
+ ???
+ }
+
private def return_successor(index: GlobalIndex[G]): CFGEntry[G] =
resolve_index(index.return_from_call())
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index 197849e05a..9b543653b1 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -56,15 +56,42 @@ object Utils {
private def get_out_variable[G](cls: Ref[G, Class[G]], o: Origin): Local[G] = Local(new DirectRef[G, Variable[G]](new Variable(TClass(cls))(o)))(o)
- def find_all_cases[G](body: Statement[G], index: GlobalIndex[G]): mutable.Set[(SwitchCase[G], GlobalIndex[G])] = body match {
- case Switch(_, _) => mutable.Set()
+ def find_all_cases[G](body: Statement[G], index: GlobalIndex[G]): Seq[(SwitchCase[G], GlobalIndex[G])] = body match {
+ case Switch(_, _) => Seq()
// Recursion on statements that can contain case statements
- case Label(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
- case Block(stmts) => mutable.LinkedHashSet.from(stmts.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2))))
- case Scope(_, stmt) => find_all_cases(stmt, index.enter_scope(body))
+ case Label(_, stat) => find_all_cases(stat, index.enter_scope(body))
+ case Block(statements) => statements.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2)))
+ case Scope(_, bod) => find_all_cases(bod, index.enter_scope(body))
+ case PVLBranch(branches) => branches.zipWithIndex.flatMap(t => find_all_cases(t._1._2, index.enter_scope(body, t._2 * 2 + 1)))
+ case PVLLoop(init, _, update, _, bod) => Seq((init, 0), (bod, 2), (update, 3)).flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2)))
+ case FramedProof(_, bod, _) => find_all_cases(bod, index.enter_scope(body))
+ case Extract(contractedStatement) => find_all_cases(contractedStatement, index.enter_scope(body))
+ case Branch(branches) => branches.zipWithIndex.flatMap(t => find_all_cases(t._1._2, index.enter_scope(body, t._2 * 2 + 1)))
+ case IndetBranch(branches) => branches.zipWithIndex.flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2)))
+ case Loop(init, _, update, _, bod) => Seq((init, 0), (bod, 2), (update, 3)).flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2)))
+ case RangedFor(_, _, bod) => find_all_cases(bod, index.enter_scope(body))
+ case TryCatchFinally(bod, after, catches) => Seq((bod, 0), (after, 1)).concat(catches.zipWithIndex.map(t => (t._1.body, t._2 + 2)))
+ .flatMap(t => find_all_cases(t._1, index.enter_scope(body, t._2)))
+ case Synchronized(_, bod) => find_all_cases(bod, index.enter_scope(body))
+ case ParInvariant(_, _, content) => find_all_cases(content, index.enter_scope(body))
+ case ParAtomic(_, content) => find_all_cases(content, index.enter_scope(body))
+ case ParBarrier(_, _, _, _, content) => find_all_cases(content, index.enter_scope(body))
+ case VecBlock(_, _, _, content) => find_all_cases(content, index.enter_scope(body))
+ case WandPackage(_, proof) => find_all_cases(proof, index.enter_scope(body))
+ case ModelDo(_, _, _, _, impl) => find_all_cases(impl, index.enter_scope(body))
+ case CPPLifetimeScope(bod) => find_all_cases(bod, index.enter_scope(body))
+ case UnresolvedSeqBranch(branches) => branches.zipWithIndex.flatMap(t => find_all_cases(t._1._2, index.enter_scope(body, t._2 * 2 + 1)))
+ case UnresolvedSeqLoop(_, _, bod) => find_all_cases(bod, index.enter_scope(body))
+ case SeqBranch(_, yes, no) => no match {
+ case Some(stmt) => Seq((yes, 0), (stmt, 1)).flatMap(t => find_all_cases(t._1, index.enter_scope(body)))
+ case None => find_all_cases(yes, index.enter_scope(body))
+ }
+ case SeqLoop(_, _, bod) => find_all_cases(bod, index.enter_scope(body))
+ case VeyMontAssignExpression(_, assign) => find_all_cases(assign, index.enter_scope(body))
+ case CommunicateX(_, _, _, assign) => find_all_cases(assign, index.enter_scope(body))
// Recursion end
- case c: SwitchCase[G] => mutable.Set((c, index))
- case _ => mutable.Set() // TODO: Assuming that there are no cases in deeper structures (branches, loops etc.)
+ case c: SwitchCase[G] => Seq((c, index))
+ case _ => Seq()
}
def negate[G](expr: Expr[G]): Expr[G] = expr match {
From 33ac27959703996ec647c94a6dd417d43e1a4d95 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 20 Feb 2024 16:40:29 +0100
Subject: [PATCH 37/85] Made variable valuations abstract in RASI generator
---
.../vct/rewrite/rasi/AbstractProcess.scala | 2 +-
.../vct/rewrite/rasi/AbstractState.scala | 41 ++++++++-----------
.../vct/rewrite/rasi/RASIGenerator.scala | 4 +-
.../vct/rewrite/rasi/UncertainValue.scala | 35 ++++++++--------
4 files changed, 40 insertions(+), 42 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index c4daa5cf9d..f272f3fb7d 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -12,7 +12,7 @@ case class AbstractProcess[G](name: String) {
private def process_cfg_edge(edge: CFGEdge[G], ast_node: Node[G], state: AbstractState[G]): AbstractState[G] = ast_node match {
// TODO: Implement!
- case Assign(target, value) => state.with_valuation(target, state.resolve_integer_expression(value).try_to_resolve().get)
+ case Assign(target, value) => state.with_valuation(target, state.resolve_expression(value))
case Exhale(res) => ???
case Assert(res) => ???
case Refute(assn) => ???
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index 9bce02bf29..fc65f4a00e 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -6,25 +6,27 @@ import vct.rewrite.cfg.CFGEntry
import scala.collection.immutable.HashMap
-case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes: HashMap[AbstractProcess[G], CFGEntry[G]]) {
+case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue], processes: HashMap[AbstractProcess[G], CFGEntry[G]]) {
def successors(): Set[AbstractState[G]] =
processes.flatMap(p => p._1.get_next(p._2, this)).toSet
def with_process_at(process: AbstractProcess[G], position: CFGEntry[G]): AbstractState[G] =
AbstractState(valuations, processes + (process -> position))
- def with_valuation(variable: Expr[G], value: Int): AbstractState[G] = variable_from_expr(variable) match {
+ def with_valuation(variable: Expr[G], value: UncertainValue): AbstractState[G] = variable_from_expr(variable) match {
case Some(concrete_variable) => AbstractState(valuations + (concrete_variable -> value), processes)
case None => this
}
- def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
+ def resolve_expression(expr: Expr[G]): UncertainValue = expr.t match {
+ case _: IntType[_] => resolve_integer_expression(expr)
+ case _: TBool[_] => resolve_boolean_expression(expr)
+ case _ => throw new IllegalArgumentException(s"Type ${expr.t.toInlineString} is not supported")
+ }
+
+ private def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
case CIntegerValue(value) => UncertainIntegerValue.single(value.intValue)
case IntegerValue(value) => UncertainIntegerValue.single(value.intValue)
- case Local(ref) => ???
- case DerefHeapVariable(ref) => ???
- case Deref(obj, ref) => ???
- case DerefPointer(pointer) => ???
case SizeOf(tname) => UncertainIntegerValue.above(0) // TODO: Can we use more information about sizeof?
case UMinus(arg) => -resolve_integer_expression(arg)
case AmbiguousMult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
@@ -56,20 +58,16 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
if (resolve_boolean_expression(cond).can_be_false) value = value.union(resolve_integer_expression(iff))
value
}
- case AmbiguousSubscript(collection, index) => ???
- case SeqSubscript(seq, index) => ???
- case ArraySubscript(arr, index) => ???
- case PointerSubscript(pointer, index) => ???
+ case Local(_) | DerefHeapVariable(_) | Deref(_, _) | DerefPointer(_) | AmbiguousSubscript(_, _) | SeqSubscript(_, _) | ArraySubscript(_, _) | PointerSubscript(_, _) => variable_from_expr(expr) match {
+ case Some(v) => valuations(v).asInstanceOf[UncertainIntegerValue]
+ case None => UncertainIntegerValue.uncertain()
+ }
case Length(arr) => UncertainIntegerValue.above(0) // TODO: Use contextual information from the global invariant
case Size(obj) => UncertainIntegerValue.above(0) // here as well
}
def resolve_boolean_expression(expr: Expr[G]): UncertainBooleanValue = expr match {
case BooleanValue(value) => UncertainBooleanValue(value, !value)
- case Local(ref) => ???
- case DerefHeapVariable(ref) => ???
- case Deref(obj, ref) => ???
- case DerefPointer(pointer) => ???
case Not(arg) => !resolve_boolean_expression(arg)
case AmbiguousOr(left, right) => resolve_boolean_expression(left) || resolve_boolean_expression(right)
case And(left, right) => resolve_boolean_expression(left) && resolve_boolean_expression(right)
@@ -92,17 +90,14 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], Int], processes
if (resolve_boolean_expression(cond).can_be_false) value = value.union(resolve_boolean_expression(iff))
value
}
- case AmbiguousSubscript(collection, index) => ???
- case SeqSubscript(seq, index) => ???
- case ArraySubscript(arr, index) => ???
- case PointerSubscript(pointer, index) => ???
+ case Local(_) | DerefHeapVariable(_) | Deref(_, _) | DerefPointer(_) | AmbiguousSubscript(_, _) | SeqSubscript(_, _) | ArraySubscript(_, _) | PointerSubscript(_, _) => variable_from_expr(expr) match {
+ case Some(v) => valuations(v).asInstanceOf[UncertainBooleanValue]
+ case None => UncertainBooleanValue(can_be_true = true, can_be_false = true)
+ }
}
private def variable_from_expr(variable: Expr[G]): Option[ConcreteVariable[G]] = ???
private def neutral_element: Expr[G] = BooleanValue(value = true)(Origin(Seq(LabelContext("neutral element for and"))))
- def to_expression(): Expr[G] = {
- valuations.map(v => Eq(v._1.to_expression(), IntegerValue(v._2)(Origin(Seq(LabelContext("int value")))))(Origin(Seq(LabelContext("valuation")))))
- .fold(neutral_element)((e1, e2) => And(e1, e2)(e1.o))
- }
+ def to_expression(): Expr[G] = valuations.map(v => v._2.to_expression(v._1.to_expression())).fold(neutral_element)((e1, e2) => And(e1, e2)(e1.o))
}
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index c00f457c31..3c2d8195cf 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -23,12 +23,12 @@ case class RASIGenerator[G]() {
current_branches.addOne(initial_state)
while (current_branches.nonEmpty) {
- val curr = current_branches.head
+ val curr: AbstractState[G] = current_branches.head
current_branches.remove(curr)
curr.successors().foreach(s => if (!found_states.contains(s)) {found_states.addOne(curr); current_branches.addOne(curr)})
}
}
- private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], Int] = ???
+ private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], UncertainValue] = ???
}
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index a433e2f1ea..86f64bf7b8 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -1,8 +1,11 @@
package vct.rewrite.rasi
+import vct.col.ast.{BooleanValue, Expr, Not}
+
trait UncertainValue {
def can_be_equal(other: UncertainValue): Boolean
def can_be_unequal(other: UncertainValue): Boolean
+ def to_expression[G](variable: Expr[G]): Expr[G]
}
case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) extends UncertainValue {
@@ -16,6 +19,13 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
case _ => true
}
+ override def to_expression[G](variable: Expr[G]): Expr[G] = {
+ if (can_be_true && can_be_false) BooleanValue(value = true)(variable.o)
+ else if (can_be_true) variable
+ else if (can_be_false) Not(variable)(variable.o)
+ else BooleanValue(value = false)(variable.o)
+ }
+
def try_to_resolve(): Option[Boolean] = {
if (can_be_true && !can_be_false) Some(true)
else if (can_be_false && !can_be_true) Some(false)
@@ -25,10 +35,8 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
def union(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false || other.can_be_false)
- def &(other: UncertainBooleanValue): UncertainBooleanValue = this && other
def &&(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true && other.can_be_true, can_be_false || other.can_be_false)
- def |(other: UncertainBooleanValue): UncertainBooleanValue = this || other
def ||(other: UncertainBooleanValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_true || other.can_be_true, can_be_false && other.can_be_false)
def unary_! : UncertainBooleanValue =
@@ -53,6 +61,8 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
case _ => true
}
+ override def to_expression[G](variable: Expr[G]): Expr[G] = value.to_expression(variable)
+
def try_to_resolve(): Option[Int] = value.try_to_resolve()
def union(other: UncertainIntegerValue): UncertainIntegerValue =
@@ -71,20 +81,13 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
def >(other: UncertainIntegerValue): UncertainBooleanValue = !(this <= other)
def <(other: UncertainIntegerValue): UncertainBooleanValue = !(this >= other)
- def unary_- : UncertainIntegerValue =
- UncertainIntegerValue(-value)
- def +(other: UncertainIntegerValue): UncertainIntegerValue =
- UncertainIntegerValue(value + other.value)
- def -(other: UncertainIntegerValue): UncertainIntegerValue =
- UncertainIntegerValue(value - other.value)
- def *(other: UncertainIntegerValue): UncertainIntegerValue =
- UncertainIntegerValue(value * other.value)
- def /(other: UncertainIntegerValue): UncertainIntegerValue =
- UncertainIntegerValue(value / other.value)
- def %(other: UncertainIntegerValue): UncertainIntegerValue =
- UncertainIntegerValue(value % other.value)
- def pow(other: UncertainIntegerValue): UncertainIntegerValue =
- UncertainIntegerValue(value.pow(other.value))
+ def unary_- : UncertainIntegerValue = UncertainIntegerValue(-value)
+ def +(other: UncertainIntegerValue): UncertainIntegerValue = UncertainIntegerValue(value + other.value)
+ def -(other: UncertainIntegerValue): UncertainIntegerValue = UncertainIntegerValue(value - other.value)
+ def *(other: UncertainIntegerValue): UncertainIntegerValue = UncertainIntegerValue(value * other.value)
+ def /(other: UncertainIntegerValue): UncertainIntegerValue = UncertainIntegerValue.uncertain() // TODO: UncertainIntegerValue(value / other.value)
+ def %(other: UncertainIntegerValue): UncertainIntegerValue = UncertainIntegerValue.uncertain() // TODO: UncertainIntegerValue(value % other.value)
+ def pow(other: UncertainIntegerValue): UncertainIntegerValue = UncertainIntegerValue.uncertain() // TODO: UncertainIntegerValue(value.pow(other.value))
}
case object UncertainIntegerValue {
def empty(): UncertainIntegerValue = UncertainIntegerValue(EmptyInterval)
From a23673be31f00bce1e7a15246dfa6e6bed8313bb Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 21 Feb 2024 14:27:39 +0100
Subject: [PATCH 38/85] Implemented some abstract process logic; added edges to
RASI; improved ConcreteVariable recognition
---
.../vct/rewrite/rasi/AbstractProcess.scala | 111 ++++++++----------
.../vct/rewrite/rasi/AbstractState.scala | 61 ++++++----
.../vct/rewrite/rasi/ConcreteVariable.scala | 45 ++++---
.../vct/rewrite/rasi/RASIGenerator.scala | 29 +++--
.../vct/rewrite/rasi/UncertainValue.scala | 4 +
5 files changed, 132 insertions(+), 118 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index f272f3fb7d..843cd58015 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -3,73 +3,54 @@ package vct.rewrite.rasi
import vct.col.ast._
import vct.rewrite.cfg.{CFGEdge, CFGEntry, CFGNode, CFGTerminal}
+import scala.collection.mutable
+
case class AbstractProcess[G](name: String) {
def get_next(node: CFGEntry[G], state: AbstractState[G]): Set[AbstractState[G]] = node match {
- case CFGTerminal() => Set()
- case CFGNode(n, succ) => succ.filter(e => e.condition.isEmpty || state.resolve_boolean_expression(e.condition.get).can_be_true)
- .map(e => process_cfg_edge(e, n, state)).toSet
+ case CFGTerminal() => Set(state.without_process(this))
+ case CFGNode(n, succ) => n match {
+ // Assign statements change the state of variables directly (if they appear in the valuation)
+ case Assign(target, value) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(target, state.resolve_expression(value))))
+ case Havoc(loc) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(loc, loc.t match {
+ case _: IntType[_] => UncertainIntegerValue.uncertain()
+ case _: TBool[_] => UncertainBooleanValue.uncertain()
+ })))
+ // TODO: Consider state changes by specifications
+ case Assume(assn) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case Inhale(res) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case InvokeProcedure(ref, _, _, _, _, _) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case InvokeConstructor(ref, _, _, _, _, _, _) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case InvokeMethod(_, ref, _, _, _, _, _) => viable_edges(succ, state).map(e => take_edge(e, state))
+ // TODO: What do wait and notify do?
+ case Wait(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case Notify(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
+ // Lock and Unlock manipulate the global lock and are potentially blocking TODO: Differentiate between locks!
+ case Lock(_) => state.lock match {
+ case Some(proc) => if (!proc.equals(this)) Set(state)
+ else throw new IllegalStateException("Trying to lock already acquired lock")
+ case None => viable_edges(succ, state).map(e => take_edge(e, state).locked_by(this))
+ }
+ case Unlock(_) => state.lock match {
+ case Some(proc) => if (proc.equals(this)) viable_edges(succ, state).map(e => take_edge(e, state).unlocked())
+ else throw new IllegalStateException("Trying to unlock lock owned by other process")
+ case None => throw new IllegalStateException("Trying to unlock unlocked lock")
+ }
+ // When forking a new process, make the step of creating it simultaneously to the normal steps TODO: consider join
+ case Fork(obj) =>
+ val edges: (Set[CFGEdge[G]], Set[CFGEdge[G]]) = viable_edges(succ, state).partition(e => e.target match {
+ case CFGTerminal() => false
+ case CFGNode(t, _) => t.equals(obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head.body.get)
+ })
+ edges._2.map(e => take_edge(e, state.with_process_at(AbstractProcess(s"${name}_${obj.toInlineString}"), edges._1.head.target)))
+ case Join(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
+ // Everything else does not affect the state, so simply go to the next step
+ case _ => viable_edges(succ, state).map(e => take_edge(e, state))
+ }
}
- private def process_cfg_edge(edge: CFGEdge[G], ast_node: Node[G], state: AbstractState[G]): AbstractState[G] = ast_node match {
- // TODO: Implement!
- case Assign(target, value) => state.with_valuation(target, state.resolve_expression(value))
- case Exhale(res) => ???
- case Assert(res) => ???
- case Refute(assn) => ???
- case Inhale(res) => ???
- case Assume(assn) => ???
- case Instantiate(_, out) => ???
- case Wait(_) => ???
- case Notify(_) => ???
- case Fork(obj) => ??? // TODO: This needs to be decided in the outer method...
- case Join(_) => ???
- case Lock(_) => ???
- case Unlock(_) => ???
- case Commit(_) => ???
- case Fold(res) => ???
- case Unfold(res) => ???
- case WandApply(res) => ???
- case Havoc(_) => ???
- case FramedProof(_, _, _) => ???
- case Extract(_) => ???
- case Eval(_) => ???
- case Return(result) => ???
- case Throw(obj) => ???
- case Break(label) => ???
- case Continue(label) => ???
- case InvokeProcedure(_, _, _, _, _, _) => ???
- case InvokeConstructor(_, _, _, _, _, _, _) => ???
- case InvokeMethod(_, _, _, _, _, _, _) => ???
- case Block(_) => ???
- case Scope(_, _) => ???
- case Branch(_) => ???
- case IndetBranch(branches) => ???
- case Switch(expr, body) => ???
- case Loop(_, _, _, _, _) => ???
- case RangedFor(_, _, _) => ???
- case TryCatchFinally(_, _, _) => ???
- case Synchronized(_, _) => ???
- case ParInvariant(_, _, _) => ???
- case ParAtomic(_, _) => ???
- case ParBarrier(_, _, _, _, _) => ???
- case ParStatement(_) => ???
- case VecBlock(_, _, _, _) => ???
- case WandPackage(_, _) => ???
- case ModelDo(_, _, _, _, _) => ???
- case CDeclarationStatement(_) => ???
- case CGoto(label) => ???
- case CPPDeclarationStatement(_) => ???
- case CPPLifetimeScope(_) => ???
- case JavaLocalDeclarationStatement(_) => ???
- case SilverNewRef(_, _) => ???
- case SilverFieldAssign(_, _, value) => ???
- case SilverLocalAssign(_, value) => ???
- case PVLCommunicate(_, _) => ???
- case PVLSeqAssign(_, _, value) => ???
- case Communicate(_, _) => ???
- case SeqAssign(_, _, value) => ???
- case UnresolvedSeqBranch(branches) => ???
- case UnresolvedSeqLoop(_, _, _) => ???
- case _ => state.with_process_at(this, edge.target)
- }
+ private def viable_edges(edges: mutable.Set[CFGEdge[G]], state: AbstractState[G]): Set[CFGEdge[G]] =
+ edges.filter(e => e.condition.isEmpty || state.resolve_boolean_expression(e.condition.get).can_be_true).toSet
+
+ private def take_edge(edge: CFGEdge[G], state: AbstractState[G]): AbstractState[G] =
+ state.with_process_at(this, edge.target)
}
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index fc65f4a00e..2e2dc18ec2 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -1,30 +1,36 @@
package vct.rewrite.rasi
import vct.col.ast._
-import vct.col.origin.{LabelContext, Origin}
import vct.rewrite.cfg.CFGEntry
import scala.collection.immutable.HashMap
-case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue], processes: HashMap[AbstractProcess[G], CFGEntry[G]]) {
+case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue], processes: HashMap[AbstractProcess[G], CFGEntry[G]], lock: Option[AbstractProcess[G]]) {
def successors(): Set[AbstractState[G]] =
processes.flatMap(p => p._1.get_next(p._2, this)).toSet
def with_process_at(process: AbstractProcess[G], position: CFGEntry[G]): AbstractState[G] =
- AbstractState(valuations, processes + (process -> position))
+ AbstractState(valuations, processes + (process -> position), lock)
+
+ def without_process(process: AbstractProcess[G]): AbstractState[G] =
+ AbstractState(valuations, processes.removed(process), lock)
def with_valuation(variable: Expr[G], value: UncertainValue): AbstractState[G] = variable_from_expr(variable) match {
- case Some(concrete_variable) => AbstractState(valuations + (concrete_variable -> value), processes)
+ case Some(concrete_variable) => AbstractState(valuations + (concrete_variable -> value), processes, lock)
case None => this
}
+ def locked_by(process: AbstractProcess[G]): AbstractState[G] = AbstractState(valuations, processes, Some(process))
+
+ def unlocked(): AbstractState[G] = AbstractState(valuations, processes, None)
+
def resolve_expression(expr: Expr[G]): UncertainValue = expr.t match {
case _: IntType[_] => resolve_integer_expression(expr)
case _: TBool[_] => resolve_boolean_expression(expr)
case _ => throw new IllegalArgumentException(s"Type ${expr.t.toInlineString} is not supported")
}
- private def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
+ def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
case CIntegerValue(value) => UncertainIntegerValue.single(value.intValue)
case IntegerValue(value) => UncertainIntegerValue.single(value.intValue)
case SizeOf(tname) => UncertainIntegerValue.above(0) // TODO: Can we use more information about sizeof?
@@ -38,20 +44,20 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
case Mult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
case FloorDiv(left, right) => resolve_integer_expression(left) / resolve_integer_expression(right)
case Mod(left, right) => resolve_integer_expression(left) % resolve_integer_expression(right)
- // TODO: Support bit operations
- case BitNot(arg) => ??? // ~resolve_integer_expression(arg)
- case AmbiguousComputationalOr(left, right) => ??? // resolve_integer_expression(left) | resolve_integer_expression(right)
- case AmbiguousComputationalXor(left, right) => ??? // resolve_integer_expression(left) ^ resolve_integer_expression(right)
- case AmbiguousComputationalAnd(left, right) => ??? // resolve_integer_expression(left) & resolve_integer_expression(right)
- case ComputationalOr(left, right) => ??? // resolve_integer_expression(left) | resolve_integer_expression(right)
- case ComputationalXor(left, right) => ??? // resolve_integer_expression(left) ^ resolve_integer_expression(right)
- case ComputationalAnd(left, right) => ??? // resolve_integer_expression(left) & resolve_integer_expression(right)
- case BitAnd(left, right) => ??? // resolve_integer_expression(left) & resolve_integer_expression(right)
- case BitOr(left, right) => ??? // resolve_integer_expression(left) | resolve_integer_expression(right)
- case BitXor(left, right) => ??? // resolve_integer_expression(left) ^ resolve_integer_expression(right)
- case BitShl(left, right) => ??? // resolve_integer_expression(left) << resolve_integer_expression(right)
- case BitShr(left, right) => ??? // resolve_integer_expression(left) >> resolve_integer_expression(right)
- case BitUShr(left, right) => ??? // resolve_integer_expression(left) >>> resolve_integer_expression(right)
+ // Bit operations destroy any knowledge of integer state TODO: Support bit operations
+ case BitNot(_) => UncertainIntegerValue.uncertain()
+ case AmbiguousComputationalOr(_, _) => UncertainIntegerValue.uncertain()
+ case AmbiguousComputationalXor(_, _) => UncertainIntegerValue.uncertain()
+ case AmbiguousComputationalAnd(_, _) => UncertainIntegerValue.uncertain()
+ case ComputationalOr(_, _) => UncertainIntegerValue.uncertain()
+ case ComputationalXor(_, _) => UncertainIntegerValue.uncertain()
+ case ComputationalAnd(_, _) => UncertainIntegerValue.uncertain()
+ case BitAnd(_, _) => UncertainIntegerValue.uncertain()
+ case BitOr(_, _) => UncertainIntegerValue.uncertain()
+ case BitXor(_, _) => UncertainIntegerValue.uncertain()
+ case BitShl(_, _) => UncertainIntegerValue.uncertain()
+ case BitShr(_, _) => UncertainIntegerValue.uncertain()
+ case BitUShr(_, _) => UncertainIntegerValue.uncertain()
case Select(cond, ift, iff) => {
var value: UncertainIntegerValue = UncertainIntegerValue.empty()
if (resolve_boolean_expression(cond).can_be_true) value = value.union(resolve_integer_expression(ift))
@@ -64,10 +70,12 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
}
case Length(arr) => UncertainIntegerValue.above(0) // TODO: Use contextual information from the global invariant
case Size(obj) => UncertainIntegerValue.above(0) // here as well
+ case InvokeProcedure(_, _, _, _, _, _) => UncertainIntegerValue.uncertain() // TODO: return value from procedure/method?
+ case InvokeMethod(_, _, _, _, _, _, _) => UncertainIntegerValue.uncertain()
}
def resolve_boolean_expression(expr: Expr[G]): UncertainBooleanValue = expr match {
- case BooleanValue(value) => UncertainBooleanValue(value, !value)
+ case BooleanValue(value) => UncertainBooleanValue.from(value)
case Not(arg) => !resolve_boolean_expression(arg)
case AmbiguousOr(left, right) => resolve_boolean_expression(left) || resolve_boolean_expression(right)
case And(left, right) => resolve_boolean_expression(left) && resolve_boolean_expression(right)
@@ -83,7 +91,7 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
case Less(left, right) => resolve_integer_expression(left) < resolve_integer_expression(right)
case GreaterEq(left, right) => resolve_integer_expression(left) >= resolve_integer_expression(right)
case LessEq(left, right) => resolve_integer_expression(left) <= resolve_integer_expression(right)
- case c: SetComparison[G] => ???
+ case c: SetComparison[G] => UncertainBooleanValue.uncertain() // TODO: Implement?
case Select(cond, ift, iff) => {
var value: UncertainBooleanValue = UncertainBooleanValue(can_be_true = false, can_be_false = false)
if (resolve_boolean_expression(cond).can_be_true) value = value.union(resolve_boolean_expression(ift))
@@ -92,12 +100,15 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
}
case Local(_) | DerefHeapVariable(_) | Deref(_, _) | DerefPointer(_) | AmbiguousSubscript(_, _) | SeqSubscript(_, _) | ArraySubscript(_, _) | PointerSubscript(_, _) => variable_from_expr(expr) match {
case Some(v) => valuations(v).asInstanceOf[UncertainBooleanValue]
- case None => UncertainBooleanValue(can_be_true = true, can_be_false = true)
+ case None => UncertainBooleanValue.uncertain()
}
+ case InvokeProcedure(_, _, _, _, _, _) => UncertainBooleanValue.uncertain() // TODO: return value from procedure/method?
+ case InvokeMethod(_, _, _, _, _, _, _) => UncertainBooleanValue.uncertain()
}
- private def variable_from_expr(variable: Expr[G]): Option[ConcreteVariable[G]] = ???
+ private def variable_from_expr(variable: Expr[G]): Option[ConcreteVariable[G]] = {
+ valuations.keys.collectFirst{ case c: ConcreteVariable[G] if c.is(variable, this) => c }
+ }
- private def neutral_element: Expr[G] = BooleanValue(value = true)(Origin(Seq(LabelContext("neutral element for and"))))
- def to_expression(): Expr[G] = valuations.map(v => v._2.to_expression(v._1.to_expression())).fold(neutral_element)((e1, e2) => And(e1, e2)(e1.o))
+ def to_expression: Expr[G] = valuations.map(v => v._2.to_expression(v._1.to_expression)).reduce((e1, e2) => And(e1, e2)(e1.o))
}
diff --git a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
index 5b9a49f598..ec644885d8 100644
--- a/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
+++ b/src/rewrite/vct/rewrite/rasi/ConcreteVariable.scala
@@ -3,25 +3,38 @@ package vct.rewrite.rasi
import vct.col.ast._
trait ConcreteVariable[G] {
- def is(expr: Expr[G]): Boolean
- def extract_from_expression(expr: Expr[G]): Field[G] = expr match {
- // TODO: Need to also consider the object!
- case Deref(obj, ref) => ref.decl
+ def is(expr: Expr[G], state: AbstractState[G]): Boolean
+ def to_expression: Expr[G]
+ def t: Type[G]
+ def field_equals(expr: Expr[G], field: InstanceField[G]): Boolean = expr match {
+ // TODO: Support other types of expressions? Take object into account?
+ case Deref(_, f) => f.decl.equals(field)
+ case _ => false
}
- def to_expression(): Expr[G]
}
-case class FieldVariable[G](field: Field[G]) extends ConcreteVariable[G] {
- override def is(expr: Expr[G]): Boolean = field.equals(extract_from_expression(expr))
- override def to_expression(): Expr[G] = ???
+case class FieldVariable[G](field: InstanceField[G]) extends ConcreteVariable[G] {
+ override def is(expr: Expr[G], state: AbstractState[G]): Boolean = field_equals(expr, field)
+ override def to_expression: Expr[G] = Deref[G](AmbiguousThis()(field.o), field.ref)(field.o)(field.o)
+ override def t: Type[G] = field.t
}
-case class IndexedVariable[G](field: Field[G], i: Int) extends ConcreteVariable[G] {
- override def is(expr: Expr[G]): Boolean = ??? /*expr match {
- case AmbiguousSubscript(collection, index) => field.equals(extract_from_expression(collection)) && i == Utils.resolve_integer_expression(index)
- case SeqSubscript(seq, index) => field.equals(extract_from_expression(seq)) && i == Utils.resolve_integer_expression(index)
- case ArraySubscript(arr, index) => field.equals(extract_from_expression(arr)) && i == Utils.resolve_integer_expression(index)
- case PointerSubscript(pointer, index) => field.equals(extract_from_expression(pointer)) && i == Utils.resolve_integer_expression(index)
- }*/
- override def to_expression(): Expr[G] = ???
+case class IndexedVariable[G](field: InstanceField[G], i: Int) extends ConcreteVariable[G] {
+ override def is(expr: Expr[G], state: AbstractState[G]): Boolean = expr match {
+ case AmbiguousSubscript(collection, index) => field_equals(collection, field) && i == state.resolve_integer_expression(index).try_to_resolve().getOrElse(-1)
+ case SeqSubscript(seq, index) => field_equals(seq, field) && i == state.resolve_integer_expression(index).try_to_resolve().getOrElse(-1)
+ case ArraySubscript(arr, index) => field_equals(arr, field) && i == state.resolve_integer_expression(index).try_to_resolve().getOrElse(-1)
+ case PointerSubscript(pointer, index) => field_equals(pointer, field) && i == state.resolve_integer_expression(index).try_to_resolve().getOrElse(-1)
+ case _ => false
+ }
+ override def to_expression: Expr[G] = field.t match {
+ case TSeq(_) => SeqSubscript(Deref[G](AmbiguousThis()(field.o), field.ref)(field.o)(field.o), IntegerValue(i)(field.o))(field.o)(field.o)
+ case TArray(_) => ArraySubscript(Deref[G](AmbiguousThis()(field.o), field.ref)(field.o)(field.o), IntegerValue(i)(field.o))(field.o)(field.o)
+ case TPointer(_) => PointerSubscript(Deref[G](AmbiguousThis()(field.o), field.ref)(field.o)(field.o), IntegerValue(i)(field.o))(field.o)(field.o)
+ }
+ override def t: Type[G] = field.t match {
+ case TSeq(element) => element
+ case TArray(element) => element
+ case TPointer(element) => element
+ }
}
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index 3c2d8195cf..390c2ed436 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -1,34 +1,39 @@
package vct.rewrite.rasi
-import vct.col.ast.{BooleanValue, Expr, Or}
-import vct.col.origin.{LabelContext, Origin}
+import vct.col.ast.{Expr, IntType, Or, TBool}
import vct.rewrite.cfg.CFGNode
import scala.collection.immutable.HashMap
import scala.collection.mutable
case class RASIGenerator[G]() {
- private val found_states: mutable.Set[AbstractState[G]] = mutable.LinkedHashSet()
- private val current_branches: mutable.Set[AbstractState[G]] = mutable.LinkedHashSet()
+ private val found_states: mutable.ArrayBuffer[AbstractState[G]] = mutable.ArrayBuffer()
+ private val found_edges: mutable.ArrayBuffer[(AbstractState[G], AbstractState[G])] = mutable.ArrayBuffer()
+ private val current_branches: mutable.ArrayBuffer[AbstractState[G]] = mutable.ArrayBuffer()
- private def neutral_element: Expr[G] = BooleanValue(value = false)(Origin(Seq(LabelContext("neutral element of or"))))
def generate_rasi(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Expr[G] = {
explore(node, vars)
- found_states.map(a => a.to_expression()).fold(neutral_element)((e1, e2) => Or(e1, e2)(e1.o))
+ found_states.distinctBy(s => s.valuations).map(s => s.to_expression).reduce((e1, e2) => Or(e1, e2)(e1.o))
}
private def explore(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Unit = {
- val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G]("main"), node)))
- found_states.addOne(initial_state)
- current_branches.addOne(initial_state)
+ val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G]("main"), node)), None)
+ found_states += initial_state
+ current_branches += initial_state
while (current_branches.nonEmpty) {
val curr: AbstractState[G] = current_branches.head
- current_branches.remove(curr)
+ current_branches -= curr
- curr.successors().foreach(s => if (!found_states.contains(s)) {found_states.addOne(curr); current_branches.addOne(curr)})
+ val successors: Set[AbstractState[G]] = curr.successors()
+ found_edges.addAll(successors.map(s => (curr, s)))
+ successors.foreach(s => if (!found_states.contains(s)) {found_states += curr; current_branches += curr})
}
}
- private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], UncertainValue] = ???
+ private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], UncertainValue] =
+ Map.from(vars.map(v => (v, v.t match {
+ case _: IntType[_] => UncertainIntegerValue.uncertain()
+ case _: TBool[_] => UncertainIntegerValue.uncertain()
+ })))
}
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index 86f64bf7b8..3cc9c0e74b 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -49,6 +49,10 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
can_be_true && other.can_be_false || can_be_false && other.can_be_true)
def !=(other: UncertainBooleanValue): UncertainBooleanValue = this ^ other
}
+case object UncertainBooleanValue {
+ def from(bool: Boolean): UncertainBooleanValue = UncertainBooleanValue(bool, !bool)
+ def uncertain(): UncertainBooleanValue = UncertainBooleanValue(can_be_true = true, can_be_false = true)
+}
case class UncertainIntegerValue(value: Interval) extends UncertainValue {
override def can_be_equal(other: UncertainValue): Boolean = other match {
From c215e4ec90f8ac85179c4db8a719c0570c160597 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 21 Feb 2024 14:38:18 +0100
Subject: [PATCH 39/85] Added option to compare booleans in expressions
---
.../vct/rewrite/rasi/AbstractState.scala | 12 +++++-----
.../vct/rewrite/rasi/UncertainValue.scala | 22 +++++++++++++++++++
2 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index 2e2dc18ec2..79252fd053 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -70,8 +70,8 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
}
case Length(arr) => UncertainIntegerValue.above(0) // TODO: Use contextual information from the global invariant
case Size(obj) => UncertainIntegerValue.above(0) // here as well
- case InvokeProcedure(_, _, _, _, _, _) => UncertainIntegerValue.uncertain() // TODO: return value from procedure/method?
- case InvokeMethod(_, _, _, _, _, _, _) => UncertainIntegerValue.uncertain()
+ case ProcedureInvocation(_, _, _, _, _, _) => UncertainIntegerValue.uncertain() // TODO: return value from procedure/method?
+ case MethodInvocation(_, _, _, _, _, _, _) => UncertainIntegerValue.uncertain()
}
def resolve_boolean_expression(expr: Expr[G]): UncertainBooleanValue = expr match {
@@ -81,8 +81,8 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
case And(left, right) => resolve_boolean_expression(left) && resolve_boolean_expression(right)
case Or(left, right) => resolve_boolean_expression(left) || resolve_boolean_expression(right)
case Implies(left, right) => (!resolve_boolean_expression(left)) || resolve_boolean_expression(right)
- case Eq(left, right) => resolve_integer_expression(left) == resolve_integer_expression(right)
- case Neq(left, right) => resolve_integer_expression(left) != resolve_integer_expression(right)
+ case Eq(left, right) => resolve_expression(left) == resolve_expression(right)
+ case Neq(left, right) => resolve_expression(left) != resolve_expression(right)
case AmbiguousGreater(left, right) => resolve_integer_expression(left) > resolve_integer_expression(right)
case AmbiguousLess(left, right) => resolve_integer_expression(left) < resolve_integer_expression(right)
case AmbiguousGreaterEq(left, right) => resolve_integer_expression(left) >= resolve_integer_expression(right)
@@ -102,8 +102,8 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
case Some(v) => valuations(v).asInstanceOf[UncertainBooleanValue]
case None => UncertainBooleanValue.uncertain()
}
- case InvokeProcedure(_, _, _, _, _, _) => UncertainBooleanValue.uncertain() // TODO: return value from procedure/method?
- case InvokeMethod(_, _, _, _, _, _, _) => UncertainBooleanValue.uncertain()
+ case ProcedureInvocation(_, _, _, _, _, _) => UncertainBooleanValue.uncertain() // TODO: return value from procedure/method?
+ case MethodInvocation(_, _, _, _, _, _, _) => UncertainBooleanValue.uncertain()
}
private def variable_from_expr(variable: Expr[G]): Option[ConcreteVariable[G]] = {
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index 3cc9c0e74b..1dd1ce36c8 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -6,6 +6,8 @@ trait UncertainValue {
def can_be_equal(other: UncertainValue): Boolean
def can_be_unequal(other: UncertainValue): Boolean
def to_expression[G](variable: Expr[G]): Expr[G]
+ def ==(other: UncertainValue): UncertainBooleanValue
+ def !=(other: UncertainValue): UncertainBooleanValue
}
case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) extends UncertainValue {
@@ -26,6 +28,16 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
else BooleanValue(value = false)(variable.o)
}
+ override def ==(other: UncertainValue): UncertainBooleanValue = other match {
+ case b: UncertainBooleanValue => this == b
+ case _ => UncertainBooleanValue.from(false)
+ }
+
+ override def !=(other: UncertainValue): UncertainBooleanValue = other match {
+ case b: UncertainBooleanValue => this != b
+ case _ => UncertainBooleanValue.from(true)
+ }
+
def try_to_resolve(): Option[Boolean] = {
if (can_be_true && !can_be_false) Some(true)
else if (can_be_false && !can_be_true) Some(false)
@@ -67,6 +79,16 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
override def to_expression[G](variable: Expr[G]): Expr[G] = value.to_expression(variable)
+ override def ==(other: UncertainValue): UncertainBooleanValue = other match {
+ case i: UncertainIntegerValue => this == i
+ case _ => UncertainBooleanValue.from(false)
+ }
+
+ override def !=(other: UncertainValue): UncertainBooleanValue = other match {
+ case i: UncertainIntegerValue => this != i
+ case _ => UncertainBooleanValue.from(true)
+ }
+
def try_to_resolve(): Option[Int] = value.try_to_resolve()
def union(other: UncertainIntegerValue): UncertainIntegerValue =
From c02cb2474aea1519ca5f741b578cc15b8bd28acf Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 21 Feb 2024 15:26:32 +0100
Subject: [PATCH 40/85] Added a printer for the RASI state space, but not
connected to the frontend yet
---
.../vct/rewrite/rasi/RASIGenerator.scala | 17 ++++++++++++++-
src/rewrite/vct/rewrite/rasi/Utils.scala | 21 +++++++++++++++++++
2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index 390c2ed436..d0f7299a20 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -3,6 +3,7 @@ package vct.rewrite.rasi
import vct.col.ast.{Expr, IntType, Or, TBool}
import vct.rewrite.cfg.CFGNode
+import java.nio.file.Path
import scala.collection.immutable.HashMap
import scala.collection.mutable
@@ -16,6 +17,12 @@ case class RASIGenerator[G]() {
found_states.distinctBy(s => s.valuations).map(s => s.to_expression).reduce((e1, e2) => Or(e1, e2)(e1.o))
}
+ def print_state_space(node: CFGNode[G], vars: Set[ConcreteVariable[G]], out_path: Path): Unit = {
+ explore(node, vars)
+ val (ns, es) = reduce_redundant_states()
+ Utils.print(ns, es, out_path)
+ }
+
private def explore(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Unit = {
val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G]("main"), node)), None)
found_states += initial_state
@@ -31,9 +38,17 @@ case class RASIGenerator[G]() {
}
}
- private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], UncertainValue] =
+ private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], UncertainValue] = {
+ // TODO: Should this be uncertain or should it be defined (e.g. 0/false)?
Map.from(vars.map(v => (v, v.t match {
case _: IntType[_] => UncertainIntegerValue.uncertain()
case _: TBool[_] => UncertainIntegerValue.uncertain()
})))
+ }
+
+ private def reduce_redundant_states(): (Seq[AbstractState[G]], Seq[(AbstractState[G], AbstractState[G])]) = {
+ val state_groups: Map[AbstractState[G], AbstractState[G]] = found_states.groupBy(s => s.valuations).flatMap(t => t._2.map(s => (s, t._2.head)))
+ val edge_groups: Seq[(AbstractState[G], AbstractState[G])] = Seq.from(found_edges.map(t => (state_groups(t._1), state_groups(t._2))).distinct)
+ (state_groups.keySet.toSeq, edge_groups)
+ }
}
diff --git a/src/rewrite/vct/rewrite/rasi/Utils.scala b/src/rewrite/vct/rewrite/rasi/Utils.scala
index c421bc35ff..bc4287dab1 100644
--- a/src/rewrite/vct/rewrite/rasi/Utils.scala
+++ b/src/rewrite/vct/rewrite/rasi/Utils.scala
@@ -1,9 +1,30 @@
package vct.rewrite.rasi
+import hre.io.RWFile
+
+import java.io.Writer
+import java.nio.file.Path
+
case object Utils {
def abs_max(a: Int, b: Int): Int = Seq(-a, a, -b, b).max
def prod_max(a1: Int, a2: Int, b1: Int, b2: Int): Int = Seq(a1 * b1, a1 * b2, a2 * b1, a2 * b2).max
def prod_min(a1: Int, a2: Int, b1: Int, b2: Int): Int = Seq(a1 * b1, a1 * b2, a2 * b1, a2 * b2).min
+
+ def print[G](states: Seq[AbstractState[G]], edges: Seq[(AbstractState[G], AbstractState[G])], out: Path): Unit = {
+ val node_names: Map[AbstractState[G], String] = Map.from(states.zipWithIndex.map(t => (t._1, s"n${t._2}")))
+ RWFile(out.toFile).write(w => print_state_space(node_names, edges, w))
+ }
+
+ private def print_state_space[G](names: Map[AbstractState[G], String], edges: Seq[(AbstractState[G], AbstractState[G])], writer: Writer): Unit = {
+ names.foreach(t => writer.append(t._2)
+ .append(s"[label=${"\""}")
+ .append(t._1.to_expression.toInlineString)
+ .append(s"${"\""}];"))
+ edges.foreach(t => writer.append(names(t._1))
+ .append(" -> ")
+ .append(names(t._2))
+ .append(";"))
+ }
}
From cff50d298ebbf40387e8c8b6f9bd85c10853c9ff Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 21 Feb 2024 16:59:08 +0100
Subject: [PATCH 41/85] Improved support for uncertain integer values
---
src/rewrite/vct/rewrite/rasi/Interval.scala | 24 ++++++++++++++-------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/Interval.scala b/src/rewrite/vct/rewrite/rasi/Interval.scala
index b75315d690..ea1d6e1695 100644
--- a/src/rewrite/vct/rewrite/rasi/Interval.scala
+++ b/src/rewrite/vct/rewrite/rasi/Interval.scala
@@ -77,9 +77,9 @@ case class MultiInterval(intervals: Set[Interval]) extends Interval {
if (new_intervals.size > 1) MultiInterval(new_intervals)
else new_intervals.head
}
- override def complement(): Interval = intervals.fold(UnboundedInterval)((i1, i2) => i1.intersection(i2.complement()))
- override def below_max(): Interval = intervals.fold(EmptyInterval)((i1, i2) => i1.union(i2.below_max()))
- override def above_min(): Interval = intervals.fold(EmptyInterval)((i1, i2) => i1.union(i2.above_min()))
+ override def complement(): Interval = intervals.foldLeft[Interval](UnboundedInterval)((i1, i2) => i1.intersection(i2.complement()))
+ override def below_max(): Interval = intervals.foldLeft[Interval](EmptyInterval)((i1, i2) => i1.union(i2.below_max()))
+ override def above_min(): Interval = intervals.foldLeft[Interval](EmptyInterval)((i1, i2) => i1.union(i2.above_min()))
override def +(other: Interval): Interval = {
val new_intervals = intervals.map(i => i + other)
// It could be that all intervals are now connected into one
@@ -113,16 +113,15 @@ case class MultiInterval(intervals: Set[Interval]) extends Interval {
if (new_intervals.size > 1) MultiInterval(new_intervals)
else new_intervals.head
}
+ private def merge_intersecting(is: Set[Interval]): Set[Interval] = MultiInterval(is).flatten().complement().complement().sub_intervals()
override def sub_intervals(): Set[Interval] = intervals.flatMap(i => i.sub_intervals())
+ private def flatten(): MultiInterval = MultiInterval(this.sub_intervals())
override def try_to_resolve(): Option[Int] = {
if (intervals.count(i => i != EmptyInterval) == 1) intervals.filter(i => i != EmptyInterval).head.try_to_resolve()
else None
}
- override def to_expression[G](variable: Expr[G]): Expr[G] =
+ override def to_expression[G](variable: Expr[G]): Expr[G] = {
intervals.map(i => i.to_expression(variable)).fold(BooleanValue[G](value = false)(variable.o))((e1, e2) => Or(e1, e2)(variable.o))
-
- private def merge_intersecting(is: Set[Interval]): Set[Interval] = {
- ???
}
}
@@ -189,7 +188,16 @@ case class BoundedInterval(lower: Int, upper: Int) extends Interval {
case UpperBoundedInterval(up) => ???
case UnboundedInterval => BoundedInterval(-Utils.abs_max(lower, upper), Utils.abs_max(lower, upper))
}
- override def %(other: Interval): Interval = ???
+ override def %(other: Interval): Interval = other match {
+ case EmptyInterval => other
+ case MultiInterval(intervals) => ???
+ case BoundedInterval(low, up) => ???
+ case LowerBoundedInterval(low) => ???
+ case UpperBoundedInterval(up) => ???
+ case UnboundedInterval =>
+ if (lower < 0) BoundedInterval(lower, scala.math.max(0, upper))
+ else BoundedInterval(0, upper)
+ }
override def unary_- : Interval = BoundedInterval(-upper, -lower)
override def pow(other: Interval): Interval = other match {
case EmptyInterval => other
From deb912f3497b46afd0f603b413e9c62b1046d475 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Wed, 21 Feb 2024 16:59:31 +0100
Subject: [PATCH 42/85] Added infrastructure for decoding assumption-induced
state changes
---
.../vct/rewrite/rasi/AbstractProcess.scala | 27 ++++++++++++-------
.../vct/rewrite/rasi/AbstractState.scala | 12 +++++++--
.../vct/rewrite/rasi/RASIGenerator.scala | 5 +---
.../vct/rewrite/rasi/UncertainValue.scala | 8 +++++-
4 files changed, 35 insertions(+), 17 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index 843cd58015..fc378555a2 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -11,16 +11,23 @@ case class AbstractProcess[G](name: String) {
case CFGNode(n, succ) => n match {
// Assign statements change the state of variables directly (if they appear in the valuation)
case Assign(target, value) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(target, state.resolve_expression(value))))
- case Havoc(loc) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(loc, loc.t match {
- case _: IntType[_] => UncertainIntegerValue.uncertain()
- case _: TBool[_] => UncertainBooleanValue.uncertain()
- })))
- // TODO: Consider state changes by specifications
- case Assume(assn) => viable_edges(succ, state).map(e => take_edge(e, state))
- case Inhale(res) => viable_edges(succ, state).map(e => take_edge(e, state))
- case InvokeProcedure(ref, _, _, _, _, _) => viable_edges(succ, state).map(e => take_edge(e, state))
- case InvokeConstructor(ref, _, _, _, _, _, _) => viable_edges(succ, state).map(e => take_edge(e, state))
- case InvokeMethod(_, ref, _, _, _, _, _) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case Havoc(loc) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(loc, UncertainValue.uncertain_of(loc.t))))
+ // Statements that induce assumptions about the state, such as assume, inhale, or a method's postcondition, might change the state implicitly
+ case Assume(assn) => viable_edges(succ, state).map(e => take_edge(e, state.with_assumption(assn)))
+ case Inhale(res) => viable_edges(succ, state).map(e => take_edge(e, state.with_assumption(res)))
+ // Abstract procedures, constructors and methods are defined by their postconditions
+ case InvokeProcedure(ref, args, _, _, _, _) => ref.decl.body match {
+ case Some(_) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case None => viable_edges(succ, state).map(e => take_edge(e, state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args)))))
+ }
+ case InvokeConstructor(ref, _, args, _, _, _, _) => ref.decl.body match {
+ case Some(_) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case None => viable_edges(succ, state).map(e => take_edge(e, state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args)))))
+ }
+ case InvokeMethod(_, ref, args, _, _, _, _) => ref.decl.body match {
+ case Some(_) => viable_edges(succ, state).map(e => take_edge(e, state))
+ case None => viable_edges(succ, state).map(e => take_edge(e, state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args)))))
+ }
// TODO: What do wait and notify do?
case Wait(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
case Notify(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index 79252fd053..17cfa7131e 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -1,6 +1,7 @@
package vct.rewrite.rasi
import vct.col.ast._
+import vct.col.util.AstBuildHelpers
import vct.rewrite.cfg.CFGEntry
import scala.collection.immutable.HashMap
@@ -15,14 +16,21 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
def without_process(process: AbstractProcess[G]): AbstractState[G] =
AbstractState(valuations, processes.removed(process), lock)
+ def locked_by(process: AbstractProcess[G]): AbstractState[G] = AbstractState(valuations, processes, Some(process))
+
+ def unlocked(): AbstractState[G] = AbstractState(valuations, processes, None)
+
def with_valuation(variable: Expr[G], value: UncertainValue): AbstractState[G] = variable_from_expr(variable) match {
case Some(concrete_variable) => AbstractState(valuations + (concrete_variable -> value), processes, lock)
case None => this
}
- def locked_by(process: AbstractProcess[G]): AbstractState[G] = AbstractState(valuations, processes, Some(process))
+ def with_assumption(assumption: Expr[G]): AbstractState[G] = ??? // TODO: Implement!
- def unlocked(): AbstractState[G] = AbstractState(valuations, processes, None)
+ def with_postcondition(post: AccountedPredicate[G], args: Map[Variable[G], Expr[G]]): AbstractState[G] =
+ with_assumption(unify_expression(AstBuildHelpers.unfoldPredicate(post).reduce((e1, e2) => Star(e1, e2)(e1.o)), args))
+
+ private def unify_expression(cond: Expr[G], args: Map[Variable[G], Expr[G]]): Expr[G] = cond // TODO: Consider arguments!
def resolve_expression(expr: Expr[G]): UncertainValue = expr.t match {
case _: IntType[_] => resolve_integer_expression(expr)
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index d0f7299a20..64d120359a 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -40,10 +40,7 @@ case class RASIGenerator[G]() {
private def get_initial_values(vars: Set[ConcreteVariable[G]]): Map[ConcreteVariable[G], UncertainValue] = {
// TODO: Should this be uncertain or should it be defined (e.g. 0/false)?
- Map.from(vars.map(v => (v, v.t match {
- case _: IntType[_] => UncertainIntegerValue.uncertain()
- case _: TBool[_] => UncertainIntegerValue.uncertain()
- })))
+ Map.from(vars.map(v => (v, UncertainValue.uncertain_of(v.t))))
}
private def reduce_redundant_states(): (Seq[AbstractState[G]], Seq[(AbstractState[G], AbstractState[G])]) = {
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index 1dd1ce36c8..b4a695b881 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -1,6 +1,6 @@
package vct.rewrite.rasi
-import vct.col.ast.{BooleanValue, Expr, Not}
+import vct.col.ast.{BooleanValue, Expr, IntType, Not, TBool, Type}
trait UncertainValue {
def can_be_equal(other: UncertainValue): Boolean
@@ -9,6 +9,12 @@ trait UncertainValue {
def ==(other: UncertainValue): UncertainBooleanValue
def !=(other: UncertainValue): UncertainBooleanValue
}
+case object UncertainValue {
+ def uncertain_of(t: Type[_]): UncertainValue = t match {
+ case _: IntType[_] => UncertainIntegerValue.uncertain()
+ case _: TBool[_] => UncertainBooleanValue.uncertain()
+ }
+}
case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) extends UncertainValue {
override def can_be_equal(other: UncertainValue): Boolean = other match {
From a97665bc7adcd25892d43c0df9848531100b544e Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 22 Feb 2024 15:47:14 +0100
Subject: [PATCH 43/85] Implemented extraction of state changes out of
assumptions and postconditions
---
.../vct/rewrite/rasi/AbstractProcess.scala | 10 +-
.../vct/rewrite/rasi/AbstractState.scala | 96 ++++++++++++++++++-
.../vct/rewrite/rasi/UncertainValue.scala | 13 +++
3 files changed, 109 insertions(+), 10 deletions(-)
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index fc378555a2..9c7d04d0d1 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -13,20 +13,20 @@ case class AbstractProcess[G](name: String) {
case Assign(target, value) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(target, state.resolve_expression(value))))
case Havoc(loc) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(loc, UncertainValue.uncertain_of(loc.t))))
// Statements that induce assumptions about the state, such as assume, inhale, or a method's postcondition, might change the state implicitly
- case Assume(assn) => viable_edges(succ, state).map(e => take_edge(e, state.with_assumption(assn)))
- case Inhale(res) => viable_edges(succ, state).map(e => take_edge(e, state.with_assumption(res)))
+ case Assume(assn) => viable_edges(succ, state).flatMap(e => state.with_assumption(assn).map(s => take_edge(e, s)))
+ case Inhale(res) => viable_edges(succ, state).flatMap(e => state.with_assumption(res).map(s => take_edge(e, s)))
// Abstract procedures, constructors and methods are defined by their postconditions
case InvokeProcedure(ref, args, _, _, _, _) => ref.decl.body match {
case Some(_) => viable_edges(succ, state).map(e => take_edge(e, state))
- case None => viable_edges(succ, state).map(e => take_edge(e, state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args)))))
+ case None => viable_edges(succ, state).flatMap(e => state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args))).map(s => take_edge(e, s)))
}
case InvokeConstructor(ref, _, args, _, _, _, _) => ref.decl.body match {
case Some(_) => viable_edges(succ, state).map(e => take_edge(e, state))
- case None => viable_edges(succ, state).map(e => take_edge(e, state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args)))))
+ case None => viable_edges(succ, state).flatMap(e => state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args))).map(s => take_edge(e, s)))
}
case InvokeMethod(_, ref, args, _, _, _, _) => ref.decl.body match {
case Some(_) => viable_edges(succ, state).map(e => take_edge(e, state))
- case None => viable_edges(succ, state).map(e => take_edge(e, state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args)))))
+ case None => viable_edges(succ, state).flatMap(e => state.with_postcondition(ref.decl.contract.ensures, Map.from(ref.decl.args.zip(args))).map(s => take_edge(e, s)))
}
// TODO: What do wait and notify do?
case Wait(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index 17cfa7131e..fb8bcb870f 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -1,7 +1,7 @@
package vct.rewrite.rasi
import vct.col.ast._
-import vct.col.util.AstBuildHelpers
+import vct.col.util.{AstBuildHelpers, Substitute}
import vct.rewrite.cfg.CFGEntry
import scala.collection.immutable.HashMap
@@ -21,16 +21,94 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
def unlocked(): AbstractState[G] = AbstractState(valuations, processes, None)
def with_valuation(variable: Expr[G], value: UncertainValue): AbstractState[G] = variable_from_expr(variable) match {
- case Some(concrete_variable) => AbstractState(valuations + (concrete_variable -> value), processes, lock)
+ case Some(concrete_variable) => val_updated(concrete_variable, value)
case None => this
}
+ private def val_updated(variable: ConcreteVariable[G], value: UncertainValue): AbstractState[G] =
+ AbstractState(valuations + (variable -> value), processes, lock)
+
+ def with_assumption(assumption: Expr[G]): Set[AbstractState[G]] = resolve_effect(assumption, negate = false)
+
+ private def resolve_effect(assumption: Expr[G], negate: Boolean): Set[AbstractState[G]] = assumption match {
+ // Consider boolean/separation logic operators
+ case Not(arg) => this.resolve_effect(arg, !negate)
+ case Star(left, right) => if (!negate) handle_and(left, right) else handle_or(left, right, neg_left = true, neg_right = true)
+ case And(left, right) => if (!negate) handle_and(left, right) else handle_or(left, right, neg_left = true, neg_right = true)
+ case Or(left, right) => if (!negate) handle_or(left, right) else handle_and(left, right, neg_left = true, neg_right = true)
+ case Implies(left, right) => if (!negate) handle_or(left, right, neg_left = true) else handle_and(left, right, neg_right = true)
+ // Atomic comparisons, if they contain a concrete variable, can actually affect the state
+ case c: Comparison[G] => handle_update(c, negate)
+ // Boolean variables could appear in the assumption without any comparison
+ case e: Expr[G] if valuations.keys.exists(v => v.is(e, this)) => Set(this.val_updated(variable_from_expr(e).get, UncertainBooleanValue.from(!negate)))
+ case _ => throw new IllegalArgumentException(s"Effect of contract expression ${assumption.toInlineString} is not implemented")
+ }
+
+ private def handle_and(left: Expr[G], right: Expr[G], neg_left: Boolean = false, neg_right: Boolean = false): Set[AbstractState[G]] =
+ this.resolve_effect(left, neg_left).flatMap(s => s.resolve_effect(right, neg_right))
+
+ private def handle_or(left: Expr[G], right: Expr[G], neg_left: Boolean = false, neg_right: Boolean = false): Set[AbstractState[G]] = {
+ val pure_left = is_pure(left)
+ val pure_right = is_pure(right)
+ // If one side is pure and the other has an effect on the state, treat this "or" as an implication. If both sides
+ // update the state, treat it as a split in the state space instead
+ if (pure_left && pure_right) Set(this)
+ else if (pure_left) handle_implies(left, right, !neg_left, neg_right)
+ else if (pure_right) handle_implies(right, left, !neg_right, neg_left)
+ else this.resolve_effect(left, neg_left) ++ this.resolve_effect(right, neg_right)
+ }
+
+ private def handle_implies(left: Expr[G], right: Expr[G], neg_left: Boolean = false, neg_right: Boolean = false): Set[AbstractState[G]] = {
+ val resolve_left: UncertainBooleanValue = resolve_boolean_expression(left)
+ var res: Set[AbstractState[G]] = Set()
+ if (neg_left && resolve_left.can_be_false || (!neg_left) && resolve_left.can_be_true) res = res ++ resolve_effect(right, neg_right)
+ if (neg_left && resolve_left.can_be_true || (!neg_left) && resolve_left.can_be_false) res = res ++ Set(this)
+ res
+ }
- def with_assumption(assumption: Expr[G]): AbstractState[G] = ??? // TODO: Implement!
+ private def handle_update(comp: Comparison[G], negate: Boolean): Set[AbstractState[G]] = {
+ val pure_left = is_pure(comp.left)
+ val pure_right = is_pure(comp.right)
+ if (pure_left == pure_right) return Set(this)
+ // Now, exactly one side is pure, and the other contains a concrete variable
+ // Resolve the variable and the other side of the equation TODO: Reduce the side with the variable if it contains anything else as well
+ val variable: ConcreteVariable[G] = if (pure_left) variable_from_expr(comp.right).get else variable_from_expr(comp.left).get
+ val value: UncertainValue = if (pure_left) resolve_expression(comp.left) else resolve_expression(comp.right)
+
+ comp match {
+ case _: Eq[_] => Set(if (!negate) this.val_updated(variable, value) else this.val_updated(variable, value.complement()))
+ case _: Neq[_] => Set(if (!negate) this.val_updated(variable, value.complement()) else this.val_updated(variable, value))
+ case AmbiguousGreater(_, _) | Greater(_, _) => bound_variable(variable, value.asInstanceOf[UncertainIntegerValue], pure_left == negate, negate)
+ case AmbiguousGreaterEq(_, _) | GreaterEq(_, _) => bound_variable(variable, value.asInstanceOf[UncertainIntegerValue], pure_left == negate, !negate)
+ case AmbiguousLessEq(_, _) | LessEq(_, _) => bound_variable(variable, value.asInstanceOf[UncertainIntegerValue], pure_left != negate, !negate)
+ case AmbiguousLess(_, _) | Less(_, _) => bound_variable(variable, value.asInstanceOf[UncertainIntegerValue], pure_left != negate, negate)
+ }
+ }
- def with_postcondition(post: AccountedPredicate[G], args: Map[Variable[G], Expr[G]]): AbstractState[G] =
+ private def bound_variable(v: ConcreteVariable[G], b: UncertainIntegerValue, var_greater: Boolean, can_be_equal: Boolean): Set[AbstractState[G]] = {
+ if (var_greater) {
+ if (can_be_equal) Set(this.val_updated(v, b.above_eq()))
+ else Set(this.val_updated(v, b.above()))
+ }
+ else {
+ if (can_be_equal) Set(this.val_updated(v, b.below_eq()))
+ else Set(this.val_updated(v, b.below()))
+ }
+ }
+
+ private def is_pure(node: Node[G]): Boolean = node match {
+ // The old value of a variable is always pure, since it cannot be updated
+ case _: Old[_] => true
+ case e: UnExpr[_] => e.subnodes.forall(n => is_pure(n))
+ case e: BinExpr[_] => e.subnodes.forall(n => is_pure(n))
+ case e: Expr[_] => if (valuations.keys.exists(v => v.is(e, this))) false else e.subnodes.forall(n => is_pure(n))
+ case _ => true
+ }
+
+ def with_postcondition(post: AccountedPredicate[G], args: Map[Variable[G], Expr[G]]): Set[AbstractState[G]] =
with_assumption(unify_expression(AstBuildHelpers.unfoldPredicate(post).reduce((e1, e2) => Star(e1, e2)(e1.o)), args))
- private def unify_expression(cond: Expr[G], args: Map[Variable[G], Expr[G]]): Expr[G] = cond // TODO: Consider arguments!
+ private def unify_expression(cond: Expr[G], args: Map[Variable[G], Expr[G]]): Expr[G] =
+ Substitute(args.map[Expr[G], Expr[G]]{ case (v, e) => Local[G](v.ref)(v.o) -> e }).dispatch(cond)
def resolve_expression(expr: Expr[G]): UncertainValue = expr.t match {
case _: IntType[_] => resolve_integer_expression(expr)
@@ -72,6 +150,10 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
if (resolve_boolean_expression(cond).can_be_false) value = value.union(resolve_integer_expression(iff))
value
}
+ case Old(exp, at) => at match {
+ case Some(_) => throw new IllegalArgumentException(s"Cannot resolve labelled old expression ${expr.toInlineString}")
+ case None => resolve_integer_expression(exp)
+ }
case Local(_) | DerefHeapVariable(_) | Deref(_, _) | DerefPointer(_) | AmbiguousSubscript(_, _) | SeqSubscript(_, _) | ArraySubscript(_, _) | PointerSubscript(_, _) => variable_from_expr(expr) match {
case Some(v) => valuations(v).asInstanceOf[UncertainIntegerValue]
case None => UncertainIntegerValue.uncertain()
@@ -106,6 +188,10 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
if (resolve_boolean_expression(cond).can_be_false) value = value.union(resolve_boolean_expression(iff))
value
}
+ case Old(exp, at) => at match {
+ case Some(_) => throw new IllegalArgumentException(s"Cannot resolve labelled old expression ${expr.toInlineString}")
+ case None => resolve_boolean_expression(exp)
+ }
case Local(_) | DerefHeapVariable(_) | Deref(_, _) | DerefPointer(_) | AmbiguousSubscript(_, _) | SeqSubscript(_, _) | ArraySubscript(_, _) | PointerSubscript(_, _) => variable_from_expr(expr) match {
case Some(v) => valuations(v).asInstanceOf[UncertainBooleanValue]
case None => UncertainBooleanValue.uncertain()
diff --git a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
index b4a695b881..540f9d16ee 100644
--- a/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
+++ b/src/rewrite/vct/rewrite/rasi/UncertainValue.scala
@@ -5,6 +5,7 @@ import vct.col.ast.{BooleanValue, Expr, IntType, Not, TBool, Type}
trait UncertainValue {
def can_be_equal(other: UncertainValue): Boolean
def can_be_unequal(other: UncertainValue): Boolean
+ def complement(): UncertainValue
def to_expression[G](variable: Expr[G]): Expr[G]
def ==(other: UncertainValue): UncertainBooleanValue
def !=(other: UncertainValue): UncertainBooleanValue
@@ -27,6 +28,8 @@ case class UncertainBooleanValue(can_be_true: Boolean, can_be_false: Boolean) ex
case _ => true
}
+ override def complement(): UncertainValue = !this
+
override def to_expression[G](variable: Expr[G]): Expr[G] = {
if (can_be_true && can_be_false) BooleanValue(value = true)(variable.o)
else if (can_be_true) variable
@@ -83,6 +86,11 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
case _ => true
}
+ override def complement(): UncertainValue = value match {
+ case BoundedInterval(lower, upper) if lower == upper => UncertainIntegerValue(value.complement())
+ case _ => UncertainIntegerValue.uncertain()
+ }
+
override def to_expression[G](variable: Expr[G]): Expr[G] = value.to_expression(variable)
override def ==(other: UncertainValue): UncertainBooleanValue = other match {
@@ -100,6 +108,11 @@ case class UncertainIntegerValue(value: Interval) extends UncertainValue {
def union(other: UncertainIntegerValue): UncertainIntegerValue =
UncertainIntegerValue(value.union(other.value))
+ def below_eq(): UncertainIntegerValue = UncertainIntegerValue(value.below_max())
+ def below(): UncertainIntegerValue = below_eq() + UncertainIntegerValue.single(-1)
+ def above_eq(): UncertainIntegerValue = UncertainIntegerValue(value.above_min())
+ def above(): UncertainIntegerValue = above_eq() + UncertainIntegerValue.single(1)
+
def ==(other: UncertainIntegerValue): UncertainBooleanValue =
UncertainBooleanValue(can_be_equal(other), can_be_unequal(other))
def !=(other: UncertainIntegerValue): UncertainBooleanValue =
From d9a5b856d61b23a3f5a2deb0afe32b43515b0e6e Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Thu, 22 Feb 2024 17:16:17 +0100
Subject: [PATCH 44/85] Started work on connecting the RASI generator to the
frontend (doesn't compile)
---
src/main/vct/main/stages/GenerateRASI.scala | 39 +++++++++++++++++++
src/main/vct/main/stages/PrintCFG.scala | 1 -
src/main/vct/main/stages/Stages.scala | 11 +++++-
src/main/vct/options/Options.scala | 10 ++++-
.../vct/rewrite/rasi/RASIGenerator.scala | 15 ++++---
5 files changed, 66 insertions(+), 10 deletions(-)
create mode 100644 src/main/vct/main/stages/GenerateRASI.scala
diff --git a/src/main/vct/main/stages/GenerateRASI.scala b/src/main/vct/main/stages/GenerateRASI.scala
new file mode 100644
index 0000000000..79f2a96a15
--- /dev/null
+++ b/src/main/vct/main/stages/GenerateRASI.scala
@@ -0,0 +1,39 @@
+package vct.main.stages
+
+import hre.stages.Stage
+import vct.col.ast.{InstanceField, InstanceMethod, Node}
+import vct.col.rewrite.Generation
+import vct.options.Options
+import vct.rewrite.rasi.{ConcreteVariable, FieldVariable, IndexedVariable, RASIGenerator}
+
+import java.nio.file.Path
+
+case object GenerateRASI {
+ def ofOptions(options: Options): Stage[Node[_ <: Generation], Unit] = {
+ GenerateRASI(options.vesuvRasiVariables, options.vesuvOutput)
+ }
+}
+
+case class GenerateRASI(vars: Option[Seq[String]], out: Path) extends Stage[Node[_ <: Generation], Unit] {
+
+ override def friendlyName: String = "Printing control flow graph"
+
+ override def progressWeight: Int = 0
+
+ override def run(in1: Node[_ <: Generation]): Unit = {
+ val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] if m.o.getPreferredName.get.snake.equals("main") => m }.get
+ val variables: Set[ConcreteVariable[_ <: Generation]] = vars.getOrElse(Seq()).map(s => resolve_variable(in1, s)).toSet
+ RASIGenerator().test(main_method, variables, out)
+ }
+
+ private def resolve_variable(in1: Node[_ <: Generation], name: String): ConcreteVariable[_ <: Generation] = {
+ val name_len = name.indexOf("[")
+ val var_name = if (name_len == -1) name else name.substring(0, name_len)
+ val index: Option[Integer] = if (name_len == -1) None else Some(Integer.valueOf(name.substring(name_len + 2, name.length - 1)))
+ val instance_field = in1.transSubnodes.collectFirst{ case f: InstanceField[_] if f.o.getPreferredName.get.snake.equals(var_name) => f }.get
+ index match {
+ case Some(i) => IndexedVariable(instance_field, i)
+ case None => FieldVariable(instance_field)
+ }
+ }
+}
diff --git a/src/main/vct/main/stages/PrintCFG.scala b/src/main/vct/main/stages/PrintCFG.scala
index e4c63e2e48..bc4adbd6b9 100644
--- a/src/main/vct/main/stages/PrintCFG.scala
+++ b/src/main/vct/main/stages/PrintCFG.scala
@@ -1,7 +1,6 @@
package vct.main.stages
import hre.stages.Stage
-import sourcecode.Name
import vct.col.ast.{InstanceMethod, Node}
import vct.col.rewrite.Generation
import vct.options.Options
diff --git a/src/main/vct/main/stages/Stages.scala b/src/main/vct/main/stages/Stages.scala
index 78cab0cb4a..cfe0e7760a 100644
--- a/src/main/vct/main/stages/Stages.scala
+++ b/src/main/vct/main/stages/Stages.scala
@@ -67,8 +67,15 @@ case object Stages {
}
def vesuvOfOptions(options: Options, blameProvider: BlameProvider) : Stages[Seq[Readable], Unit] = {
- Parsing.ofOptions(options, blameProvider)
- .thenRun(Output.vesuvOfOptions(options))
+ if (options.vesuvGenerateRasi) {
+ Parsing.ofOptions(options, blameProvider)
+ .thenRun(Resolution.ofOptions(options, blameProvider))
+ .thenRun(GenerateRASI.ofOptions(options))
+ }
+ else {
+ Parsing.ofOptions(options, blameProvider)
+ .thenRun(Output.vesuvOfOptions(options))
+ }
}
def cfgTransformationOfOptions(options: Options, blameProvider: BlameProvider): Stages[Seq[Readable], Unit] = {
diff --git a/src/main/vct/options/Options.scala b/src/main/vct/options/Options.scala
index 287369c192..8f83f6a969 100644
--- a/src/main/vct/options/Options.scala
+++ b/src/main/vct/options/Options.scala
@@ -264,8 +264,12 @@ case object Options {
.action((_, c) => c.copy(mode = Mode.VeSUV))
.text("Enable VeSUV mode: transform SystemC designs to PVL to be deductively verified")
.children(
- opt[Path]("vesuv-output").required().valueName("") // TODO: Give option for default location?
- .action((path, c) => c.copy(vesuvOutput = path))
+ opt[Path]("vesuv-output").required().valueName("")
+ .action((path, c) => c.copy(vesuvOutput = path)),
+ opt[Unit]("generate-rasi").action((_, c) => c.copy(vesuvGenerateRasi = true)).children(
+ opt[Seq[String]]("rasi-vars").required().valueName(",...")
+ .action((vars, c) => c.copy(vesuvRasiVariables = Some(vars)))
+ )
),
note(""),
@@ -412,6 +416,8 @@ case class Options
// VeSUV options
vesuvOutput: Path = null,
+ vesuvGenerateRasi: Boolean = false,
+ vesuvRasiVariables: Option[Seq[String]] = None,
// Control flow graph options
cfgOutput: Path = null,
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index 64d120359a..c50636a26d 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -1,7 +1,7 @@
package vct.rewrite.rasi
-import vct.col.ast.{Expr, IntType, Or, TBool}
-import vct.rewrite.cfg.CFGNode
+import vct.col.ast.{Expr, InstanceMethod, Or}
+import vct.rewrite.cfg.{CFGEntry, CFGGenerator}
import java.nio.file.Path
import scala.collection.immutable.HashMap
@@ -12,18 +12,23 @@ case class RASIGenerator[G]() {
private val found_edges: mutable.ArrayBuffer[(AbstractState[G], AbstractState[G])] = mutable.ArrayBuffer()
private val current_branches: mutable.ArrayBuffer[AbstractState[G]] = mutable.ArrayBuffer()
- def generate_rasi(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Expr[G] = {
+ def execute(entry_point: InstanceMethod[G], vars: Set[ConcreteVariable[G]]): Expr[G] =
+ generate_rasi(CFGGenerator().generate(entry_point), vars)
+ def test(entry_point: InstanceMethod[G], vars: Set[ConcreteVariable[G]], out_path: Path): Unit =
+ print_state_space(CFGGenerator().generate(entry_point), vars, out_path)
+
+ private def generate_rasi(node: CFGEntry[G], vars: Set[ConcreteVariable[G]]): Expr[G] = {
explore(node, vars)
found_states.distinctBy(s => s.valuations).map(s => s.to_expression).reduce((e1, e2) => Or(e1, e2)(e1.o))
}
- def print_state_space(node: CFGNode[G], vars: Set[ConcreteVariable[G]], out_path: Path): Unit = {
+ private def print_state_space(node: CFGEntry[G], vars: Set[ConcreteVariable[G]], out_path: Path): Unit = {
explore(node, vars)
val (ns, es) = reduce_redundant_states()
Utils.print(ns, es, out_path)
}
- private def explore(node: CFGNode[G], vars: Set[ConcreteVariable[G]]): Unit = {
+ private def explore(node: CFGEntry[G], vars: Set[ConcreteVariable[G]]): Unit = {
val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G]("main"), node)), None)
found_states += initial_state
current_branches += initial_state
From 8235eff21e870e08c71b62f772f487c00954583b Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 23 Feb 2024 09:29:04 +0100
Subject: [PATCH 45/85] Added comments to VeSUV command line options
---
src/main/vct/options/Options.scala | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/main/vct/options/Options.scala b/src/main/vct/options/Options.scala
index 8f83f6a969..133c5a583f 100644
--- a/src/main/vct/options/Options.scala
+++ b/src/main/vct/options/Options.scala
@@ -265,10 +265,14 @@ case object Options {
.text("Enable VeSUV mode: transform SystemC designs to PVL to be deductively verified")
.children(
opt[Path]("vesuv-output").required().valueName("")
- .action((path, c) => c.copy(vesuvOutput = path)),
- opt[Unit]("generate-rasi").action((_, c) => c.copy(vesuvGenerateRasi = true)).children(
+ .action((path, c) => c.copy(vesuvOutput = path))
+ .text("Output file for the result of the transformation"),
+ opt[Unit]("generate-rasi").action((_, c) => c.copy(vesuvGenerateRasi = true))
+ .text("Instead of transforming a SystemC design to PVL, generate a global invariant for a PVL program")
+ .children(
opt[Seq[String]]("rasi-vars").required().valueName(",...")
.action((vars, c) => c.copy(vesuvRasiVariables = Some(vars)))
+ .text("[WIP] Preliminary selection mechanism for RASI variables; might be replaced later")
)
),
From b64bf510c0b207c00ad1cf9c69f8106f6286586e Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 23 Feb 2024 12:57:09 +0100
Subject: [PATCH 46/85] Fixed type error, project compiles
---
src/main/vct/main/stages/GenerateRASI.scala | 9 +++++----
src/main/vct/options/Options.scala | 1 +
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/main/vct/main/stages/GenerateRASI.scala b/src/main/vct/main/stages/GenerateRASI.scala
index 79f2a96a15..5111c4939c 100644
--- a/src/main/vct/main/stages/GenerateRASI.scala
+++ b/src/main/vct/main/stages/GenerateRASI.scala
@@ -21,16 +21,17 @@ case class GenerateRASI(vars: Option[Seq[String]], out: Path) extends Stage[Node
override def progressWeight: Int = 0
override def run(in1: Node[_ <: Generation]): Unit = {
- val main_method = in1.transSubnodes.collectFirst{ case m: InstanceMethod[_] if m.o.getPreferredName.get.snake.equals("main") => m }.get
- val variables: Set[ConcreteVariable[_ <: Generation]] = vars.getOrElse(Seq()).map(s => resolve_variable(in1, s)).toSet
+ val in = in1.asInstanceOf[Node[Generation]]
+ val main_method = in.transSubnodes.collectFirst{ case m: InstanceMethod[_] if m.o.getPreferredName.get.snake.equals("main") => m }.get
+ val variables: Set[ConcreteVariable[Generation]] = vars.getOrElse(Seq()).map(s => resolve_variable(in, s)).toSet
RASIGenerator().test(main_method, variables, out)
}
- private def resolve_variable(in1: Node[_ <: Generation], name: String): ConcreteVariable[_ <: Generation] = {
+ private def resolve_variable(in: Node[Generation], name: String): ConcreteVariable[Generation] = {
val name_len = name.indexOf("[")
val var_name = if (name_len == -1) name else name.substring(0, name_len)
val index: Option[Integer] = if (name_len == -1) None else Some(Integer.valueOf(name.substring(name_len + 2, name.length - 1)))
- val instance_field = in1.transSubnodes.collectFirst{ case f: InstanceField[_] if f.o.getPreferredName.get.snake.equals(var_name) => f }.get
+ val instance_field = in.transSubnodes.collectFirst{ case f: InstanceField[_] if f.o.getPreferredName.get.snake.equals(var_name) => f }.get
index match {
case Some(i) => IndexedVariable(instance_field, i)
case None => FieldVariable(instance_field)
diff --git a/src/main/vct/options/Options.scala b/src/main/vct/options/Options.scala
index 133c5a583f..01d23e1062 100644
--- a/src/main/vct/options/Options.scala
+++ b/src/main/vct/options/Options.scala
@@ -284,6 +284,7 @@ case object Options {
.children(
opt[Path]("cfg-output").required().valueName("")
.action((path, c) => c.copy(cfgOutput = path))
+ .text("Output file for the control flow graph in .dot format")
),
note(""),
From 75446586656e200770a8b60334ced949a73e2cef Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 23 Feb 2024 13:05:03 +0100
Subject: [PATCH 47/85] Progress on switch/case implementation
---
.../vct/rewrite/cfg/CFGGenerator.scala | 39 ++++++++++++-------
src/rewrite/vct/rewrite/cfg/Index.scala | 2 -
2 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 0883a48b28..6688578347 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -22,12 +22,11 @@ case class CFGGenerator[G]() {
cfg_node.successors.addAll(find_successors(node, context))
// Handle labels and goto statements
node match {
- case label: Label[G] => {
+ case label: Label[_] =>
// For labels, add them to the label map and add them to any goto statements going to that label
found_labels.addOne((label.decl, cfg_node))
searched_labels.getOrElse(label.decl, mutable.Set()).map(g => g.successors.addOne(CFGEdge(cfg_node, None)))
- }
- case goto: Goto[G] =>
+ case goto: Goto[_] =>
// For goto statements, if the label could not be resolved, add the statement to the waiting list for the right label
if (cfg_node.successors.isEmpty) {
if (searched_labels.contains(goto.lbl.decl)) searched_labels(goto.lbl.decl).addOne(cfg_node)
@@ -53,7 +52,7 @@ case class CFGGenerator[G]() {
case Send(_, _, res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
case Recv(_) => sequential_successor(context)
case DefaultCase() => sequential_successor(context)
- case Case(pattern) => sequential_successor(context) // TODO: Handle expression side effects in switch case conditions
+ case Case(_) => sequential_successor(context)
case Label(_, _) => evaluate_first(context.enter_scope(node))
case Goto(lbl) => found_labels.get(lbl.decl) match {
case Some(node) => mutable.Set(CFGEdge(node, None))
@@ -104,7 +103,7 @@ case class CFGGenerator[G]() {
case Branch(_) => evaluate_first(context.enter_scope(node))
case IndetBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
- case s: Switch[G] => handle_switch_statement(s, context)
+ case s: Switch[_] => handle_switch_statement(s, context.enter_scope(node))
case Loop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
case RangedFor(_, _, _) => evaluate_first(context.enter_scope(node))
case TryCatchFinally(_, _, _) => evaluate_first(context.enter_scope(node))
@@ -159,27 +158,39 @@ case class CFGGenerator[G]() {
else sequential_successor(index)
}
- private def sequential_successor(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
- if (index.indices.nonEmpty) index.make_step().map(i => CFGEdge(resolve_index(i._1), i._2))
- else mutable.Set(CFGEdge(CFGTerminal(), None))
+ private def sequential_successor(index: GlobalIndex[G], cond: Option[Expr[G]] = None): mutable.Set[CFGEdge[G]] = {
+ if (index.indices.nonEmpty) index.make_step().map(i => CFGEdge(resolve_index(i._1), Utils.and(i._2, cond)))
+ else mutable.Set(CFGEdge(CFGTerminal(), cond))
}
private def handle_switch_statement(switch: Switch[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
val switches: Seq[(SwitchCase[G], GlobalIndex[G])] = Utils.find_all_cases(switch.body, context.enter_scope(switch))
- var conds: Seq[(Eval[G], GlobalIndex[G])] = Seq()
+ var conds: Seq[(Case[G], Eval[G], GlobalIndex[G])] = Seq()
var default: Option[(DefaultCase[G], GlobalIndex[G])] = None
for (s <- switches) {
s._1 match {
- case c @ Case(pattern) => {
+ case c @ Case(pattern) =>
val cond = Eq(switch.expr, pattern)(c.o)
// Collect only up to default case
- if (default.isEmpty) conds = conds :+ (Eval(cond)(cond.o), s._2)
- }
+ if (default.isEmpty) conds = conds :+ (c, Eval(cond)(cond.o), s._2)
case c @ DefaultCase() => default = Some((c, s._2))
}
}
- // TODO: Finish implementation
- ???
+ conds = conds.reverse
+
+ var previous_cond: Option[Expr[G]] = Some(Utils.negate(conds.head._2.expr))
+ var successor_to_previous: mutable.Set[CFGEdge[G]] = default match {
+ case Some(t) => mutable.Set(CFGEdge(convert(t._1, t._2), previous_cond))
+ case None => sequential_successor(context, previous_cond)
+ }
+
+ for (c <- conds) {
+ successor_to_previous.addOne(CFGEdge(convert(c._1, c._3), Some(c._2.expr)))
+ val node = CFGNode(c._2, successor_to_previous)
+ // TODO: Enter node at appropriate index in converted_nodes!
+ successor_to_previous = mutable.Set(CFGEdge(node, None)) // TODO: Add edge condition here!
+ }
+ successor_to_previous
}
private def return_successor(index: GlobalIndex[G]): CFGEntry[G] =
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index f4a0108be4..656ea5dce4 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -290,7 +290,6 @@ case class ExtractIndex[G](extract: Extract[G]) extends Index[G] {
case class EvalIndex[G](eval: Eval[G], index: Int, subexpressions: Seq[Statement[G]]) extends Index[G] {
def this(eval: Eval[G], index: Int) = this(eval, index, Utils.find_all_subexpressions(eval.expr))
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
- // TODO: Consider conditions in expressions, too
if (index < subexpressions.size - 1) Set((Step(EvalIndex(eval, index + 1)), None))
else Set((Outgoing(), None))
}
@@ -494,7 +493,6 @@ case class IndetBranchIndex[G](indet_branch: IndetBranch[G], index: Int) extends
}
}
-// TODO: Switch cases could be multiple context indices deep; this does not work with the single index for make_step()
case class SwitchIndex[G](switch: Switch[G]) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
override def resolve(): Statement[G] = switch.body
From 40d70590c7b942fb500365847e3fb7dc580ea08b Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Fri, 23 Feb 2024 16:23:44 +0100
Subject: [PATCH 48/85] Fixing the most severe bugs, program compiles and runs
---
src/main/vct/main/stages/GenerateRASI.scala | 2 +-
.../vct/rewrite/rasi/AbstractProcess.scala | 15 +++--
.../vct/rewrite/rasi/AbstractState.scala | 2 +-
src/rewrite/vct/rewrite/rasi/Interval.scala | 62 ++++++++++++++++---
.../vct/rewrite/rasi/RASIGenerator.scala | 9 +--
src/rewrite/vct/rewrite/rasi/Utils.scala | 6 +-
6 files changed, 74 insertions(+), 22 deletions(-)
diff --git a/src/main/vct/main/stages/GenerateRASI.scala b/src/main/vct/main/stages/GenerateRASI.scala
index 5111c4939c..b061092fe7 100644
--- a/src/main/vct/main/stages/GenerateRASI.scala
+++ b/src/main/vct/main/stages/GenerateRASI.scala
@@ -16,7 +16,7 @@ case object GenerateRASI {
case class GenerateRASI(vars: Option[Seq[String]], out: Path) extends Stage[Node[_ <: Generation], Unit] {
- override def friendlyName: String = "Printing control flow graph"
+ override def friendlyName: String = "Generate reachable abstract states invariant"
override def progressWeight: Int = 0
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
index 9c7d04d0d1..7dc6683c26 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractProcess.scala
@@ -5,12 +5,15 @@ import vct.rewrite.cfg.{CFGEdge, CFGEntry, CFGNode, CFGTerminal}
import scala.collection.mutable
-case class AbstractProcess[G](name: String) {
+case class AbstractProcess[G](obj: Expr[G]) {
def get_next(node: CFGEntry[G], state: AbstractState[G]): Set[AbstractState[G]] = node match {
case CFGTerminal() => Set(state.without_process(this))
case CFGNode(n, succ) => n match {
// Assign statements change the state of variables directly (if they appear in the valuation)
- case Assign(target, value) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(target, state.resolve_expression(value))))
+ case Assign(target, value) => target.t match {
+ case _: IntType[_] | TBool() => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(target, state.resolve_expression(value))))
+ case _ => viable_edges(succ, state).map(e => take_edge(e, state))
+ }
case Havoc(loc) => viable_edges(succ, state).map(e => take_edge(e, state.with_valuation(loc, UncertainValue.uncertain_of(loc.t))))
// Statements that induce assumptions about the state, such as assume, inhale, or a method's postcondition, might change the state implicitly
case Assume(assn) => viable_edges(succ, state).flatMap(e => state.with_assumption(assn).map(s => take_edge(e, s)))
@@ -42,14 +45,16 @@ case class AbstractProcess[G](name: String) {
else throw new IllegalStateException("Trying to unlock lock owned by other process")
case None => throw new IllegalStateException("Trying to unlock unlocked lock")
}
- // When forking a new process, make the step of creating it simultaneously to the normal steps TODO: consider join
+ // When forking a new process, make the step of creating it simultaneously to the normal steps
case Fork(obj) =>
val edges: (Set[CFGEdge[G]], Set[CFGEdge[G]]) = viable_edges(succ, state).partition(e => e.target match {
case CFGTerminal() => false
case CFGNode(t, _) => t.equals(obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head.body.get)
})
- edges._2.map(e => take_edge(e, state.with_process_at(AbstractProcess(s"${name}_${obj.toInlineString}"), edges._1.head.target)))
- case Join(obj) => viable_edges(succ, state).map(e => take_edge(e, state))
+ edges._2.map(e => take_edge(e, state.with_process_at(AbstractProcess(obj), edges._1.head.target))) // TODO: Can only one thread per class instance be launched?
+ case Join(obj) =>
+ if (state.processes.keys.forall(p => p.obj != obj)) viable_edges(succ, state).map(e => take_edge(e, state))
+ else Set(state)
// Everything else does not affect the state, so simply go to the next step
case _ => viable_edges(succ, state).map(e => take_edge(e, state))
}
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index fb8bcb870f..a35ac1e76a 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -11,7 +11,7 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
processes.flatMap(p => p._1.get_next(p._2, this)).toSet
def with_process_at(process: AbstractProcess[G], position: CFGEntry[G]): AbstractState[G] =
- AbstractState(valuations, processes + (process -> position), lock)
+ AbstractState(valuations, processes.removed(process) + (process -> position), lock)
def without_process(process: AbstractProcess[G]): AbstractState[G] =
AbstractState(valuations, processes.removed(process), lock)
diff --git a/src/rewrite/vct/rewrite/rasi/Interval.scala b/src/rewrite/vct/rewrite/rasi/Interval.scala
index ea1d6e1695..f2c05dfb27 100644
--- a/src/rewrite/vct/rewrite/rasi/Interval.scala
+++ b/src/rewrite/vct/rewrite/rasi/Interval.scala
@@ -26,6 +26,49 @@ case class Infinite() extends IntervalSize {
override def get: Int = throw new NoSuchElementException("Accessing infinite interval size element")
}
+/**
+ * An interval represents a set of integers. It can be empty (type EmptyInterval
), unbounded (type
+ * UnboundedInterval
), bounded by two integers (type BoundedInterval
), below an upper bound
+ * (type UpperBoundedInterval
), above a lower bound (type LowerBoundedInterval
) or a union of
+ * a finite set of other intervals (type MultiInterval
). If it is bounded, then the bounds are contained in
+ * the interval.
+ *
+ * An interval supports the following operations:
+ *
+ * SIZE OPERATIONS
+ * - empty()
returns true
if the interval is empty and false
otherwise
+ * - non_empty()
returns false
if the interval is empty and true
otherwise
+ * - size()
returns the size of the interval in the form of Finite(n)
if the interval is
+ * finite and of size n
, or Infinite()
otherwise
+ *
+ * SET OPERATIONS
+ * - intersection(Interval)
returns the intersection between this interval and the argument
+ * - union(Interval)
returns the union of this interval and the argument
+ * - complement()
returns the interval representing all integers that are not contained in this interval
+ *
+ * ARITHMETIC OPERATIONS
+ * - below_max()
returns an interval whose upper bound is the maximum entry in this interval, or an
+ * unbounded interval if there is no maximum
+ * - above_min()
returns an interval whose lower bound is the minimum entry in this interval, or an
+ * unbounded interval if there is no minimum
+ * - +
returns the interval representing the set of sums of entries in both intervals
+ * - -
returns the interval representing the set of subtractions of entries of the second interval from
+ * entries in the first
+ * - *
returns the interval overapproximating the set of products from both intervals. Since this is not
+ * generally a contiguous interval, this overapproximates to the interval between the minimum and maximum product or,
+ * if multiple such intervals can be defined, to the union of these intervals
+ * - unary_-
returns the element-wise negation of this interval
+ * - /
NOT IMPLEMENTED
+ * - %
NOT IMPLEMENTED
+ * - pow
NOT IMPLEMENTED
+ *
+ * ADMINISTRATIVE OPERATIONS
+ * - sub_intervals()
returns the minimum set of contiguous intervals this interval is a union of
+ * - try_to_resolve()
if this interval contains exactly one integer, returns this integer, otherwise
+ * returns None
+ * - to_expression(Variable)
returns an expression indicating that the given variable is within this
+ * interval
+ */
sealed abstract class Interval {
def empty(): Boolean
def non_empty(): Boolean = !empty()
@@ -113,7 +156,7 @@ case class MultiInterval(intervals: Set[Interval]) extends Interval {
if (new_intervals.size > 1) MultiInterval(new_intervals)
else new_intervals.head
}
- private def merge_intersecting(is: Set[Interval]): Set[Interval] = MultiInterval(is).flatten().complement().complement().sub_intervals()
+ private def merge_intersecting(is: Set[Interval]): Set[Interval] = MultiInterval(is).sub_intervals().reduce((i1, i2) => i1.union(i2)).sub_intervals()
override def sub_intervals(): Set[Interval] = intervals.flatMap(i => i.sub_intervals())
private def flatten(): MultiInterval = MultiInterval(this.sub_intervals())
override def try_to_resolve(): Option[Int] = {
@@ -146,13 +189,13 @@ case class BoundedInterval(lower: Int, upper: Int) extends Interval {
case EmptyInterval => this
case mi: MultiInterval => mi.union(this)
case BoundedInterval(low, up) =>
- if (up <= upper && up >= lower || low <= upper && low >= lower) BoundedInterval(scala.math.min(low, lower), scala.math.max(up, upper))
+ if (up <= upper + 1 && up >= lower - 1 || low <= upper + 1 && low >= lower - 1) BoundedInterval(scala.math.min(low, lower), scala.math.max(up, upper))
else MultiInterval(Set(this, other))
case LowerBoundedInterval(low) =>
- if (upper >= low) LowerBoundedInterval(scala.math.min(low, lower))
+ if (upper + 1 >= low) LowerBoundedInterval(scala.math.min(low, lower))
else MultiInterval(Set(this, other))
case UpperBoundedInterval(up) =>
- if (lower <= up) UpperBoundedInterval(scala.math.max(up, upper))
+ if (lower - 1 <= up) UpperBoundedInterval(scala.math.max(up, upper))
else MultiInterval(Set(this, other))
case UnboundedInterval => other
}
@@ -206,7 +249,8 @@ case class BoundedInterval(lower: Int, upper: Int) extends Interval {
case LowerBoundedInterval(low) => ???
case UpperBoundedInterval(up) => ???
case UnboundedInterval =>
- if (lower < 0) other
+ if (lower < -1) other
+ else if (lower == -1) LowerBoundedInterval(-1)
else LowerBoundedInterval(0)
}
override def try_to_resolve(): Option[Int] = {
@@ -238,11 +282,11 @@ case class LowerBoundedInterval(lower: Int) extends Interval {
case EmptyInterval => this
case mi: MultiInterval => mi.union(this)
case BoundedInterval(low, up) =>
- if (up >= lower) LowerBoundedInterval(scala.math.min(low, lower))
+ if (up + 1 >= lower) LowerBoundedInterval(scala.math.min(low, lower))
else MultiInterval(Set(other, this))
case LowerBoundedInterval(low) => LowerBoundedInterval(scala.math.min(low, lower))
case UpperBoundedInterval(up) =>
- if (up >= lower) UnboundedInterval
+ if (up + 1 >= lower) UnboundedInterval
else MultiInterval(Set(other, this))
case UnboundedInterval => other
}
@@ -294,10 +338,10 @@ case class UpperBoundedInterval(upper: Int) extends Interval {
case EmptyInterval => this
case mi: MultiInterval => mi.union(this)
case BoundedInterval(low, up) =>
- if (low <= upper) UpperBoundedInterval(scala.math.max(upper, up))
+ if (low - 1 <= upper) UpperBoundedInterval(scala.math.max(upper, up))
else MultiInterval(Set(this, other))
case LowerBoundedInterval(low) =>
- if (low <= upper) UnboundedInterval
+ if (low - 1 <= upper) UnboundedInterval
else MultiInterval(Set(this, other))
case UpperBoundedInterval(up) => UpperBoundedInterval(scala.math.max(upper, up))
case UnboundedInterval => other
diff --git a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
index c50636a26d..95433bb7b2 100644
--- a/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
+++ b/src/rewrite/vct/rewrite/rasi/RASIGenerator.scala
@@ -1,6 +1,7 @@
package vct.rewrite.rasi
-import vct.col.ast.{Expr, InstanceMethod, Or}
+import vct.col.ast.{Expr, InstanceMethod, Null, Or}
+import vct.col.origin.Origin
import vct.rewrite.cfg.{CFGEntry, CFGGenerator}
import java.nio.file.Path
@@ -29,7 +30,7 @@ case class RASIGenerator[G]() {
}
private def explore(node: CFGEntry[G], vars: Set[ConcreteVariable[G]]): Unit = {
- val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G]("main"), node)), None)
+ val initial_state = AbstractState(get_initial_values(vars), HashMap((AbstractProcess[G](Null()(Origin(Seq()))), node)), None)
found_states += initial_state
current_branches += initial_state
@@ -39,7 +40,7 @@ case class RASIGenerator[G]() {
val successors: Set[AbstractState[G]] = curr.successors()
found_edges.addAll(successors.map(s => (curr, s)))
- successors.foreach(s => if (!found_states.contains(s)) {found_states += curr; current_branches += curr})
+ successors.foreach(s => if (!found_states.contains(s)) {found_states += s; current_branches += s})
}
}
@@ -51,6 +52,6 @@ case class RASIGenerator[G]() {
private def reduce_redundant_states(): (Seq[AbstractState[G]], Seq[(AbstractState[G], AbstractState[G])]) = {
val state_groups: Map[AbstractState[G], AbstractState[G]] = found_states.groupBy(s => s.valuations).flatMap(t => t._2.map(s => (s, t._2.head)))
val edge_groups: Seq[(AbstractState[G], AbstractState[G])] = Seq.from(found_edges.map(t => (state_groups(t._1), state_groups(t._2))).distinct)
- (state_groups.keySet.toSeq, edge_groups)
+ (state_groups.values.toSeq.distinct, edge_groups)
}
}
diff --git a/src/rewrite/vct/rewrite/rasi/Utils.scala b/src/rewrite/vct/rewrite/rasi/Utils.scala
index bc4287dab1..3274c780dc 100644
--- a/src/rewrite/vct/rewrite/rasi/Utils.scala
+++ b/src/rewrite/vct/rewrite/rasi/Utils.scala
@@ -18,13 +18,15 @@ case object Utils {
}
private def print_state_space[G](names: Map[AbstractState[G], String], edges: Seq[(AbstractState[G], AbstractState[G])], writer: Writer): Unit = {
+ writer.append("digraph {\n")
names.foreach(t => writer.append(t._2)
.append(s"[label=${"\""}")
.append(t._1.to_expression.toInlineString)
- .append(s"${"\""}];"))
+ .append(s"${"\""}];\n"))
edges.foreach(t => writer.append(names(t._1))
.append(" -> ")
.append(names(t._2))
- .append(";"))
+ .append(";\n"))
+ writer.append("}")
}
}
From e445aa22665b23f19d392e4588380afa4a6d2900 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 26 Feb 2024 14:02:39 +0100
Subject: [PATCH 49/85] Refactored CFG generation to use statement supertypes
instead of pattern matching all statements
---
src/col/vct/col/ast/Node.scala | 136 +++++++++---------
.../lang/silver/SilverFieldAssignImpl.scala | 4 +-
.../lang/silver/SilverLocalAssignImpl.scala | 4 +-
.../ExpressionContainerStatementImpl.scala | 8 ++
.../ast/statement/terminal/AssertImpl.scala | 4 +-
.../ast/statement/terminal/AssumeImpl.scala | 4 +-
.../ast/statement/terminal/CommitImpl.scala | 4 +-
.../ast/statement/terminal/ExhaleImpl.scala | 4 +-
.../col/ast/statement/terminal/FoldImpl.scala | 4 +-
.../col/ast/statement/terminal/ForkImpl.scala | 4 +-
.../ast/statement/terminal/HavocImpl.scala | 4 +-
.../ast/statement/terminal/InhaleImpl.scala | 4 +-
.../col/ast/statement/terminal/JoinImpl.scala | 4 +-
.../col/ast/statement/terminal/LockImpl.scala | 4 +-
.../ast/statement/terminal/NotifyImpl.scala | 4 +-
.../ast/statement/terminal/RefuteImpl.scala | 4 +-
.../col/ast/statement/terminal/SendImpl.scala | 6 +-
.../ast/statement/terminal/UnfoldImpl.scala | 4 +-
.../ast/statement/terminal/UnlockImpl.scala | 4 +-
.../col/ast/statement/terminal/WaitImpl.scala | 4 +-
.../statement/terminal/WandApplyImpl.scala | 4 +-
.../statement/veymont/PVLSeqAssignImpl.scala | 4 +-
.../ast/statement/veymont/SeqAssignImpl.scala | 4 +-
.../col/ast/unsorted/InstantiateImpl.scala | 3 +-
.../vct/rewrite/cfg/CFGGenerator.scala | 93 ++----------
.../vct/rewrite/rasi/AbstractState.scala | 2 +-
26 files changed, 158 insertions(+), 170 deletions(-)
create mode 100644 src/col/vct/col/ast/statement/behavior/ExpressionContainerStatementImpl.scala
diff --git a/src/col/vct/col/ast/Node.scala b/src/col/vct/col/ast/Node.scala
index df74796728..9301b54e4a 100644
--- a/src/col/vct/col/ast/Node.scala
+++ b/src/col/vct/col/ast/Node.scala
@@ -72,6 +72,7 @@ import vct.col.ast.lang.smt._
import vct.col.ast.lang.sycl._
import vct.col.ast.node._
import vct.col.ast.statement._
+import vct.col.ast.statement.behavior.ExpressionContainerStatementImpl
import vct.col.ast.statement.composite._
import vct.col.ast.statement.exceptional._
import vct.col.ast.statement.nonexecutable._
@@ -178,72 +179,75 @@ final case class IterationContract[G](requires: Expr[G], ensures: Expr[G], conte
@family final case class IterVariable[G](variable: Variable[G], from: Expr[G], to: Expr[G])(implicit val o: Origin) extends NodeFamily[G] with IterVariableImpl[G]
@family sealed trait Statement[G] extends NodeFamily[G] with StatementImpl[G]
+sealed trait PurelySequentialStatement[G] extends Statement[G]
+sealed trait ControlContainerStatement[G] extends Statement[G]
+sealed trait ExpressionContainerStatement[G] extends Statement[G] with ExpressionContainerStatementImpl[G]
-final case class PVLBranch[G](branches: Seq[(Expr[G], Statement[G])])(val blame: Blame[FrontendIfFailure])(implicit val o: Origin) extends Statement[G] with PVLBranchImpl[G]
-final case class PVLLoop[G](init: Statement[G], cond: Expr[G], update: Statement[G], contract: LoopContract[G], body: Statement[G])(val blame: Blame[FrontEndLoopFailure])(implicit val o: Origin) extends Statement[G] with PVLLoopImpl[G]
+final case class PVLBranch[G](branches: Seq[(Expr[G], Statement[G])])(val blame: Blame[FrontendIfFailure])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with PVLBranchImpl[G]
+final case class PVLLoop[G](init: Statement[G], cond: Expr[G], update: Statement[G], contract: LoopContract[G], body: Statement[G])(val blame: Blame[FrontEndLoopFailure])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with PVLLoopImpl[G]
sealed trait NonExecutableStatement[G] extends Statement[G] with NonExecutableStatementImpl[G]
-final case class LocalDecl[G](local: Variable[G])(implicit val o: Origin) extends NonExecutableStatement[G] with LocalDeclImpl[G]
-final case class SpecIgnoreStart[G]()(implicit val o: Origin) extends NonExecutableStatement[G] with SpecIgnoreStartImpl[G]
-final case class SpecIgnoreEnd[G]()(implicit val o: Origin) extends NonExecutableStatement[G] with SpecIgnoreEndImpl[G]
+final case class LocalDecl[G](local: Variable[G])(implicit val o: Origin) extends NonExecutableStatement[G] with PurelySequentialStatement[G] with LocalDeclImpl[G]
+final case class SpecIgnoreStart[G]()(implicit val o: Origin) extends NonExecutableStatement[G] with PurelySequentialStatement[G] with SpecIgnoreStartImpl[G]
+final case class SpecIgnoreEnd[G]()(implicit val o: Origin) extends NonExecutableStatement[G] with PurelySequentialStatement[G] with SpecIgnoreEndImpl[G]
sealed trait NormallyCompletingStatement[G] extends Statement[G] with NormallyCompletingStatementImpl[G]
final case class Assign[G](target: Expr[G], value: Expr[G])(val blame: Blame[AssignFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with AssignImpl[G]
-final case class Send[G](decl: SendDecl[G], delta: BigInt, res: Expr[G])(val blame: Blame[SendFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with SendImpl[G]
-final case class Recv[G](ref: Ref[G, SendDecl[G]])(implicit val o: Origin) extends NormallyCompletingStatement[G] with RecvImpl[G]
+final case class Send[G](decl: SendDecl[G], delta: BigInt, res: Expr[G])(val blame: Blame[SendFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with SendImpl[G]
+final case class Recv[G](ref: Ref[G, SendDecl[G]])(implicit val o: Origin) extends NormallyCompletingStatement[G] with PurelySequentialStatement[G] with RecvImpl[G]
sealed trait SwitchCase[G] extends NormallyCompletingStatement[G] with SwitchCaseImpl[G]
-final case class DefaultCase[G]()(implicit val o: Origin) extends SwitchCase[G] with DefaultCaseImpl[G]
-final case class Case[G](pattern: Expr[G])(implicit val o: Origin) extends SwitchCase[G] with CaseImpl[G]
-final case class Label[G](decl: LabelDecl[G], stat: Statement[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with LabelImpl[G]
+final case class DefaultCase[G]()(implicit val o: Origin) extends SwitchCase[G] with PurelySequentialStatement[G] with DefaultCaseImpl[G]
+final case class Case[G](pattern: Expr[G])(implicit val o: Origin) extends SwitchCase[G] with PurelySequentialStatement[G] with CaseImpl[G]
+final case class Label[G](decl: LabelDecl[G], stat: Statement[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ControlContainerStatement[G] with LabelImpl[G]
final case class Goto[G](lbl: Ref[G, LabelDecl[G]])(implicit val o: Origin) extends NormallyCompletingStatement[G] with GotoImpl[G]
-final case class Exhale[G](res: Expr[G])(val blame: Blame[ExhaleFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExhaleImpl[G]
-final case class Assert[G](res: Expr[G])(val blame: Blame[AssertFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with AssertImpl[G]
-final case class Refute[G](assn: Expr[G])(val blame: Blame[RefuteFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with RefuteImpl[G]
-final case class Inhale[G](res: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with InhaleImpl[G]
-final case class Assume[G](assn: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with AssumeImpl[G]
-final case class Instantiate[G](cls: Ref[G, Class[G]], out: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with InstantiateImpl[G]
-final case class Wait[G](obj: Expr[G])(val blame: Blame[UnlockFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with WaitImpl[G]
-final case class Notify[G](obj: Expr[G])(val blame: Blame[NotifyFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with NotifyImpl[G]
-final case class Fork[G](obj: Expr[G])(val blame: Blame[ForkFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ForkImpl[G]
-final case class Join[G](obj: Expr[G])(val blame: Blame[JoinFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with JoinImpl[G]
-final case class Lock[G](obj: Expr[G])(val blame: Blame[LockFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with LockImpl[G]
-final case class Unlock[G](obj: Expr[G])(val blame: Blame[UnlockFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with UnlockImpl[G]
-final case class Commit[G](obj: Expr[G])(val blame: Blame[CommitFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with CommitImpl[G]
-final case class Fold[G](res: Expr[G])(val blame: Blame[FoldFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with FoldImpl[G]
-final case class Unfold[G](res: Expr[G])(val blame: Blame[UnfoldFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with UnfoldImpl[G]
-final case class WandApply[G](res: Expr[G])(val blame: Blame[WandApplyFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with WandApplyImpl[G]
-final case class Havoc[G](loc: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with HavocImpl[G]
-final case class FramedProof[G](pre: Expr[G], body: Statement[G], post: Expr[G])(val blame: Blame[FramedProofFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with FramedProofImpl[G]
-final case class Extract[G](contractedStatement: Statement[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExtractImpl[G]
+final case class Exhale[G](res: Expr[G])(val blame: Blame[ExhaleFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with ExhaleImpl[G]
+final case class Assert[G](res: Expr[G])(val blame: Blame[AssertFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with AssertImpl[G]
+final case class Refute[G](assn: Expr[G])(val blame: Blame[RefuteFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with RefuteImpl[G]
+final case class Inhale[G](res: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with InhaleImpl[G]
+final case class Assume[G](assn: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with AssumeImpl[G]
+final case class Instantiate[G](cls: Ref[G, Class[G]], out: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with InstantiateImpl[G]
+final case class Wait[G](obj: Expr[G])(val blame: Blame[UnlockFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with WaitImpl[G]
+final case class Notify[G](obj: Expr[G])(val blame: Blame[NotifyFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with NotifyImpl[G]
+final case class Fork[G](obj: Expr[G])(val blame: Blame[ForkFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with ForkImpl[G]
+final case class Join[G](obj: Expr[G])(val blame: Blame[JoinFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with JoinImpl[G]
+final case class Lock[G](obj: Expr[G])(val blame: Blame[LockFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with LockImpl[G]
+final case class Unlock[G](obj: Expr[G])(val blame: Blame[UnlockFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with UnlockImpl[G]
+final case class Commit[G](obj: Expr[G])(val blame: Blame[CommitFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with CommitImpl[G]
+final case class Fold[G](res: Expr[G])(val blame: Blame[FoldFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with FoldImpl[G]
+final case class Unfold[G](res: Expr[G])(val blame: Blame[UnfoldFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with UnfoldImpl[G]
+final case class WandApply[G](res: Expr[G])(val blame: Blame[WandApplyFailed])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with WandApplyImpl[G]
+final case class Havoc[G](loc: Expr[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ExpressionContainerStatement[G] with HavocImpl[G]
+final case class FramedProof[G](pre: Expr[G], body: Statement[G], post: Expr[G])(val blame: Blame[FramedProofFailure])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ControlContainerStatement[G] with FramedProofImpl[G]
+final case class Extract[G](contractedStatement: Statement[G])(implicit val o: Origin) extends NormallyCompletingStatement[G] with ControlContainerStatement[G] with ExtractImpl[G]
sealed trait ExceptionalStatement[G] extends Statement[G] with ExceptionalStatementImpl[G]
-final case class Eval[G](expr: Expr[G])(implicit val o: Origin) extends ExceptionalStatement[G] with EvalImpl[G]
+final case class Eval[G](expr: Expr[G])(implicit val o: Origin) extends ExceptionalStatement[G] with ControlContainerStatement[G] with EvalImpl[G]
sealed trait InvocationStatement[G] extends ExceptionalStatement[G] with InvokingNode[G] with InvocationStatementImpl[G]
-final case class InvokeProcedure[G](ref: Ref[G, Procedure[G]], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with InvokeProcedureImpl[G]
-final case class InvokeConstructor[G](ref: Ref[G, Constructor[G]], out: Expr[G], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with InvokeConstructorImpl[G]
-final case class InvokeMethod[G](obj: Expr[G], ref: Ref[G, InstanceMethod[G]], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InstanceInvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with InstanceApply[G] with InvokeMethodImpl[G]
+final case class InvokeProcedure[G](ref: Ref[G, Procedure[G]], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with ControlContainerStatement[G] with InvokeProcedureImpl[G]
+final case class InvokeConstructor[G](ref: Ref[G, Constructor[G]], out: Expr[G], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with ControlContainerStatement[G] with InvokeConstructorImpl[G]
+final case class InvokeMethod[G](obj: Expr[G], ref: Ref[G, InstanceMethod[G]], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InstanceInvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with InstanceApply[G] with ControlContainerStatement[G] with InvokeMethodImpl[G]
final case class Return[G](result: Expr[G])(implicit val o: Origin) extends ExceptionalStatement[G] with ReturnImpl[G]
final case class Throw[G](obj: Expr[G])(val blame: Blame[ThrowNull])(implicit val o: Origin) extends ExceptionalStatement[G] with ThrowImpl[G]
final case class Break[G](label: Option[Ref[G, LabelDecl[G]]])(implicit val o: Origin) extends ExceptionalStatement[G] with BreakImpl[G]
final case class Continue[G](label: Option[Ref[G, LabelDecl[G]]])(implicit val o: Origin) extends ExceptionalStatement[G] with ContinueImpl[G]
sealed trait CompositeStatement[G] extends Statement[G] with CompositeStatementImpl[G]
-final case class Block[G](statements: Seq[Statement[G]])(implicit val o: Origin) extends CompositeStatement[G] with BlockImpl[G]
-@scopes[Variable] @scopes[CLocalDeclaration] @scopes[CPPLocalDeclaration] @scopes[JavaLocalDeclaration] final case class Scope[G](locals: Seq[Variable[G]], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ScopeImpl[G]
-final case class Branch[G](branches: Seq[(Expr[G], Statement[G])])(implicit val o: Origin) extends CompositeStatement[G] with BranchImpl[G]
-final case class IndetBranch[G](branches: Seq[Statement[G]])(implicit val o: Origin) extends CompositeStatement[G] with IndetBranchImpl[G]
-final case class Switch[G](expr: Expr[G], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with SwitchImpl[G]
-@scopes[SendDecl] final case class Loop[G](init: Statement[G], cond: Expr[G], update: Statement[G], contract: LoopContract[G], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with LoopImpl[G]
-@scopes[Variable] final case class RangedFor[G](iter: IterVariable[G], contract: LoopContract[G], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with Declarator[G] with RangedForImpl[G]
-final case class TryCatchFinally[G](body: Statement[G], after: Statement[G], catches: Seq[CatchClause[G]])(implicit val o: Origin) extends CompositeStatement[G] with TryCatchFinallyImpl[G]
-final case class Synchronized[G](obj: Expr[G], body: Statement[G])(val blame: Blame[LockRegionFailure])(implicit val o: Origin) extends CompositeStatement[G] with SynchronizedImpl[G]
-@scopes[ParInvariantDecl] final case class ParInvariant[G](decl: ParInvariantDecl[G], inv: Expr[G], content: Statement[G])(val blame: Blame[ParInvariantNotEstablished])(implicit val o: Origin) extends CompositeStatement[G] with ParInvariantImpl[G]
-final case class ParAtomic[G](inv: Seq[Ref[G, ParInvariantDecl[G]]], content: Statement[G])(val blame: Blame[ParInvariantNotMaintained])(implicit val o: Origin) extends CompositeStatement[G] with ParAtomicImpl[G]
-final case class ParBarrier[G](block: Ref[G, ParBlockDecl[G]], invs: Seq[Ref[G, ParInvariantDecl[G]]], requires: Expr[G], ensures: Expr[G], content: Statement[G])(val blame: Blame[ParBarrierFailure])(implicit val o: Origin) extends CompositeStatement[G] with ParBarrierImpl[G]
-final case class ParStatement[G](impl: ParRegion[G])(implicit val o: Origin) extends CompositeStatement[G] with ParStatementImpl[G]
-@scopes[Variable] final case class VecBlock[G](iters: Seq[IterVariable[G]], requires: Expr[G], ensures: Expr[G], content: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with VecBlockImpl[G]
-final case class WandPackage[G](res: Expr[G], proof: Statement[G])(val blame: Blame[PackageFailure])(implicit val o: Origin) extends CompositeStatement[G] with WandPackageImpl[G]
-final case class ModelDo[G](model: Expr[G], perm: Expr[G], after: Expr[G], action: Expr[G], impl: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ModelDoImpl[G]
+final case class Block[G](statements: Seq[Statement[G]])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with BlockImpl[G]
+@scopes[Variable] @scopes[CLocalDeclaration] @scopes[CPPLocalDeclaration] @scopes[JavaLocalDeclaration] final case class Scope[G](locals: Seq[Variable[G]], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with ScopeImpl[G]
+final case class Branch[G](branches: Seq[(Expr[G], Statement[G])])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with BranchImpl[G]
+final case class IndetBranch[G](branches: Seq[Statement[G]])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with IndetBranchImpl[G]
+final case class Switch[G](expr: Expr[G], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with SwitchImpl[G]
+@scopes[SendDecl] final case class Loop[G](init: Statement[G], cond: Expr[G], update: Statement[G], contract: LoopContract[G], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with LoopImpl[G]
+@scopes[Variable] final case class RangedFor[G](iter: IterVariable[G], contract: LoopContract[G], body: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with Declarator[G] with ControlContainerStatement[G] with RangedForImpl[G]
+final case class TryCatchFinally[G](body: Statement[G], after: Statement[G], catches: Seq[CatchClause[G]])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with TryCatchFinallyImpl[G]
+final case class Synchronized[G](obj: Expr[G], body: Statement[G])(val blame: Blame[LockRegionFailure])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with SynchronizedImpl[G]
+@scopes[ParInvariantDecl] final case class ParInvariant[G](decl: ParInvariantDecl[G], inv: Expr[G], content: Statement[G])(val blame: Blame[ParInvariantNotEstablished])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with ParInvariantImpl[G]
+final case class ParAtomic[G](inv: Seq[Ref[G, ParInvariantDecl[G]]], content: Statement[G])(val blame: Blame[ParInvariantNotMaintained])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with ParAtomicImpl[G]
+final case class ParBarrier[G](block: Ref[G, ParBlockDecl[G]], invs: Seq[Ref[G, ParInvariantDecl[G]]], requires: Expr[G], ensures: Expr[G], content: Statement[G])(val blame: Blame[ParBarrierFailure])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with ParBarrierImpl[G]
+final case class ParStatement[G](impl: ParRegion[G])(implicit val o: Origin) extends CompositeStatement[G] with PurelySequentialStatement[G] with ParStatementImpl[G]
+@scopes[Variable] final case class VecBlock[G](iters: Seq[IterVariable[G]], requires: Expr[G], ensures: Expr[G], content: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with VecBlockImpl[G]
+final case class WandPackage[G](res: Expr[G], proof: Statement[G])(val blame: Blame[PackageFailure])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with WandPackageImpl[G]
+final case class ModelDo[G](model: Expr[G], perm: Expr[G], after: Expr[G], action: Expr[G], impl: Statement[G])(implicit val o: Origin) extends CompositeStatement[G] with ControlContainerStatement[G] with ModelDoImpl[G]
@family sealed trait GlobalDeclaration[G] extends Declaration[G] with GlobalDeclarationImpl[G]
final class HeapVariable[G](val t: Type[G])(implicit val o: Origin) extends GlobalDeclaration[G] with HeapVariableImpl[G]
@@ -978,7 +982,7 @@ sealed trait CAbstractDeclaration[G] extends GlobalDeclaration[G] with CAbstract
}
sealed trait CStatement[G] extends Statement[G] with CStatementImpl[G]
-final case class CDeclarationStatement[G](decl: CLocalDeclaration[G])(implicit val o: Origin) extends CStatement[G] with CDeclarationStatementImpl[G]
+final case class CDeclarationStatement[G](decl: CLocalDeclaration[G])(implicit val o: Origin) extends CStatement[G] with PurelySequentialStatement[G] with CDeclarationStatementImpl[G]
final case class CGoto[G](label: String)(implicit val o: Origin) extends CStatement[G] with CGotoImpl[G] {
var ref: Option[LabelDecl[G]] = None
}
@@ -1072,8 +1076,8 @@ sealed trait CPPAbstractDeclaration[G] extends GlobalDeclaration[G] with CPPAbst
}
sealed trait CPPStatement[G] extends Statement[G] with CPPStatementImpl[G]
-final case class CPPDeclarationStatement[G](decl: CPPLocalDeclaration[G])(implicit val o: Origin) extends CPPStatement[G] with CPPDeclarationStatementImpl[G]
-final case class CPPLifetimeScope[G](body: Statement[G])(implicit val o: Origin) extends CPPStatement[G] with CPPLifetimeScopeImpl[G]
+final case class CPPDeclarationStatement[G](decl: CPPLocalDeclaration[G])(implicit val o: Origin) extends CPPStatement[G] with PurelySequentialStatement[G] with CPPDeclarationStatementImpl[G]
+final case class CPPLifetimeScope[G](body: Statement[G])(implicit val o: Origin) extends CPPStatement[G] with ControlContainerStatement[G] with CPPLifetimeScopeImpl[G]
sealed trait CPPExpr[G] extends Expr[G] with CPPExprImpl[G]
final case class CPPLocal[G](name: String, genericArgs: Seq[CPPExprOrTypeSpecifier[G]])(val blame: Blame[DerefInsufficientPermission])(implicit val o: Origin) extends CPPExpr[G] with CPPLocalImpl[G] {
@@ -1170,7 +1174,7 @@ final class JavaFields[G](val modifiers: Seq[JavaModifier[G]], val t: Type[G], v
@family final class JavaLocalDeclaration[G](val modifiers: Seq[JavaModifier[G]], val t: Type[G], val decls: Seq[JavaVariableDeclaration[G]])(implicit val o: Origin) extends Declaration[G] with JavaLocalDeclarationImpl[G]
sealed trait JavaStatement[G] extends Statement[G] with JavaStatementImpl[G]
-final case class JavaLocalDeclarationStatement[G](decl: JavaLocalDeclaration[G])(implicit val o: Origin) extends JavaStatement[G] with JavaLocalDeclarationStatementImpl[G]
+final case class JavaLocalDeclarationStatement[G](decl: JavaLocalDeclaration[G])(implicit val o: Origin) extends JavaStatement[G] with PurelySequentialStatement[G] with JavaLocalDeclarationStatementImpl[G]
sealed trait JavaType[G] extends Type[G] with JavaTypeImpl[G]
final case class JavaNamedType[G](names: Seq[(String, Option[Seq[Type[G]]])])(implicit val o: Origin) extends JavaType[G] with JavaNamedTypeImpl[G] {
@@ -1334,8 +1338,8 @@ case class PVLFamilyRange[G](family: String, binder: String, start: Expr[G], end
@family case class PVLAccess[G](subject: PVLSubject[G], field: String)(val blame: Blame[PVLAccessFailure])(implicit val o: Origin) extends NodeFamily[G] with PVLAccessImpl[G] {
var ref: Option[RefField[G]] = None
}
-case class PVLCommunicate[G](sender: PVLAccess[G], receiver: PVLAccess[G])(val blame: Blame[PVLCommunicateFailure])(implicit val o: Origin) extends Statement[G] with PVLCommunicateImpl[G]
-final case class PVLSeqAssign[G](receiver: Ref[G, PVLEndpoint[G]], field: Ref[G, InstanceField[G]], value: Expr[G])(val blame: Blame[PVLSeqAssignFailure])(implicit val o: Origin) extends Statement[G] with PVLSeqAssignImpl[G]
+case class PVLCommunicate[G](sender: PVLAccess[G], receiver: PVLAccess[G])(val blame: Blame[PVLCommunicateFailure])(implicit val o: Origin) extends Statement[G] with PurelySequentialStatement[G] with PVLCommunicateImpl[G]
+final case class PVLSeqAssign[G](receiver: Ref[G, PVLEndpoint[G]], field: Ref[G, InstanceField[G]], value: Expr[G])(val blame: Blame[PVLSeqAssignFailure])(implicit val o: Origin) extends Statement[G] with ExpressionContainerStatement[G] with PVLSeqAssignImpl[G]
@family final class Endpoint[G](val cls: Ref[G, Class[G]], val constructor: Ref[G, Constructor[G]], val args: Seq[Expr[G]])(val blame: Blame[EndpointFailure])(implicit val o: Origin) extends Declaration[G] with EndpointImpl[G]
@scopes[Endpoint] final class SeqProg[G](val contract: ApplicableContract[G], val args : Seq[Variable[G]], val endpoints: Seq[Endpoint[G]], val run: SeqRun[G], val decls: Seq[ClassDeclaration[G]])(val blame: Blame[SeqCallableFailure])(implicit val o: Origin) extends GlobalDeclaration[G] with SeqProgImpl[G]
@@ -1343,23 +1347,23 @@ final case class PVLSeqAssign[G](receiver: Ref[G, PVLEndpoint[G]], field: Ref[G,
@family sealed trait Subject[G] extends NodeFamily[G] with SubjectImpl[G]
final case class EndpointName[G](ref: Ref[G, Endpoint[G]])(implicit val o: Origin) extends Subject[G] with EndpointNameImpl[G]
@family case class Access[G](subject: Subject[G], field: Ref[G, InstanceField[G]])(val blame: Blame[AccessFailure])(implicit val o: Origin) extends NodeFamily[G] with AccessImpl[G]
-final case class Communicate[G](receiver: Access[G], sender: Access[G])(val blame: Blame[CommunicateFailure])(implicit val o: Origin) extends Statement[G] with CommunicateImpl[G]
+final case class Communicate[G](receiver: Access[G], sender: Access[G])(val blame: Blame[CommunicateFailure])(implicit val o: Origin) extends Statement[G] with PurelySequentialStatement[G] with CommunicateImpl[G]
-final case class SeqAssign[G](receiver: Ref[G, Endpoint[G]], field: Ref[G, InstanceField[G]], value: Expr[G])(val blame: Blame[SeqAssignFailure])(implicit val o: Origin) extends Statement[G] with SeqAssignImpl[G]
+final case class SeqAssign[G](receiver: Ref[G, Endpoint[G]], field: Ref[G, InstanceField[G]], value: Expr[G])(val blame: Blame[SeqAssignFailure])(implicit val o: Origin) extends Statement[G] with ExpressionContainerStatement[G] with SeqAssignImpl[G]
final case class EndpointUse[G](ref: Ref[G, Endpoint[G]])(implicit val o: Origin) extends Expr[G] with EndpointUseImpl[G]
-final case class UnresolvedSeqBranch[G](branches: Seq[(Expr[G], Statement[G])])(val blame: Blame[SeqBranchFailure])(implicit val o: Origin) extends Statement[G] with UnresolvedSeqBranchImpl[G]
-final case class UnresolvedSeqLoop[G](cond: Expr[G], contract: LoopContract[G], body: Statement[G])(val blame: Blame[SeqLoopFailure])(implicit val o: Origin) extends Statement[G] with UnresolvedSeqLoopImpl[G]
+final case class UnresolvedSeqBranch[G](branches: Seq[(Expr[G], Statement[G])])(val blame: Blame[SeqBranchFailure])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with UnresolvedSeqBranchImpl[G]
+final case class UnresolvedSeqLoop[G](cond: Expr[G], contract: LoopContract[G], body: Statement[G])(val blame: Blame[SeqLoopFailure])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with UnresolvedSeqLoopImpl[G]
@family sealed trait SeqGuard[G] extends NodeFamily[G] with SeqGuardImpl[G]
final case class EndpointGuard[G](endpoint: Ref[G, Endpoint[G]], condition: Expr[G])(implicit val o: Origin) extends SeqGuard[G] with EndpointGuardImpl[G]
final case class UnpointedGuard[G](condition: Expr[G])(implicit val o: Origin) extends SeqGuard[G] with UnpointedGuardImpl[G]
-final case class SeqBranch[G](guards: Seq[SeqGuard[G]], yes: Statement[G], no: Option[Statement[G]])(val blame: Blame[SeqBranchFailure])(implicit val o: Origin) extends Statement[G] with SeqBranchImpl[G]
-final case class SeqLoop[G](guards: Seq[SeqGuard[G]], contract: LoopContract[G], body: Statement[G])(val blame: Blame[SeqLoopFailure])(implicit val o: Origin) extends Statement[G] with SeqLoopImpl[G]
+final case class SeqBranch[G](guards: Seq[SeqGuard[G]], yes: Statement[G], no: Option[Statement[G]])(val blame: Blame[SeqBranchFailure])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with SeqBranchImpl[G]
+final case class SeqLoop[G](guards: Seq[SeqGuard[G]], contract: LoopContract[G], body: Statement[G])(val blame: Blame[SeqLoopFailure])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with SeqLoopImpl[G]
-final case class VeyMontAssignExpression[G](endpoint: Ref[G, Endpoint[G]], assign: Statement[G])(implicit val o: Origin) extends Statement[G] with VeyMontAssignExpressionImpl[G]
-final case class CommunicateX[G](receiver: Ref[G, Endpoint[G]], sender: Ref[G, Endpoint[G]], chanType: Type[G], assign: Statement[G])(implicit val o: Origin) extends Statement[G] with CommunicateXImpl[G]
+final case class VeyMontAssignExpression[G](endpoint: Ref[G, Endpoint[G]], assign: Statement[G])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with VeyMontAssignExpressionImpl[G]
+final case class CommunicateX[G](receiver: Ref[G, Endpoint[G]], sender: Ref[G, Endpoint[G]], chanType: Type[G], assign: Statement[G])(implicit val o: Origin) extends Statement[G] with ControlContainerStatement[G] with CommunicateXImpl[G]
sealed trait SilverExpr[G] extends Expr[G] with SilverExprImpl[G]
final case class SilverDeref[G](obj: Expr[G], field: Ref[G, SilverField[G]])(val blame: Blame[InsufficientPermission])(implicit val o: Origin) extends SilverExpr[G] with HeapDeref[G] with SilverDerefImpl[G]
@@ -1379,11 +1383,11 @@ final case class SilverPartialADTFunctionInvocation[G](name: String, args: Seq[E
final case class SilverUntypedNonemptyLiteralMap[G](values: Seq[(Expr[G], Expr[G])])(implicit val o: Origin) extends SilverExpr[G] with SilverUntypedNonemptyLiteralMapImpl[G]
sealed trait SilverStatement[G] extends Statement[G] with SilverStatementImpl[G]
-final case class SilverNewRef[G](v: Ref[G, Variable[G]], fields: Seq[Ref[G, SilverField[G]]])(implicit val o: Origin) extends SilverStatement[G] with SilverNewRefImpl[G]
+final case class SilverNewRef[G](v: Ref[G, Variable[G]], fields: Seq[Ref[G, SilverField[G]]])(implicit val o: Origin) extends SilverStatement[G] with PurelySequentialStatement[G] with SilverNewRefImpl[G]
sealed trait SilverAssign[G] extends SilverStatement[G] with SilverAssignImpl[G]
-final case class SilverFieldAssign[G](obj: Expr[G], field: Ref[G, SilverField[G]], value: Expr[G])(val blame: Blame[AssignFailed])(implicit val o: Origin) extends SilverAssign[G] with SilverFieldAssignImpl[G]
-final case class SilverLocalAssign[G](v: Ref[G, Variable[G]], value: Expr[G])(implicit val o: Origin) extends SilverAssign[G] with SilverLocalAssignImpl[G]
+final case class SilverFieldAssign[G](obj: Expr[G], field: Ref[G, SilverField[G]], value: Expr[G])(val blame: Blame[AssignFailed])(implicit val o: Origin) extends SilverAssign[G] with ExpressionContainerStatement[G] with SilverFieldAssignImpl[G]
+final case class SilverLocalAssign[G](v: Ref[G, Variable[G]], value: Expr[G])(implicit val o: Origin) extends SilverAssign[G] with ExpressionContainerStatement[G] with SilverLocalAssignImpl[G]
sealed trait SilverDeclaration[G] extends GlobalDeclaration[G] with SilverDeclarationImpl[G]
final class SilverField[G](val t: Type[G])(implicit val o: Origin) extends SilverDeclaration[G] with SilverFieldImpl[G]
diff --git a/src/col/vct/col/ast/lang/silver/SilverFieldAssignImpl.scala b/src/col/vct/col/ast/lang/silver/SilverFieldAssignImpl.scala
index d99657c937..5483a0f410 100644
--- a/src/col/vct/col/ast/lang/silver/SilverFieldAssignImpl.scala
+++ b/src/col/vct/col/ast/lang/silver/SilverFieldAssignImpl.scala
@@ -1,10 +1,12 @@
package vct.col.ast.lang.silver
-import vct.col.ast.SilverFieldAssign
+import vct.col.ast.{Expr, SilverFieldAssign}
import vct.col.print.{Ctx, Doc, Precedence, Text}
import vct.col.ast.ops.SilverFieldAssignOps
trait SilverFieldAssignImpl[G] extends SilverFieldAssignOps[G] { this: SilverFieldAssign[G] =>
override def layout(implicit ctx: Ctx): Doc =
obj.bind(Precedence.POSTFIX) <> "." <> ctx.name(field) <+> ":=" <+> value
+
+ override def expr: Expr[G] = this.value
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/lang/silver/SilverLocalAssignImpl.scala b/src/col/vct/col/ast/lang/silver/SilverLocalAssignImpl.scala
index 95f26cb5fd..432549a54a 100644
--- a/src/col/vct/col/ast/lang/silver/SilverLocalAssignImpl.scala
+++ b/src/col/vct/col/ast/lang/silver/SilverLocalAssignImpl.scala
@@ -1,10 +1,12 @@
package vct.col.ast.lang.silver
-import vct.col.ast.SilverLocalAssign
+import vct.col.ast.{Expr, SilverLocalAssign}
import vct.col.print.{Ctx, Doc, Text}
import vct.col.ast.ops.SilverLocalAssignOps
trait SilverLocalAssignImpl[G] extends SilverLocalAssignOps[G] { this: SilverLocalAssign[G] =>
override def layout(implicit ctx: Ctx): Doc =
Text(ctx.name(v)) <+> ":=" <+> value
+
+ override def expr: Expr[G] = this.value
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/behavior/ExpressionContainerStatementImpl.scala b/src/col/vct/col/ast/statement/behavior/ExpressionContainerStatementImpl.scala
new file mode 100644
index 0000000000..b9e503bccb
--- /dev/null
+++ b/src/col/vct/col/ast/statement/behavior/ExpressionContainerStatementImpl.scala
@@ -0,0 +1,8 @@
+package vct.col.ast.statement.behavior
+
+import vct.col.ast.{Expr, ExpressionContainerStatement}
+import vct.col.ast.statement.StatementImpl
+
+trait ExpressionContainerStatementImpl[G] extends StatementImpl[G] { this: ExpressionContainerStatement[G] =>
+ def expr: Expr[G]
+}
diff --git a/src/col/vct/col/ast/statement/terminal/AssertImpl.scala b/src/col/vct/col/ast/statement/terminal/AssertImpl.scala
index fa2d2991ca..c34211c664 100644
--- a/src/col/vct/col/ast/statement/terminal/AssertImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/AssertImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Assert
+import vct.col.ast.{Assert, Expr}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.AssertOps
@@ -15,4 +15,6 @@ trait AssertImpl[G] extends AssertOps[G] { this: Assert[G] =>
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/AssumeImpl.scala b/src/col/vct/col/ast/statement/terminal/AssumeImpl.scala
index 998a03b3b1..0c6ebb0227 100644
--- a/src/col/vct/col/ast/statement/terminal/AssumeImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/AssumeImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Assume
+import vct.col.ast.{Assume, Expr}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.AssumeOps
@@ -15,4 +15,6 @@ trait AssumeImpl[G] extends AssumeOps[G] { this: Assume[G] =>
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.assn
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/CommitImpl.scala b/src/col/vct/col/ast/statement/terminal/CommitImpl.scala
index 3d5b56a99e..58260e1576 100644
--- a/src/col/vct/col/ast/statement/terminal/CommitImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/CommitImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Commit
+import vct.col.ast.{Commit, Expr}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.CommitOps
@@ -9,4 +9,6 @@ trait CommitImpl[G] extends CommitOps[G] { this: Commit[G] =>
Text("commit") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/ExhaleImpl.scala b/src/col/vct/col/ast/statement/terminal/ExhaleImpl.scala
index cc4b9f02ce..a4f547867f 100644
--- a/src/col/vct/col/ast/statement/terminal/ExhaleImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/ExhaleImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Exhale
+import vct.col.ast.{Exhale, Expr}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.ExhaleOps
@@ -15,4 +15,6 @@ trait ExhaleImpl[G] extends ExhaleOps[G] { this: Exhale[G] =>
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/FoldImpl.scala b/src/col/vct/col/ast/statement/terminal/FoldImpl.scala
index ca666aae69..ace25ef36f 100644
--- a/src/col/vct/col/ast/statement/terminal/FoldImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/FoldImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Fold
+import vct.col.ast.{Expr, Fold}
import vct.col.ast.node.NodeFamilyImpl
import vct.col.ast.util.CheckFoldUnfoldTarget
import vct.col.print.{Ctx, Doc, Show, Text}
@@ -17,4 +17,6 @@ trait FoldImpl[G] extends NodeFamilyImpl[G] with CheckFoldUnfoldTarget[G] with F
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/ForkImpl.scala b/src/col/vct/col/ast/statement/terminal/ForkImpl.scala
index e5852ce79b..6ae45b80c8 100644
--- a/src/col/vct/col/ast/statement/terminal/ForkImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/ForkImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Fork
+import vct.col.ast.{Expr, Fork}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.ForkOps
@@ -9,4 +9,6 @@ trait ForkImpl[G] extends ForkOps[G] { this: Fork[G] =>
Text("fork") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/HavocImpl.scala b/src/col/vct/col/ast/statement/terminal/HavocImpl.scala
index b3a546b71c..eb346ef388 100644
--- a/src/col/vct/col/ast/statement/terminal/HavocImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/HavocImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Havoc
+import vct.col.ast.{Expr, Havoc}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.HavocOps
@@ -9,4 +9,6 @@ trait HavocImpl[G] extends HavocOps[G] { this: Havoc[G] =>
Text("havoc") <+> loc <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.loc
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/InhaleImpl.scala b/src/col/vct/col/ast/statement/terminal/InhaleImpl.scala
index 47693b025e..0a4dfcf00a 100644
--- a/src/col/vct/col/ast/statement/terminal/InhaleImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/InhaleImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Inhale
+import vct.col.ast.{Expr, Inhale}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.InhaleOps
@@ -15,4 +15,6 @@ trait InhaleImpl[G] extends InhaleOps[G] { this: Inhale[G] =>
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/JoinImpl.scala b/src/col/vct/col/ast/statement/terminal/JoinImpl.scala
index 1904f6702d..3d7f3f9bde 100644
--- a/src/col/vct/col/ast/statement/terminal/JoinImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/JoinImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Join
+import vct.col.ast.{Expr, Join}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.JoinOps
@@ -9,4 +9,6 @@ trait JoinImpl[G] extends JoinOps[G] { this: Join[G] =>
Text("join") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/LockImpl.scala b/src/col/vct/col/ast/statement/terminal/LockImpl.scala
index 6962fcae94..6a864ee3f7 100644
--- a/src/col/vct/col/ast/statement/terminal/LockImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/LockImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Lock
+import vct.col.ast.{Expr, Lock}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.LockOps
@@ -9,4 +9,6 @@ trait LockImpl[G] extends LockOps[G] { this: Lock[G] =>
Text("lock") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/NotifyImpl.scala b/src/col/vct/col/ast/statement/terminal/NotifyImpl.scala
index 8803df8c7e..28a79fd47a 100644
--- a/src/col/vct/col/ast/statement/terminal/NotifyImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/NotifyImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Notify
+import vct.col.ast.{Expr, Notify}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.NotifyOps
@@ -9,4 +9,6 @@ trait NotifyImpl[G] extends NotifyOps[G] { this: Notify[G] =>
Text("notify") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/RefuteImpl.scala b/src/col/vct/col/ast/statement/terminal/RefuteImpl.scala
index b88f33c6a6..957e4ed4e4 100644
--- a/src/col/vct/col/ast/statement/terminal/RefuteImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/RefuteImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Refute
+import vct.col.ast.{Expr, Refute}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.RefuteOps
@@ -15,4 +15,6 @@ trait RefuteImpl[G] extends RefuteOps[G] { this: Refute[G] =>
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.assn
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/SendImpl.scala b/src/col/vct/col/ast/statement/terminal/SendImpl.scala
index e596a220f3..fd007d8340 100644
--- a/src/col/vct/col/ast/statement/terminal/SendImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/SendImpl.scala
@@ -1,7 +1,7 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Send
-import vct.col.print.{Ctx, Doc, Show, Text, Group}
+import vct.col.ast.{Expr, Send}
+import vct.col.print.{Ctx, Doc, Group, Show, Text}
import vct.col.ast.ops.SendOps
trait SendImpl[G] extends SendOps[G] { this: Send[G] =>
@@ -10,4 +10,6 @@ trait SendImpl[G] extends SendOps[G] { this: Send[G] =>
override def layout(implicit ctx: Ctx): Doc =
Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/UnfoldImpl.scala b/src/col/vct/col/ast/statement/terminal/UnfoldImpl.scala
index f2b3e9602d..759e81c9c5 100644
--- a/src/col/vct/col/ast/statement/terminal/UnfoldImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/UnfoldImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Unfold
+import vct.col.ast.{Expr, Unfold}
import vct.col.ast.node.NodeFamilyImpl
import vct.col.ast.util.CheckFoldUnfoldTarget
import vct.col.print.{Ctx, Doc, Show, Text}
@@ -17,4 +17,6 @@ trait UnfoldImpl[G] extends NodeFamilyImpl[G] with CheckFoldUnfoldTarget[G] with
case Ctx.Silver => layoutSilver
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/UnlockImpl.scala b/src/col/vct/col/ast/statement/terminal/UnlockImpl.scala
index 0f80ea4c44..3f4f2acc23 100644
--- a/src/col/vct/col/ast/statement/terminal/UnlockImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/UnlockImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Unlock
+import vct.col.ast.{Expr, Unlock}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.UnlockOps
@@ -9,4 +9,6 @@ trait UnlockImpl[G] extends UnlockOps[G] { this: Unlock[G] =>
Text("unlock") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/WaitImpl.scala b/src/col/vct/col/ast/statement/terminal/WaitImpl.scala
index 340b36d14c..317a91b89c 100644
--- a/src/col/vct/col/ast/statement/terminal/WaitImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/WaitImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.Wait
+import vct.col.ast.{Expr, Wait}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.WaitOps
@@ -9,4 +9,6 @@ trait WaitImpl[G] extends WaitOps[G] { this: Wait[G] =>
Text("wait") <+> obj <> ";"
override def layout(implicit ctx: Ctx): Doc = Doc.inlineSpec(Show.lazily(layoutSpec(_)))
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/terminal/WandApplyImpl.scala b/src/col/vct/col/ast/statement/terminal/WandApplyImpl.scala
index 0e1e225e60..332ca1d490 100644
--- a/src/col/vct/col/ast/statement/terminal/WandApplyImpl.scala
+++ b/src/col/vct/col/ast/statement/terminal/WandApplyImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.terminal
-import vct.col.ast.WandApply
+import vct.col.ast.{Expr, WandApply}
import vct.col.print.{Ctx, Doc, Show, Text}
import vct.col.ast.ops.WandApplyOps
@@ -12,4 +12,6 @@ trait WandApplyImpl[G] extends WandApplyOps[G] { this: WandApply[G] =>
case Ctx.Silver => Text("apply") <+> res
case _ => Doc.inlineSpec(Show.lazily(layoutSpec(_)))
}
+
+ override def expr: Expr[G] = this.res
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/veymont/PVLSeqAssignImpl.scala b/src/col/vct/col/ast/statement/veymont/PVLSeqAssignImpl.scala
index 13ceba06dc..9dcbfaa67a 100644
--- a/src/col/vct/col/ast/statement/veymont/PVLSeqAssignImpl.scala
+++ b/src/col/vct/col/ast/statement/veymont/PVLSeqAssignImpl.scala
@@ -1,10 +1,12 @@
package vct.col.ast.statement.veymont
-import vct.col.ast.PVLSeqAssign
+import vct.col.ast.{Expr, PVLSeqAssign}
import vct.col.print.{Ctx, Doc, Group, Text}
import vct.col.ast.ops.PVLSeqAssignOps
trait PVLSeqAssignImpl[G] extends PVLSeqAssignOps[G] { this: PVLSeqAssign[G] =>
override def layout(implicit ctx: Ctx): Doc =
Group(Text(receiver.decl.name) <> "." <> ctx.name(field) <+> ":=" <+> value.show)
+
+ override def expr: Expr[G] = this.value
}
diff --git a/src/col/vct/col/ast/statement/veymont/SeqAssignImpl.scala b/src/col/vct/col/ast/statement/veymont/SeqAssignImpl.scala
index 4ea2a6f88d..6f19873baf 100644
--- a/src/col/vct/col/ast/statement/veymont/SeqAssignImpl.scala
+++ b/src/col/vct/col/ast/statement/veymont/SeqAssignImpl.scala
@@ -1,6 +1,6 @@
package vct.col.ast.statement.veymont
-import vct.col.ast.{Endpoint, SeqAssign}
+import vct.col.ast.{Endpoint, Expr, SeqAssign}
import vct.col.ast.statement.StatementImpl
import vct.col.check.{CheckContext, CheckError, SeqProgParticipant}
import vct.col.print.{Ctx, Doc, Group, Text}
@@ -18,4 +18,6 @@ trait SeqAssignImpl[G] extends StatementImpl[G] with SeqAssignOps[G] { this: Seq
if (!context.currentParticipatingEndpoints.get.contains(this.receiver.decl))
Seq(SeqProgParticipant(this))
else Nil
+
+ override def expr: Expr[G] = this.value
}
diff --git a/src/col/vct/col/ast/unsorted/InstantiateImpl.scala b/src/col/vct/col/ast/unsorted/InstantiateImpl.scala
index 824bcb5862..85fadd5742 100644
--- a/src/col/vct/col/ast/unsorted/InstantiateImpl.scala
+++ b/src/col/vct/col/ast/unsorted/InstantiateImpl.scala
@@ -1,9 +1,10 @@
package vct.col.ast.unsorted
-import vct.col.ast.Instantiate
+import vct.col.ast.{Expr, Instantiate}
import vct.col.ast.ops.InstantiateOps
import vct.col.print._
trait InstantiateImpl[G] extends InstantiateOps[G] { this: Instantiate[G] =>
// override def layout(implicit ctx: Ctx): Doc = ???
+ override def expr: Expr[G] = this.out
}
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 6688578347..214ce28d26 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -38,53 +38,21 @@ case class CFGGenerator[G]() {
}
private def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = node match {
- case PVLBranch(_) => evaluate_first(context.enter_scope(node))
- case PVLLoop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
- // NonExecutableStatement
- case LocalDecl(_) => sequential_successor(context)
- case SpecIgnoreStart() => sequential_successor(context)
- case SpecIgnoreEnd() => sequential_successor(context)
- // NormallyCompletingStatement
+ // Exceptional statements
case Assign(_, _) => context.indices.head match {
case AssignmentIndex(a, _) if a == node => sequential_successor(context)
case _ => evaluate_first(context.enter_scope(node))
}
- case Send(_, _, res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Recv(_) => sequential_successor(context)
- case DefaultCase() => sequential_successor(context)
- case Case(_) => sequential_successor(context)
- case Label(_, _) => evaluate_first(context.enter_scope(node))
case Goto(lbl) => found_labels.get(lbl.decl) match {
case Some(node) => mutable.Set(CFGEdge(node, None))
case None => mutable.Set()
}
- case Exhale(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Assert(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Refute(assn) => handle_expression_container(node, Eval(assn)(assn.o), context, sequential_successor(context))
- case Inhale(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Assume(assn) => handle_expression_container(node, Eval(assn)(assn.o), context, sequential_successor(context))
- case Instantiate(_, out) => handle_expression_container(node, Eval(out)(out.o), context, sequential_successor(context))
- case Wait(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
- case Notify(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
- case Fork(obj) => {
+ case Fork(obj) => // TODO: Evaluate the obj expression before executing fork
val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head
// Get the successor(s) of the fork statement as well as the new thread, starting with the run method
sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, GlobalIndex[G](mutable.Seq()).enter_scope(run_method)), None))
- }
- case Join(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
- case Lock(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
- case Unlock(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
- case Commit(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, sequential_successor(context))
- case Fold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Unfold(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case WandApply(res) => handle_expression_container(node, Eval(res)(res.o), context, sequential_successor(context))
- case Havoc(loc) => handle_expression_container(node, Eval(loc)(loc.o), context, sequential_successor(context))
- case FramedProof(_, _, _) => evaluate_first(context.enter_scope(node))
- case Extract(_) => evaluate_first(context.enter_scope(node))
- // ExceptionalStatement
- case Eval(_) => evaluate_first(context.enter_scope(node))
case Return(result) => handle_expression_container(node, Eval(result)(result.o), context, mutable.Set(CFGEdge(return_successor(context), None)))
- case Throw(obj) => mutable.Set(CFGEdge(exception_successor(obj, context), None))
+ case Throw(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, mutable.Set(CFGEdge(exception_successor(obj, context), None)))
case Break(label) => label match {
case Some(ref) => ??? // TODO: Handle break label!
case None => mutable.Set(CFGEdge(break_successor(context), None))
@@ -93,54 +61,19 @@ case class CFGGenerator[G]() {
case Some(ref) => ??? // TODO: Handle continue label!
case None => mutable.Set(CFGEdge(continue_successor(context), None))
}
- // InvocationStatement
- case InvokeProcedure(_, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
- case InvokeConstructor(_, _, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
- case InvokeMethod(_, _, _, _, _, _, _) => evaluate_first(context.enter_scope(node))
- // CompositeStatement
- case Block(_) => evaluate_first(context.enter_scope(node))
- case Scope(_, _) => evaluate_first(context.enter_scope(node))
- case Branch(_) => evaluate_first(context.enter_scope(node))
case IndetBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
case s: Switch[_] => handle_switch_statement(s, context.enter_scope(node))
- case Loop(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
- case RangedFor(_, _, _) => evaluate_first(context.enter_scope(node))
- case TryCatchFinally(_, _, _) => evaluate_first(context.enter_scope(node))
- case Synchronized(_, _) => evaluate_first(context.enter_scope(node))
- case ParInvariant(_, _, _) => evaluate_first(context.enter_scope(node))
- case ParAtomic(_, _) => evaluate_first(context.enter_scope(node))
- case ParBarrier(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
- case ParStatement(_) => sequential_successor(context)
- case VecBlock(_, _, _, _) => evaluate_first(context.enter_scope(node))
- case WandPackage(_, _) => evaluate_first(context.enter_scope(node))
- case ModelDo(_, _, _, _, _) => evaluate_first(context.enter_scope(node))
- // CStatement
- case CDeclarationStatement(_) => sequential_successor(context)
- case CGoto(label) => ??? // I'm not dealing with string labels
- // CPPStatement
- case CPPDeclarationStatement(_) => sequential_successor(context)
- case CPPLifetimeScope(_) => evaluate_first(context.enter_scope(node))
- case JavaLocalDeclarationStatement(_) => sequential_successor(context)
- // SilverStatement
- case SilverNewRef(_, _) => sequential_successor(context)
- case SilverFieldAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
- case SilverLocalAssign(_, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
- // OTHER
- case PVLCommunicate(_, _) => sequential_successor(context)
- case PVLSeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
- case Communicate(_, _) => sequential_successor(context)
- case SeqAssign(_, _, value) => handle_expression_container(node, Eval(value)(value.o), context, sequential_successor(context))
- case UnresolvedSeqBranch(branches) =>
- mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1._2, context.enter_scope(node, b._2)), None)))
- case UnresolvedSeqLoop(_, _, _) => evaluate_first(context.enter_scope(node))
+ case CGoto(label) => ???
case SeqBranch(_, yes, no) => no match { // TODO: What are the conditions here?
case Some(stmt) => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node, 0)), None), CFGEdge(convert(stmt, context.enter_scope(node, 1)), None))
case None => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None))
}
case SeqLoop(_, _, _) => evaluate_first(context.enter_scope(node)) // TODO: What are the conditions here?
- case VeyMontAssignExpression(_, _) => evaluate_first(context.enter_scope(node))
- case CommunicateX(_, _, _, _) => evaluate_first(context.enter_scope(node))
+ // Statements that can be categorized into a broader role for control flow analysis
+ case ecs: ExpressionContainerStatement[_] => handle_expression_container(node, Eval(ecs.expr)(ecs.o), context, sequential_successor(context))
+ case _: ControlContainerStatement[_] => evaluate_first(context.enter_scope(node))
+ case _: PurelySequentialStatement[_] => sequential_successor(context)
}
private def handle_expression_container(statement: Statement[G],
@@ -178,17 +111,17 @@ case class CFGGenerator[G]() {
}
conds = conds.reverse
- var previous_cond: Option[Expr[G]] = Some(Utils.negate(conds.head._2.expr))
var successor_to_previous: mutable.Set[CFGEdge[G]] = default match {
- case Some(t) => mutable.Set(CFGEdge(convert(t._1, t._2), previous_cond))
- case None => sequential_successor(context, previous_cond)
+ case Some(t) => mutable.Set(CFGEdge(convert(t._1, t._2), Some(Utils.negate(conds.head._2.expr))))
+ case None => sequential_successor(context, Some(Utils.negate(conds.head._2.expr)))
}
- for (c <- conds) {
+ for ((c, i) <- conds.zipWithIndex) {
successor_to_previous.addOne(CFGEdge(convert(c._1, c._3), Some(c._2.expr)))
val node = CFGNode(c._2, successor_to_previous)
// TODO: Enter node at appropriate index in converted_nodes!
- successor_to_previous = mutable.Set(CFGEdge(node, None)) // TODO: Add edge condition here!
+ if (i == conds.size - 1) successor_to_previous = mutable.Set(CFGEdge(node, None))
+ else successor_to_previous = mutable.Set(CFGEdge(node, Some(Utils.negate(conds(i + 1)._2.expr))))
}
successor_to_previous
}
diff --git a/src/rewrite/vct/rewrite/rasi/AbstractState.scala b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
index a35ac1e76a..78558f31f2 100644
--- a/src/rewrite/vct/rewrite/rasi/AbstractState.scala
+++ b/src/rewrite/vct/rewrite/rasi/AbstractState.scala
@@ -119,7 +119,7 @@ case class AbstractState[G](valuations: Map[ConcreteVariable[G], UncertainValue]
def resolve_integer_expression(expr: Expr[G]): UncertainIntegerValue = expr match {
case CIntegerValue(value) => UncertainIntegerValue.single(value.intValue)
case IntegerValue(value) => UncertainIntegerValue.single(value.intValue)
- case SizeOf(tname) => UncertainIntegerValue.above(0) // TODO: Can we use more information about sizeof?
+ case SizeOf(tname) => UncertainIntegerValue.above(1) // TODO: Can we use more information about sizeof?
case UMinus(arg) => -resolve_integer_expression(arg)
case AmbiguousMult(left, right) => resolve_integer_expression(left) * resolve_integer_expression(right)
case AmbiguousPlus(left, right) => resolve_integer_expression(left) + resolve_integer_expression(right)
From fd3739b4ff1a54e58ab06dca12f0ff16c40e99ca Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 26 Feb 2024 15:35:12 +0100
Subject: [PATCH 50/85] Removed expression container node before resolution of
expression
---
src/col/vct/col/ast/Node.scala | 4 +-
.../statement/exceptional/ReturnImpl.scala | 2 +
.../ast/statement/exceptional/ThrowImpl.scala | 4 +-
.../vct/rewrite/cfg/CFGGenerator.scala | 65 ++++++++++---------
src/rewrite/vct/rewrite/cfg/Index.scala | 13 ++--
src/rewrite/vct/rewrite/cfg/Utils.scala | 3 +-
6 files changed, 52 insertions(+), 39 deletions(-)
diff --git a/src/col/vct/col/ast/Node.scala b/src/col/vct/col/ast/Node.scala
index 9301b54e4a..3829a4949c 100644
--- a/src/col/vct/col/ast/Node.scala
+++ b/src/col/vct/col/ast/Node.scala
@@ -226,8 +226,8 @@ sealed trait InvocationStatement[G] extends ExceptionalStatement[G] with Invokin
final case class InvokeProcedure[G](ref: Ref[G, Procedure[G]], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with ControlContainerStatement[G] with InvokeProcedureImpl[G]
final case class InvokeConstructor[G](ref: Ref[G, Constructor[G]], out: Expr[G], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with ControlContainerStatement[G] with InvokeConstructorImpl[G]
final case class InvokeMethod[G](obj: Expr[G], ref: Ref[G, InstanceMethod[G]], args: Seq[Expr[G]], outArgs: Seq[Expr[G]], typeArgs: Seq[Type[G]], givenMap: Seq[(Ref[G, Variable[G]], Expr[G])], yields: Seq[(Expr[G], Ref[G, Variable[G]])])(val blame: Blame[InstanceInvocationFailure])(implicit val o: Origin) extends InvocationStatement[G] with InstanceApply[G] with ControlContainerStatement[G] with InvokeMethodImpl[G]
-final case class Return[G](result: Expr[G])(implicit val o: Origin) extends ExceptionalStatement[G] with ReturnImpl[G]
-final case class Throw[G](obj: Expr[G])(val blame: Blame[ThrowNull])(implicit val o: Origin) extends ExceptionalStatement[G] with ThrowImpl[G]
+final case class Return[G](result: Expr[G])(implicit val o: Origin) extends ExceptionalStatement[G] with ExpressionContainerStatement[G] with ReturnImpl[G]
+final case class Throw[G](obj: Expr[G])(val blame: Blame[ThrowNull])(implicit val o: Origin) extends ExceptionalStatement[G] with ExpressionContainerStatement[G] with ThrowImpl[G]
final case class Break[G](label: Option[Ref[G, LabelDecl[G]]])(implicit val o: Origin) extends ExceptionalStatement[G] with BreakImpl[G]
final case class Continue[G](label: Option[Ref[G, LabelDecl[G]]])(implicit val o: Origin) extends ExceptionalStatement[G] with ContinueImpl[G]
diff --git a/src/col/vct/col/ast/statement/exceptional/ReturnImpl.scala b/src/col/vct/col/ast/statement/exceptional/ReturnImpl.scala
index 2be3cf2267..0dbccb5968 100644
--- a/src/col/vct/col/ast/statement/exceptional/ReturnImpl.scala
+++ b/src/col/vct/col/ast/statement/exceptional/ReturnImpl.scala
@@ -18,4 +18,6 @@ trait ReturnImpl[G] extends ExceptionalStatementImpl[G] with ReturnOps[G] { this
override def layout(implicit ctx: Ctx): Doc =
Text("return") <> (if(result == Void[G]()) Text(";") else Empty <+> result <> ";")
+
+ override def expr: Expr[G] = this.result
}
\ No newline at end of file
diff --git a/src/col/vct/col/ast/statement/exceptional/ThrowImpl.scala b/src/col/vct/col/ast/statement/exceptional/ThrowImpl.scala
index c6925487c9..c49866e6e9 100644
--- a/src/col/vct/col/ast/statement/exceptional/ThrowImpl.scala
+++ b/src/col/vct/col/ast/statement/exceptional/ThrowImpl.scala
@@ -1,10 +1,12 @@
package vct.col.ast.statement.exceptional
-import vct.col.ast.Throw
+import vct.col.ast.{Expr, Throw}
import vct.col.print.{Ctx, Doc, Text}
import vct.col.ast.ops.ThrowOps
trait ThrowImpl[G] extends ThrowOps[G] { this: Throw[G] =>
override def layout(implicit ctx: Ctx): Doc =
Text("throw") <+> obj <> ";"
+
+ override def expr: Expr[G] = this.obj
}
\ No newline at end of file
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 214ce28d26..31e4637336 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -16,43 +16,61 @@ case class CFGGenerator[G]() {
private def convert(node: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
// If a node has already been visited, then it should not be created again
if (converted_nodes.contains(context)) return converted_nodes(context)
- // Create new node with its successors (if possible)
- val cfg_node: CFGNode[G] = CFGNode(node, mutable.Set())
- converted_nodes.addOne((context, cfg_node))
- cfg_node.successors.addAll(find_successors(node, context))
- // Handle labels and goto statements
+ // Convert to CFG node depending on type of statement
node match {
+ case assign: Assign[_] => assign_to_cfg(assign, context)
+ case stmt: ExpressionContainerStatement[_] => expression_container_to_cfg(stmt, context)
case label: Label[_] =>
+ val cfg_node: CFGNode[G] = statement_to_cfg(node, context)
// For labels, add them to the label map and add them to any goto statements going to that label
found_labels.addOne((label.decl, cfg_node))
- searched_labels.getOrElse(label.decl, mutable.Set()).map(g => g.successors.addOne(CFGEdge(cfg_node, None)))
+ searched_labels.getOrElse(label.decl, mutable.Set()).foreach(g => g.successors.addOne(CFGEdge(cfg_node, None)))
+ cfg_node
case goto: Goto[_] =>
+ val cfg_node: CFGNode[G] = statement_to_cfg(node, context)
// For goto statements, if the label could not be resolved, add the statement to the waiting list for the right label
if (cfg_node.successors.isEmpty) {
if (searched_labels.contains(goto.lbl.decl)) searched_labels(goto.lbl.decl).addOne(cfg_node)
else searched_labels.addOne((goto.lbl.decl, mutable.Set(cfg_node)))
}
- case _ =>
+ cfg_node
+ case _ => statement_to_cfg(node, context)
}
+ }
+
+ private def statement_to_cfg(stmt: Statement[G], context: GlobalIndex[G]): CFGNode[G] = {
+ val cfg_node: CFGNode[G] = CFGNode(stmt, mutable.Set())
+ converted_nodes.addOne((context, cfg_node))
+ cfg_node.successors.addAll(find_successors(stmt, context))
cfg_node
}
+ private def assign_to_cfg(assign: Assign[G], context: GlobalIndex[G]): CFGNode[G] = context.indices.head match {
+ case AssignmentIndex(a, _) if a == assign => statement_to_cfg(assign, context)
+ case _ => convert(Eval(assign.target)(assign.target.o), context.enter_scope(assign))
+ }
+
+ private def expression_container_to_cfg(stmt: ExpressionContainerStatement[G], context: GlobalIndex[G]): CFGNode[G] = context.indices.head match {
+ case ExpressionContainerIndex(s, _) if s == stmt => statement_to_cfg(stmt, context)
+ case _ => convert(Eval(stmt.expr)(stmt.expr.o), context.enter_scope(stmt))
+ }
+
private def find_successors(node: Statement[G], context: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = node match {
// Exceptional statements
- case Assign(_, _) => context.indices.head match {
- case AssignmentIndex(a, _) if a == node => sequential_successor(context)
- case _ => evaluate_first(context.enter_scope(node))
- }
+ // Statements that can jump to another part of the program
case Goto(lbl) => found_labels.get(lbl.decl) match {
case Some(node) => mutable.Set(CFGEdge(node, None))
case None => mutable.Set()
}
- case Fork(obj) => // TODO: Evaluate the obj expression before executing fork
+ case CGoto(label) => ???
+ // Statements that simultaneously affect other threads
+ case Fork(obj) =>
val run_method: RunMethod[G] = obj.t.asClass.get.cls.decl.declarations.collect{ case r: RunMethod[G] => r }.head
// Get the successor(s) of the fork statement as well as the new thread, starting with the run method
sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, GlobalIndex[G](mutable.Seq()).enter_scope(run_method)), None))
- case Return(result) => handle_expression_container(node, Eval(result)(result.o), context, mutable.Set(CFGEdge(return_successor(context), None)))
- case Throw(obj) => handle_expression_container(node, Eval(obj)(obj.o), context, mutable.Set(CFGEdge(exception_successor(obj, context), None)))
+ // Statements that jump out of the current control flow context
+ case Return(_) => mutable.Set(CFGEdge(return_successor(context), None))
+ case Throw(obj) => mutable.Set(CFGEdge(exception_successor(obj, context), None))
case Break(label) => label match {
case Some(ref) => ??? // TODO: Handle break label!
case None => mutable.Set(CFGEdge(break_successor(context), None))
@@ -61,31 +79,20 @@ case class CFGGenerator[G]() {
case Some(ref) => ??? // TODO: Handle continue label!
case None => mutable.Set(CFGEdge(continue_successor(context), None))
}
+ // Irregular control container statements
case IndetBranch(branches) =>
mutable.LinkedHashSet.from(branches.zipWithIndex.map(b => CFGEdge(convert(b._1, context.enter_scope(node, b._2)), None)))
case s: Switch[_] => handle_switch_statement(s, context.enter_scope(node))
- case CGoto(label) => ???
- case SeqBranch(_, yes, no) => no match { // TODO: What are the conditions here?
+ case SeqBranch(_, yes, no) => no match {
case Some(stmt) => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node, 0)), None), CFGEdge(convert(stmt, context.enter_scope(node, 1)), None))
case None => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None))
}
- case SeqLoop(_, _, _) => evaluate_first(context.enter_scope(node)) // TODO: What are the conditions here?
- // Statements that can be categorized into a broader role for control flow analysis
- case ecs: ExpressionContainerStatement[_] => handle_expression_container(node, Eval(ecs.expr)(ecs.o), context, sequential_successor(context))
+ // Other statements that can be categorized into a broader role for control flow analysis
+ case _: ExpressionContainerStatement[_] => sequential_successor(context)
case _: ControlContainerStatement[_] => evaluate_first(context.enter_scope(node))
case _: PurelySequentialStatement[_] => sequential_successor(context)
}
- private def handle_expression_container(statement: Statement[G],
- expression: Eval[G],
- context: GlobalIndex[G],
- successors_to_statement: mutable.Set[CFGEdge[G]]): mutable.Set[CFGEdge[G]] = {
- context.indices.head match {
- case ExpressionContainerIndex(stmt, 1) if stmt == statement => successors_to_statement
- case _ => mutable.Set(CFGEdge(convert(expression, context.enter_scope(expression)), None))
- }
- }
-
private def evaluate_first(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] = {
if (index.has_statement()) mutable.Set(CFGEdge(resolve_index(index), None))
else sequential_successor(index)
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 656ea5dce4..4f11e26fca 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -160,7 +160,7 @@ object Index {
case seq_loop: SeqLoop[G] => SeqLoopIndex(seq_loop)
case veymont_assign_expression: VeyMontAssignExpression[G] => VeyMontAssignExpressionIndex(veymont_assign_expression)
case communicatex: CommunicateX[G] => CommunicateXIndex(communicatex)
- case statement: Statement[G] => ExpressionContainerIndex(statement, index)
+ case statement: ExpressionContainerStatement[G] => ExpressionContainerIndex(statement, index)
}
def apply[G](node: Node[G], index: Int): Index[G] = from(node, index)
@@ -185,12 +185,15 @@ case class RunMethodIndex[G](run_method: RunMethod[G]) extends Index[G] {
}
}
-case class ExpressionContainerIndex[G](statement: Statement[G], index: Int) extends Index[G] {
+case class ExpressionContainerIndex[G](statement: ExpressionContainerStatement[G], index: Int) extends Index[G] {
override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = {
if (index == 0) Set((Step(ExpressionContainerIndex(statement, 1)), None))
else Set((Outgoing(), None))
}
- override def resolve(): Statement[G] = statement
+ override def resolve(): Statement[G] = index match {
+ case 0 => Eval(statement.expr)(statement.expr.o)
+ case 1 => statement
+ }
override def equals(obj: scala.Any): Boolean = obj match {
case ExpressionContainerIndex(s, i) => i == index && s.equals(statement)
case _ => false
@@ -656,7 +659,7 @@ case class UnresolvedSeqLoopIndex[G](unresolved_seq_loop: UnresolvedSeqLoop[G],
}
case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Outgoing(), None)) // TODO: What are the conditions?
override def resolve(): Statement[G] = index match {
case 0 => seq_branch.yes
case 1 => seq_branch.no.get
@@ -668,7 +671,7 @@ case class SeqBranchIndex[G](seq_branch: SeqBranch[G], index: Int) extends Index
}
case class SeqLoopIndex[G](seq_loop: SeqLoop[G]) extends Index[G] {
- override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing(), None))
+ override def make_step(): Set[(NextIndex[G], Option[Expr[G]])] = Set((Step(this), None), (Outgoing(), None)) // TODO: What are the conditions?
override def resolve(): Statement[G] = seq_loop.body
override def equals(obj: scala.Any): Boolean = obj match {
case SeqLoopIndex(s) => s.equals(seq_loop)
diff --git a/src/rewrite/vct/rewrite/cfg/Utils.scala b/src/rewrite/vct/rewrite/cfg/Utils.scala
index 9b543653b1..4fd3f5f92a 100644
--- a/src/rewrite/vct/rewrite/cfg/Utils.scala
+++ b/src/rewrite/vct/rewrite/cfg/Utils.scala
@@ -4,8 +4,6 @@ import vct.col.ast._
import vct.col.origin.Origin
import vct.col.ref.{DirectRef, Ref}
-import scala.collection.mutable
-
object Utils {
def find_all_subexpressions[G](expr: Expr[G]): Seq[Statement[G]] = expr match {
@@ -96,6 +94,7 @@ object Utils {
def negate[G](expr: Expr[G]): Expr[G] = expr match {
case Not(inner) => inner
+ case BooleanValue(value) => BooleanValue(!value)(expr.o)
case _ => Not(expr)(expr.o)
}
From 53709b39cf2da1fb9a35a88d31bef6fbd79daad2 Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Mon, 26 Feb 2024 17:11:39 +0100
Subject: [PATCH 51/85] Fixed return followed by branches; removed scope nodes
from CFG
---
src/rewrite/vct/rewrite/cfg/CFGGenerator.scala | 17 ++++++++++++++---
src/rewrite/vct/rewrite/cfg/Index.scala | 7 +++----
2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
index 31e4637336..7d2d962233 100644
--- a/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
+++ b/src/rewrite/vct/rewrite/cfg/CFGGenerator.scala
@@ -18,8 +18,10 @@ case class CFGGenerator[G]() {
if (converted_nodes.contains(context)) return converted_nodes(context)
// Convert to CFG node depending on type of statement
node match {
+ // If a statement contains an expression, the expression(s) must be evaluated before the statement
case assign: Assign[_] => assign_to_cfg(assign, context)
case stmt: ExpressionContainerStatement[_] => expression_container_to_cfg(stmt, context)
+ // Handle labels and gotos, since those can occur in arbitrary order
case label: Label[_] =>
val cfg_node: CFGNode[G] = statement_to_cfg(node, context)
// For labels, add them to the label map and add them to any goto statements going to that label
@@ -34,6 +36,13 @@ case class CFGGenerator[G]() {
else searched_labels.addOne((goto.lbl.decl, mutable.Set(cfg_node)))
}
cfg_node
+ // Leave all container statements except for invocations out of the CFG
+ case _: InvocationStatement[_] => statement_to_cfg(node, context)
+ case c: ControlContainerStatement[_] =>
+ val new_context = context.enter_scope(c)
+ if (!new_context.has_statement()) statement_to_cfg(c, context)
+ else convert(new_context.resolve().get, new_context)
+ // Any non-special statement is simply converted to a CFG node
case _ => statement_to_cfg(node, context)
}
}
@@ -69,7 +78,7 @@ case class CFGGenerator[G]() {
// Get the successor(s) of the fork statement as well as the new thread, starting with the run method
sequential_successor(context).addOne(CFGEdge(convert(run_method.body.get, GlobalIndex[G](mutable.Seq()).enter_scope(run_method)), None))
// Statements that jump out of the current control flow context
- case Return(_) => mutable.Set(CFGEdge(return_successor(context), None))
+ case Return(_) => return_successors(context)
case Throw(obj) => mutable.Set(CFGEdge(exception_successor(obj, context), None))
case Break(label) => label match {
case Some(ref) => ??? // TODO: Handle break label!
@@ -87,6 +96,8 @@ case class CFGGenerator[G]() {
case Some(stmt) => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node, 0)), None), CFGEdge(convert(stmt, context.enter_scope(node, 1)), None))
case None => mutable.Set(CFGEdge(convert(yes, context.enter_scope(node)), None))
}
+ // Assign statements cannot be easily categorized because they contain two expressions
+ case Assign(_, _) => sequential_successor(context)
// Other statements that can be categorized into a broader role for control flow analysis
case _: ExpressionContainerStatement[_] => sequential_successor(context)
case _: ControlContainerStatement[_] => evaluate_first(context.enter_scope(node))
@@ -133,8 +144,8 @@ case class CFGGenerator[G]() {
successor_to_previous
}
- private def return_successor(index: GlobalIndex[G]): CFGEntry[G] =
- resolve_index(index.return_from_call())
+ private def return_successors(index: GlobalIndex[G]): mutable.Set[CFGEdge[G]] =
+ index.return_from_call().map(t => CFGEdge(resolve_index(t._1), t._2))
private def exception_successor(exception: Expr[G], index: GlobalIndex[G]): CFGEntry[G] =
resolve_index(index.handle_exception(exception))
diff --git a/src/rewrite/vct/rewrite/cfg/Index.scala b/src/rewrite/vct/rewrite/cfg/Index.scala
index 4f11e26fca..a5b634c149 100644
--- a/src/rewrite/vct/rewrite/cfg/Index.scala
+++ b/src/rewrite/vct/rewrite/cfg/Index.scala
@@ -45,15 +45,14 @@ case class GlobalIndex[G](indices: mutable.Seq[Index[G]]) {
def has_statement(): Boolean = indices.nonEmpty && indices.head.has_statement()
- def return_from_call(): GlobalIndex[G] = {
+ def return_from_call(): mutable.Set[(GlobalIndex[G], Option[Expr[G]])] = {
// Find innermost subroutine call
val stack: mutable.Seq[Index[G]] = indices.dropWhile {
case InvokeProcedureIndex(_, _) | InvokeMethodIndex(_, _) => false
case _ => true
}
- // Find the next statement
- // TODO: Does this always return exactly one next step?
- GlobalIndex(stack.tail).make_step().head._1
+ // Find the possible next statements
+ GlobalIndex(stack.tail).make_step()
}
def handle_exception(e: Expr[G]): GlobalIndex[G] = {
From 7c3857c27adcbc2dbac1acc2485886fb33ddedbc Mon Sep 17 00:00:00 2001
From: Philip Tasche
Date: Tue, 27 Feb 2024 09:33:01 +0100
Subject: [PATCH 52/85] Added node back to CFG if it is the first context of a
run method; removed old Java attempt at RASI generator
---
.../invariants/AbstractProcess.java | 132 ------------------
.../invariants/AbstractState.java | 41 ------
.../invariants/ExecutableState.java | 24 ----
.../invariants/ExecutionState.java | 4 -
.../systemctocol/invariants/Generator.java | 52 -------
.../invariants/ResolvedExecutionState.java | 43 ------
.../variables/ConcreteVariable.java | 20 ---
.../invariants/variables/FieldVariable.java | 27 ----
.../variables/SequenceIndexVariable.java | 44 ------
.../vct/rewrite/cfg/CFGGenerator.scala | 7 +-
.../vct/rewrite/rasi/AbstractProcess.scala | 4 +-
11 files changed, 8 insertions(+), 390 deletions(-)
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java
delete mode 100644 src/parsers/vct/parsers/transform/systemctocol/invariants/variables/SequenceIndexVariable.java
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
deleted file mode 100644
index 53942c79a8..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractProcess.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants;
-
-import vct.col.ast.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class AbstractProcess {
-
- protected RunMethod process_method;
-
- public AbstractProcess(RunMethod method) {
- this.process_method = method;
- }
-
- public List> simulate(int[] program_counter, AbstractState starting_state) {
- List> final_result = new ArrayList<>();
-
- List> current_branches = new ArrayList<>();
- List> current_stack = resolve(program_counter);
- current_branches.add(new ResolvedExecutionState<>(starting_state, program_counter, current_stack));
-
- return final_result;
- }
-
- private List> small_step() {
- return null;
- }
-
- /**
- * Returns the current statement stack at the execution state given by the program counter. Iteratively resolves the
- * context of each statement and returns a stack with the highest-level container at position 0 and the statement
- * currently to be evaluated at position n - 1.
- *
- * @param program_counter A list of indices showing which point in each container points to the right statement
- * @return A list of container statements, with the desired statement in the last position
- */
- private List> resolve(int[] program_counter) {
- List> stack = new ArrayList<>();
- Statement context = process_method.body().get();
- stack.add(context);
- for(int index : program_counter) {
- context = get_at_index(context, index);
- stack.add(context);
- }
- return stack;
- }
-
- /**
- * Takes a statement that (can) contain other statements as well as an index and returns the given index statement
- * contained within the container.
- *
- * @param container Container statement
- * @param index Index of desired contained statement
- * @return Statement at index index
within the body of container
- */
- private Statement get_at_index(Statement container, int index) {
- // Ignores the following:
- // TryCatchFinally
- // Synchronized
- // ParInvariant
- // ParAtomic
- // VecBlock
- // WandPackage
- // ModelDo
- if (container instanceof PVLBranch pvl_branch) {
- return pvl_branch.branches().apply(index)._2();
- }
- else if (container instanceof PVLLoop pvl_loop) {
- return switch (index) {
- case 0 -> pvl_loop.init();
- case 1 -> pvl_loop.body();
- case 2 -> pvl_loop.update();
- default -> throw new IndexOutOfBoundsException("Loop index must at most be 2.");
- };
- }
- else if (container instanceof InvokeProcedure invoke_procedure) {
- if (index == 0) {
- return invoke_procedure.ref().decl().body().get();
- }
- else throw new IndexOutOfBoundsException("Invalid index for procedure invocation.");
- }
- else if (container instanceof InvokeConstructor invoke_constructor) {
- if (index == 0) {
- return invoke_constructor.ref().decl().body().get();
- }
- else throw new IndexOutOfBoundsException("Invalid index for constructor invocation.");
- }
- else if (container instanceof InvokeMethod invoke_method) {
- if (index == 0) {
- return invoke_method.ref().decl().body().get();
- }
- else throw new IndexOutOfBoundsException("Invalid index for method invocation.");
- }
- else if (container instanceof Block block) {
- return block.statements().apply(index);
- }
- else if (container instanceof Scope scope) {
- if (index == 0) {
- return scope.body();
- }
- else throw new IndexOutOfBoundsException("Invalid index for scope.");
- }
- else if (container instanceof Branch branch) {
- return branch.branches().apply(index)._2();
- }
- else if (container instanceof IndetBranch indet_branch) {
- return indet_branch.branches().apply(index);
- }
- else if (container instanceof Switch switch_stmt) {
- if (index == 0) {
- return switch_stmt.body();
- }
- else throw new IndexOutOfBoundsException("Invalid index for switch statement.");
- }
- else if (container instanceof Loop loop) {
- return switch (index) {
- case 0 -> loop.init();
- case 1 -> loop.body();
- case 2 -> loop.update();
- default -> throw new IndexOutOfBoundsException("Loop index must at most be 2.");
- };
- }
- else if (container instanceof RangedFor ranged_for) {
- if (index == 0) {
- return ranged_for.body();
- }
- else throw new IndexOutOfBoundsException("Invalid index for foreach loop.");
- }
- else throw new IllegalArgumentException("Statement " + container + " is not supported as a container.");
- }
-}
\ No newline at end of file
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
deleted file mode 100644
index 64a2c73cbc..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/AbstractState.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants;
-
-import vct.col.ast.Expr;
-import vct.parsers.transform.systemctocol.invariants.variables.ConcreteVariable;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class AbstractState {
- private final Map, Integer> valuations;
-
- public AbstractState() {
- valuations = new HashMap<>();
- }
-
- public AbstractState(AbstractState previous) {
- this.valuations = new HashMap<>(previous.valuations);
- }
-
- public boolean contains_variable(Expr variable_expression) {
- return valuations.keySet().stream().anyMatch((var) -> var.is(variable_expression));
- }
-
- public void set_valuation(ConcreteVariable var, int value) {
- valuations.put(var, value);
- }
-
- public int get_valuation(ConcreteVariable var) {
- return valuations.get(var);
- }
-
- public int get_valuation(Expr variable_expression) {
- return valuations.entrySet().stream()
- .filter((entry) -> entry.getKey().is(variable_expression))
- .findFirst().orElseThrow().getValue();
- }
-
- public boolean equals(AbstractState other) {
- return valuations.equals(other.valuations);
- }
-}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
deleted file mode 100644
index 383253d726..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutableState.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public record ExecutableState (AbstractState state,
- int[][] execution_location_by_process,
- int next_process_id) {
-
- public List> execute_step(List> processes) {
- AbstractProcess next_process = processes.get(next_process_id);
- List> updates = next_process.simulate(execution_location_by_process[next_process_id], state);
-
- List> result = new ArrayList<>();
- for (ExecutionState update : updates) {
- int[][] updated_counter = execution_location_by_process.clone();
- updated_counter[next_process_id] = update.program_counter();
- for (int i = 0; i < processes.size(); i++) {
- result.add(new ExecutableState<>(update.abstract_state(), updated_counter, i));
- }
- }
- return result;
- }
-}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
deleted file mode 100644
index a28d2f0bfa..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/ExecutionState.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants;
-
-public record ExecutionState (AbstractState abstract_state, int[] program_counter) {}
-
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
deleted file mode 100644
index 7587c894e3..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/Generator.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants;
-
-import vct.col.ast.InstanceMethod;
-import vct.parsers.ParseResult;
-import vct.parsers.transform.systemctocol.invariants.variables.ConcreteVariable;
-
-import java.util.*;
-
-public class Generator {
-
- private final List> processes;
-
- private final List> active_branches;
-
- private final Set> considered_branches;
-
- public Generator(ParseResult parse_result,
- Map, Integer> considered_variables,
- InstanceMethod main_method) {
- // Initialize processes
- processes = new ArrayList<>();
- initialize_processes();
-
- // Initialize active branches
- active_branches = new ArrayList<>();
-
- // Initialize considered branches to empty list
- considered_branches = new HashSet<>();
- }
-
- public void execute() {
- initialize_branches();
-
- while (!active_branches.isEmpty()) {
- ExecutableState exploring = active_branches.remove(0);
- List> new_possibilities = exploring.execute_step(processes)
- .stream()
- .filter((state) -> !considered_branches.contains(state))
- .toList();
- considered_branches.addAll(new_possibilities);
- active_branches.addAll(new_possibilities);
- }
- }
-
- private void initialize_processes() {
-
- }
-
- private void initialize_branches() {
- AbstractState initial_state = new AbstractState<>();
- }
-}
\ No newline at end of file
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java
deleted file mode 100644
index 4a0c8047e2..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/ResolvedExecutionState.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants;
-
-import vct.col.ast.Statement;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ResolvedExecutionState {
-
- private AbstractState state;
- private int[] program_counter;
- private List> program_location;
-
- public ResolvedExecutionState(AbstractState state, int[] program_counter, List> program_location) {
- this.state = new AbstractState<>(state);
- this.program_counter = program_counter.clone();
- this.program_location = new ArrayList<>(program_location);
- }
-
- public AbstractState get_state() {
- return state;
- }
-
- public int[] get_program_counter() {
- return program_counter;
- }
-
- public List> get_program_location() {
- return program_location;
- }
-
- public Statement get_current_statement() {
- return program_location.get(program_location.size() - 1);
- }
-
- public void move_forward() {
-
- }
-
- public ExecutionState export() {
- return new ExecutionState<>(state, program_counter);
- }
-}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java
deleted file mode 100644
index a288348988..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/ConcreteVariable.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants.variables;
-
-import vct.col.ast.Deref;
-import vct.col.ast.Expr;
-import vct.col.ast.Field;
-import vct.col.ast.PVLDeref;
-
-public abstract class ConcreteVariable {
-
- public abstract boolean is(Expr expression);
-
- public abstract boolean equals(ConcreteVariable other);
-
- protected Field extract_from_expression(Expr expression) {
- if (expression instanceof Deref deref) {
- return deref.ref().decl();
- }
- else throw new IllegalArgumentException("Could not parse expression " + expression.toString());
- }
-}
diff --git a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java b/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java
deleted file mode 100644
index 52f9fbdd94..0000000000
--- a/src/parsers/vct/parsers/transform/systemctocol/invariants/variables/FieldVariable.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package vct.parsers.transform.systemctocol.invariants.variables;
-
-import vct.col.ast.Expr;
-import vct.col.ast.Field;
-
-public class FieldVariable extends ConcreteVariable {
-
- protected Field field;
-
- public FieldVariable(Field field) {
- this.field = field;
- }
-
- @Override
- public boolean is(Expr