1

I am trying to define the small-step semantics of a very simple language with arithmetic expressions (source code available here). For the sake of simplicity, we assume that the language only allows literals and the unary minus (-exp).

datatype expression = Literal(int) | Minus(expression)

I have defined a relation e1 --> e2 specifying how to perform a step of computation from the expression e1 to obtain the expression e2. Let us just assume for simplicity that this relation always holds:

predicate Step(exp: expression, exp': expression) { true }

(Any other nontrivial relation would also lead to the problem I describe here).

Now I define a relation e1 -->* e2 specifying that e1 can be reduced into e2 after zero, one, or more steps of computation (as in R. Leino's paper on extreme predicates, pg. 11):

inductive predicate StepStar(exp: expression, exp': expression) {
    (exp == exp')
    || (exists exp'': expression :: Step(exp, exp'') && StepStar(exp'', exp'))
}

I would like to prove that the -->* is closed under contexts. That is, that e -->* e' implies Minus(e) -->* Minus(e'). This can be proved by induction on the derivation of -->*:

inductive lemma StepStarContext(exp1: expression, exp3: expression)
    requires StepStar(exp1, exp3)
    ensures StepStar(Minus(exp1), Minus(exp3)) 
{
    if (exp1 == exp3) {
        // Base case: zero steps.
    } else {
        // Inductive step.

        // We unfold the exp1 -->* exp3 relation into: exp1 --> exp2 -->* exp3.
        var exp2 :| Step(exp1, exp2) && StepStar(exp2, exp3);

        // Apply induction hypothesis on exp2 -->* exp3.
        StepStarContext(exp2, exp3);

        // ASSERTION VIOLATION: 
        // Why Minus(exp2) -->* Minus(exp3) cannot be proved?
        assert StepStar(Minus(exp2), Minus(exp3));
    }
}    

Dafny cannot prove the last assertion, although it should follow from the postcondition application StepStarContext(exp2, exp3). If I comment out that assertion Dafny can prove the lemma successfully (in fact, Dafny can still prove it when its body is empty), but I am still intrigued by the fact it cannot prove this assertion. Am I missing something?

I have also derived the prefix lemma as described in Leino's paper (see source code). Strangely, Dafny does not only prove this assertion in the prefix lemma, but also in the inductive lemma StepStarContext. Why?

Any help would be appreciated.

Dafny version: 2.1.1

1 Answers1

1

I have realized that the inductive lemma is desugared as follows:

lemma StepStarContext#[_k: ORDINAL](exp1: expression, exp3: expression)
  requires StepStar#[_k](exp1, exp3)
  ensures StepStar(Minus(exp1), Minus(exp3)) 
{
  if (exp1 == exp3) {
     ...
  } else {
    ...
    // Apply induction hypothesis on exp2 -->* exp3.
    StepStarContext(exp2, exp3);

    // ASSERTION VIOLATION: 
    assert StepStar#[_k - 1](Minus(exp2), Minus(exp3));
  }
}    

Basically, the unsatisfied assertion asks the length of exp2 -->* exp3 to be _k - 1, but this does not follow from induction hypothesis, since the conclusion of the lemma says that Minus(exp1) --> Minus(exp3) for some derivation length. So, in the induction hypothesis, we cannot expect exp2 -->* exp3 to be of length _k - 1.

After fixing the assertion as follows,

assert exists k' :: StepStar#[k'](Minus(exp2), Minus(exp3));

Dafny gives the go-ahead.