Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Trivial copy assignment (y := x) is omitted from SMT named assumes → missing from unsat cores / coverage #1066

@ricostynha1

Description

@ricostynha1

Reproduce

From Dafny using this version:
4.11.0+fcb2042d6d043a2634f0854338c08feeaaaf4ae2
With this code:

method Abs(x: int) returns (y: int)
  ensures x>=0 ==> x==y
  ensures x<0 ==> x==-y
{
  if x < 0 {
    y := -x;
  } else {
    y :=  x;
  }
}

Running the proof dependencies tool:
dafny verify [file_name] --verification-coverage-report cov --log-format text --solver-option LOG_FILE=output.smt2 --bprint output.bpl

We obtain that line y:=x was unused by the proof.

Problem

The thing is:
In the generated Boogie implementation I see both assignment statements present:

implementation {:smt_option "smt.arith.solver", "2"} {:verboseName "Abs (correctness)"} Impl$$_module.__default.Abs(x#0: int) returns (defass#y#0: bool, y#0: int, $_reverifyPost: bool)
{
  var $_ModifiesFrame: [ref,Field]bool;

    // AddMethodImpl: Abs, Impl$$_module.__default.Abs
    $_ModifiesFrame := (lambda $o: ref, $f: Field :: 
      $o != null && $Unbox(read($Heap, $o, alloc)): bool ==> false);
    assume {:captureState "abs.dfy(4,0): initial state"} true;
    $_reverifyPost := false;
    // ----- if statement ----- abs.dfy(5,3)
    assume true;
    if (x#0 < 0)
    {
        // ----- assignment statement -----  abs.dfy(6,7)
        assume true;
        assume true;
        y#0 := 0 - x#0;
        defass#y#0 := true;
        assume {:captureState "abs.dfy(6,11)"} true;
    }
    else
    {
        // ----- assignment statement -----  abs.dfy(8,7)
        assume true;
        assume true;
        y#0 := x#0;
        defass#y#0 := true;
        assume {:captureState "abs.dfy(8,11)"} true;
    }

    assert {:id "id12"} defass#y#0;
}

In the SMT translation (output.smt2) the arithmetic assignment (y := -x) leads to a small, named assume that can be extracted in unsat cores / coverage. Example pattern (from SMT):

assert (! $generated@@93 :named aux$$assume$$id10))
(=> $generated@@93 (= $generated@@94 (- 0 $generated@@90))

That aux$$assume$$id10 name lets the coverage/unsat-core tooling attribute the verification obligation to the Dafny/Boogie source line.

However, for the trivial copy y := x there is no analogous named assume generated in the SMT. As a result the coverage report (and unsat-core extraction) never attributes that source line as a relevant proof node — it appears “unused by the proof”

Why I think this is a problem

From a coverage / proof-explanation point of view a line present in the Boogie translation should be reflected as a traceable proof obligation (or at least have instrumentation) so tools that extract unsat cores or compute verification coverage can report which source lines were relevant to the proof. Omitting a named assume for y := x makes the coverage tool miss that line.

This is surprising because semantically y := x is a state mutation like y := x + 2. And y := x + 2 generated the named assume clause.

Workarounds

Replacing y := x with a call to an identity function forces the SMT to create a named assume, so coverage shows the line as relevant. Example:

function {:inline} id(x: int): int := x
...
y := id(x);

Expected behaviour

1 - The translation should produce a named assume (or other traceable instrumentation) for every assignment statement in the Boogie program, even trivial copies like y := x, so coverage / unsat-core extraction can reliably point to source lines; or
2 - There should be a clear documented option to disable the collapsing/optimization that removes such named assumptions, or an option to enable “full instrumentation” for coverage/unsat-core extraction.

Question

1 - Is this an intentional optimization in Boogie -> smt pipeline (i.e., trivial copies are collapsed and not instrumented), or is it a bug/omission in the SMT-name/assume generation?
2 - If this is intentional, is there a recommended way to force per-statement named assumptions (or otherwise ensure that every source statement appears in unsat cores / coverage reports) without modifying source code (i.e., without adding id(x) calls or asserts)?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions