Skip to content

Alternate translation of do-while-block to avoid or (||) short circuiting#158

Merged
hei411 merged 6 commits into
mainfrom
heili/do-while
Jul 2, 2026
Merged

Alternate translation of do-while-block to avoid or (||) short circuiting#158
hei411 merged 6 commits into
mainfrom
heili/do-while

Conversation

@hei411

@hei411 hei411 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

This is a pull request making changes to the do-while loop. Changes and design choices are a bit subtle, which I describe below:

Originally a do while block of the form do {body} while(cond) is translated to bool first = true; while (first || cond){ body; first = false; }.
This is troublesome since || does not support short-circuiting. F* also complains a lot when cond performs external stateful function calls.

Instead, we translate the do while block to bool cont = true; bool first = true; while (cont) { body; cont = cond; first = false; }

  • We keep the variable first as before for backwards compatibility.
  • Here this transformation is only performed when body does not contain continue in the top level. Otherwise, we fall back to the original translation.
  • just like _do_while_first, we add a PAL annotation _do_while_cond to refer to cont for defining invariants
  • PAL automatically inserts an invariant cont==first||cond if cond is pure. This is also used to support backwards compatibility.

@gebner

gebner commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Please rebase to main. Otherwise lgtm.

t-heili and others added 6 commits July 2, 2026 14:10
Previously `do { body } while (cond)` was always desugared to
`bool first = true; while (first || cond) { first = false; body; }`.

This adds a cleaner desugaring used when the do-body contains no
top-level `continue` (continues inside a nested for/while/do-while are
excluded via hasTopLevelContinue, which treats loops as boundaries but
descends into if/switch/blocks/labels):

    bool cont = true; bool first = true;
    while (cont) { body; first = false; cont = cond; }

An internal `cont` flag drives the loop; a separate `first` flag (named
by _do_while_first) preserves first-iteration semantics for invariants.
To keep loops whose body relies on the loop condition verifiable, we
auto-inject the invariant `cont == (first || cond)` plus `_live(cont)`.

When a top-level `continue` is present, we fall back to the original
desugaring unchanged. Both paths now save/restore forLoopIncrement
around body translation.

Adds test/do_while_nested_continue exercising the new path with a
continue nested in an inner loop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the nested-loop-continue example into test/do_while as a new
`nested_continue` function instead of a standalone test folder, keeping
all do-while desugaring cases together.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The clean do-while desugaring auto-injects `cont == (first || cond)` as a
`with_pure` loop invariant. When the guard `cond` has side effects (e.g.
`do { ... } while (f());` with a bool-returning function `f`), embedding
it in a pure invariant is rejected by F* (Error 228, "Cannot use ... in
impure spec").

Gate the linking invariant on `!cond->HasSideEffects(astCtx)`: for an
impure guard we omit it entirely. `cont` already holds the guard's most
recent value (assigned at the end of the body), which is all an impure
guard can convey; there is no pure fact to state.

Adds `f` + `g_loop` (function-call guard, empty body) to test/do_while,
ported from ../loopback-new/pal-tests/bool_call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hei411 hei411 marked this pull request as ready for review July 2, 2026 21:36
@gebner

gebner commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Please fix the PR title and then feel free to merge!

@hei411 hei411 changed the title Heili/do while Alternate translation of do while block to avoid or || short circuiting Jul 2, 2026
@hei411 hei411 changed the title Alternate translation of do while block to avoid or || short circuiting Alternate translation of do-while-block to avoid or (||) short circuiting Jul 2, 2026
@hei411 hei411 merged commit e9310eb into main Jul 2, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants