Skip to content

Stainless crash: Variable ... is not defined in context when lifting two while loops #1742

Description

@sfiruch

Claude reports:

object CrashRepro {
  def f(t: Int, rob: Int, lim: Int): Int = {
    require(rob >= 0 && t >= 0 && lim >= 0)
    var cT = 0
    var vT = rob
    if (t != 0) {
      cT = 0
      vT = cT + rob
      var cont = true
      (while (cont && vT <= t && vT <= lim) {
        decreases(lim - vT + (if (cont) 1 else 0))
        if (vT < lim) { cT = cT + 1; vT = cT + rob } else { cont = false }
      }).invariant(cT >= 0 && vT >= rob)
    }
    var i = 0
    var acc = 0
    (while (i < 8) {
      decreases(8 - i)
      acc = acc + (if (vT > i) 1 else 0)
      i = i + 1
    }).invariant(i >= 0 && acc >= 0)
    vT + cT + acc
  }
}
  • Stainless: 0.9.9.3 (stainless-dotty-standalone-0.9.9.3.jar, Scala 3.7.2)
  • JVM: any (reproduced on Zulu OpenJDK 26.0.1)
  • OS: reproduced on Windows 11; not OS-specific.

How to reproduce

stainless --config-file=false CrashRepro.scala

Observed behaviour

Stainless aborts with:

[ Fatal  ] CrashRepro.scala:33:7: Variable cT 2 is not defined in context:
[ Fatal  ] Kind: error: argument types (call fWhile(cT, lim, rob, t, vT, i, acc, vT))
[ Fatal  ] Functions: fWhile
...

inox.package$FatalError: Variable cT 2 is not defined in context
    at inox.Reporter.fatalError(Reporter.scala:56)
    at stainless.verification.TypeChecker.inferType(TypeChecker.scala:723)
    at stainless.verification.TypeChecker.checkType(TypeChecker.scala:1262)
    ...

Expected behaviour

The program should either verify normally or report a legitimate user error — not
abort with an internal "not defined in context" failure.

What seems to be going on

The function f contains two while loops. Both are lifted to a helper named
fWhile — i.e. the generated names collide (no disambiguating suffix). From the error
dump, the two call sites have different signatures:

  • inner loop (inside the if): fWhile(lim, rob, t, cT, vT, cont) — 6 args
  • trailing loop (after the if): fWhile(cT, lim, rob, t, vT, i, acc, vT) — 8 args

Resolving one call against the other fWhile definition leaves cT undefined in the
calling context, which is what the type-checker reports.

Minimisation notes

All of the following are required; removing any one makes the error disappear:

  • two local vars where one is defined in terms of the other (vT = cT + rob);
  • both reassigned inside a while loop that is nested inside an if;
  • a boolean loop-guard var (cont) updated in the loop body;
  • one of the outer vars (vT) read by a second while loop after the if.

In particular, collapsing to a single var (dropping vT), or deleting the trailing
while loop, both make it compile cleanly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions