Skip to content

Do not prove false theorems about ENABLED, <>, and \cdot under a binder - #314

Merged
lemmy merged 2 commits into
mainfrom
mku-gh309
Nov 10, 2026
Merged

lemmy merged 2 commits into
mainfrom
mku-gh309

Conversation

@lemmy

@lemmy lemmy commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

A term that tlapm does not translate (ENABLED, a temporal formula) is abstracted as a nullary constant, dropping the variables it depends on. Each test is a false theorem that tlapm currently proves (by SMT, Zenon, or Isabelle).


When tlapm sends an obligation to a back-end, coalesce_modal_visitor in src/frontend/coalesce.ml replaces each subterm built from ENABLED, <>,
[], \cdot, Prime, or UNCHANGED by an opaque operator, because the back-ends cannot reason about these operators. The opaque operator must be applied to the free constants and free variables of the subterm, so that the abstraction still depends on everything the subterm depends on.

To find them, coalesce_modal_visitor calls vars_visitor#compute. compute starts a traversal of the subterm with the method expr. Whenever expr meets a constant or variable that is not bound inside the subterm, it adds it to the instance variable vars of vars_visitor. When the traversal ends, compute should return that set.

Instead, compute began with let vars = ref IxSet.empty in. That created a second, local vars with the same name, which expr never added to, and compute returned it. So:

  • The traversal collected the right constants and variables, into the instance variable.
  • compute never read that variable. It returned the empty local set, so every abstraction got no arguments.
  • The instance variable was never reset either, so it kept growing across calls. Nothing read it, so that did no harm.

As a result, a subterm under a binder, such as <>(x = t) in \E t : <>(x = t), became a constant h that does not depend on t. A back-end could then treat <>(x = t) as the same formula for every t and prove false theorems.


#309

@lemmy lemmy added the soundness Issues and PRs related to getting TLAPM to prove FALSE label Oct 26, 2026
lemmy added a commit that referenced this pull request Oct 28, 2026
tlapm proves (\E t : F(t)) => (\A t : F(t)) when F(t) is an ENABLED,
<> or \cdot term. The tests are taken from
#314
issue309_enabled_binder_smt_stest, issue309_enabled_binder_zenon_stest,
issue309_enabled_binder_isa_stest, issue309_eventually_binder_stest and
issue309_cdot_binder_stest are marked known.

Related to Github issue #309
#309

[Tests]

Co-authored-by: scottcochran <5066685+scottcochran@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit that referenced this pull request Oct 28, 2026
tlapm proves (\E t : F(t)) => (\A t : F(t)) when F(t) is an ENABLED,
<> or \cdot term. The tests are taken from
#314
issue309_enabled_binder_smt_stest, issue309_enabled_binder_zenon_stest,
issue309_enabled_binder_isa_stest, issue309_eventually_binder_stest and
issue309_cdot_binder_stest are marked known.

Related to Github issue #309
#309

[Tests]

Co-authored-by: scottcochran <5066685+scottcochran@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy

This comment was marked as outdated.

lemmy added a commit to lemmy/tlapm that referenced this pull request Oct 30, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit to lemmy/tlapm that referenced this pull request Oct 30, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit to lemmy/tlapm that referenced this pull request Oct 30, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit to lemmy/tlapm that referenced this pull request Oct 30, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit to lemmy/tlapm that referenced this pull request Oct 30, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit to lemmy/tlapm that referenced this pull request Nov 3, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
@lemmy
lemmy marked this pull request as ready for review November 4, 2026 15:41
@lemmy lemmy self-assigned this Nov 4, 2026
@lemmy
lemmy requested a review from muenchnerkindl November 4, 2026 16:03
@lemmy lemmy changed the title Add soundness reproducers for #309 Do not prove false theorems about ENABLED, <>, and \cdot under a binder Nov 4, 2026
@lemmy
lemmy force-pushed the mku-gh309 branch 3 times, most recently from 31b1eb5 to 4e2f7d4 Compare November 5, 2026 01:04
@lemmy
lemmy requested a balanced review from Copilot November 5, 2026 01:16

This comment was marked as resolved.

lemmy added a commit to lemmy/tlapm that referenced this pull request Nov 5, 2026
The action frontend of pull request tlaplus#314 abstracts [](x = 0) and
[](y = 0) as one function of the current values of x and y.  So tlapm
proves x = y => ([](x = 0) => [](y = 0)), although the two formulas
depend on the whole behavior.  Main does not prove the theorem, so the
test is not marked known; it fails once tlaplus#314 is merged as it is.

tlaplus#314

[Tests]

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
@lemmy
lemmy force-pushed the mku-gh309 branch 3 times, most recently from 3de45ce to 479413e Compare November 9, 2026 03:14
lemmy added a commit to tlaplus/Examples that referenced this pull request Nov 9, 2026
The draft soundness fix (tlaplus/tlapm#314)
exposes a completeness gap: coalescing assigns different opaque
identifiers to the lemma's n,o and the goal's u,v.
Expand ENABLED and the relevant definitions so ordinary first-order
instantiation can discharge the step.

The proof statement and assumptions remain unchanged. Unfolding
definitions and expanding ENABLED preserve the TLA+ meaning of the
obligation.

Related:
tlaplus/tlapm#309
tlaplus/tlapm#314

Co-authored-by: GPT-6.1 Sol Ultra <noreply@openai.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy added a commit to tlaplus/Examples that referenced this pull request Nov 9, 2026
The draft soundness fix (tlaplus/tlapm#314)
exposes a completeness gap: coalescing assigns different opaque
identifiers to the lemma's n,o and the goal's u,v.
Expand ENABLED and the relevant definitions so ordinary first-order
instantiation can discharge the step.

The proof statement and assumptions remain unchanged. Unfolding
definitions and expanding ENABLED preserve the TLA+ meaning of the
obligation.

Related:
tlaplus/tlapm#309
tlaplus/tlapm#314

Co-authored-by: GPT-6.1 Sol Ultra <noreply@openai.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
lemmy and others added 2 commits November 10, 2026 09:51
Terms abstracted under a binder ignored the bound variable, so tlapm
proved false theorems.

Fixes Github issue #309
#309
Related to Github issue #310
#310

[Tests]

Co-authored-by: scottcochran <5066685+scottcochran@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
Catch Invalid_type_downcast when an operator reference appears where
an ordinary expression is expected. Report "Operator-valued expression
is not supported here" through the normal error path, replacing the
generic unexpected SMT backend exception and backtrace.

Co-authored-by: GPT-6.1 Sol Ultra <noreply@openai.com>
Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>
@lemmy
lemmy marked this pull request as ready for review November 10, 2026 18:53
@lemmy

lemmy commented Nov 10, 2026

Copy link
Copy Markdown
Member Author
❯ git log -n1
commit 07bc9dbf930e10d62ace069914e3c1bb7a74c25c (HEAD -> main, origin/main, origin/HEAD)
Author: Markus Alexander Kuppe <github.com@lemmster.de>
Date:   Mon Nov 9 14:39:56 2026 -0800

    Add soundness tests.

    Each test is a false theorem that tlapm currently proves.

    Related:
    * Some tests were found by agentic bug finding
    * https://github.com/tlaplus/tlapm/issues/309
    * https://github.com/tlaplus/tlapm/issues/310

    [Tests]

    Co-Authored-By: Scott Cochran <5066685+scottcochran@users.noreply.github.com>
    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Co-authored-by: GPT-6.1 Sol Ultra <noreply@openai.com>
    Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>

 ~/.opam/5.5.2/bin/tlapm --version
07bc9db

❯ dune exec --no-build -- tlapm --strict --cleanfp --nofp test/soundness_tests/BinderUnsound.tla
(* fingerprints file ".tlacache/BinderUnsound.tlaps/fingerprints" removed *)
Zenon error: exhausted search space without finding a proof
(* created new ".tlacache/BinderUnsound.tlaps/BinderUnsound.thy" *)
(* fingerprints written in ".tlacache/BinderUnsound.tlaps/fingerprints" *)
File "./test/soundness_tests/BinderUnsound.tla", line 35, characters 1-7:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x
           PROVE  (\E t \in S : x = t) => (\A t \in S : x = t)
File "./test/soundness_tests/BinderUnsound.tla", line 1, character 1 to line 36, character 77:
[ERROR]: 1/9 obligations failed.
There were backend errors processing module `"BinderUnsound"`.

❯ git log -n1
commit 7eeb85651c3ed248f60f57c3b8642eebb36de2c5 (HEAD, origin/mku-gh309)
Author: Markus Alexander Kuppe <github.com@lemmster.de>
Date:   Mon Nov 9 16:10:37 2026 -0800

    Report unsupported operator values during type synthesis

    Catch Invalid_type_downcast when an operator reference appears where
    an ordinary expression is expected. Report "Operator-valued expression
    is not supported here" through the normal error path, replacing the
    generic unexpected SMT backend exception and backtrace.

    Co-authored-by: GPT-6.1 Sol Ultra <noreply@openai.com>
    Signed-off-by: Markus Alexander Kuppe <github.com@lemmster.de>

~/src/TLA/tlapm remotes/origin/mku-gh309*
❯ ~/.opam/5.5.2/bin/tlapm --version
7eeb856

❯ dune exec --no-build -- tlapm --strict --cleanfp --nofp test/soundness_tests/BinderUnsound.tla
(* fingerprints file ".tlacache/BinderUnsound.tlaps/fingerprints" removed *)
Zenon error: exhausted search space without finding a proof
Zenon error: exhausted search space without finding a proof
Zenon error: exhausted search space without finding a proof
Zenon error: exhausted search space without finding a proof
(* created new ".tlacache/BinderUnsound.tlaps/BinderUnsound.thy" *)
(* fingerprints written in ".tlacache/BinderUnsound.tlaps/fingerprints" *)
File "./test/soundness_tests/BinderUnsound.tla", line 35, characters 1-7:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x
           PROVE  (\E t \in S : x = t) => (\A t \in S : x = t)
File "./test/soundness_tests/BinderUnsound.tla", line 30, characters 1-2:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x,
                  BadEventuallyProp ==
                    (\E t \in S : <>(x = t)) => (\A t \in S : <>(x = t))
           PROVE  Spec => BadEventuallyProp
File "./test/soundness_tests/BinderUnsound.tla", line 23, characters 42-43:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x,
                  BadEnabled ==
                    (\E t \in S : ENABLED A(t))
                    => (\A t \in S : ENABLED A(t)),
                  Isa
           PROVE  BadEnabled
File "./test/soundness_tests/BinderUnsound.tla", line 22, characters 42-43:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x,
                  BadEnabled ==
                    (\E t \in S : ENABLED A(t))
                    => (\A t \in S : ENABLED A(t)),
                  Zenon
           PROVE  BadEnabled
File "./test/soundness_tests/BinderUnsound.tla", line 21, characters 42-43:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x,
                  BadEnabled ==
                    (\E t \in S : ENABLED A(t))
                    => (\A t \in S : ENABLED A(t)),
                  SMT
           PROVE  BadEnabled
File "./test/soundness_tests/BinderUnsound.tla", line 20, characters 42-43:
[ERROR]: Could not prove or check:
           ASSUME NEW CONSTANT S,
                  NEW VARIABLE x,
                  BadEnabled ==
                    (\E t \in S : ENABLED A(t))
                    => (\A t \in S : ENABLED A(t))
           PROVE  BadEnabled
File "./test/soundness_tests/BinderUnsound.tla", line 1, character 1 to line 36, character 77:
[ERROR]: 6/9 obligations failed.
There were backend errors processing module `"BinderUnsound"`.

@lemmy
lemmy merged commit 265da74 into main Nov 10, 2026
5 checks passed
@lemmy
lemmy deleted the mku-gh309 branch November 10, 2026 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

soundness Issues and PRs related to getting TLAPM to prove FALSE

Development

Successfully merging this pull request may close these issues.

2 participants