Skip to content

implies appears 9 times in live source and 0 times in the compiler: the consequent is discarded and typecheck reports OK #2992

Description

@gHashTag

implies is a word this compiler has never heard, and 82 assertions are silently truncated at the antecedent

$ grep -c 'implies\|==>' bootstrap/src/compiler.rs
0
$ grep -rn --include='*.t27' 'implies' specs/ | wc -l
82          (in 19 specs)

The corpus writes implication 82 times. The compiler contains no token, no
parse arm and no lowering for it, in any spelling.

What happens instead

Minimal probe, four lines, written from scratch:

invariant a_implies_b
    given x = a()
    assert x == true implies b() == false
$ t27c gen-c /tmp/imp.t27
void invariant_a_implies_b(void) {
    __auto_type x = a();
    assert((x == true));          <-- the consequent is gone
}
$ t27c check /tmp/imp.t27
Typecheck OK (0 errors, 0 warnings)

Real corpus site, specs/fpga/top_level.t27:201:

assert busy == false implies ready == true
/* gen-c */            assert((busy == false));
/* gen */              const ready = system_ready();
                       _ = ready; // dead after const-inlining
                       if (!(busy == false)) __t27_assert_fail(...);
                       // invariant: ... NOT CHECKED -- body was not lowered (T43)

Why this is worse than dropping the assertion

P implies Q is vacuously true whenever P is false. What is emitted is
assert P -- a different and strictly stronger proposition. The invariant
above says nothing when the system is busy; the generated code demands it
not be busy. An assertion that says nothing is a gap; an assertion that says
something else can fail on correct behaviour, or pass while the property it was
written for is violated.

Zig's output is the honest half of the same confusion: it emits the wrong check
AND, two lines later, NOT CHECKED -- body was not lowered. Two contradictory
statements about one invariant, in adjacent lines.

Not Verilog-only

This is C, Zig and Rust. The Verilog backend emits every invariant as a comment
(#2869), so it is the one backend where the truncation cannot mislead anybody --
the peer-backend oracle this project relies on has all three surviving arms
agreeing on the same wrong proposition, which is exactly the case #2847 named as
where cross-checking is blind.

The decision in it

Adding ==> as !a || b was already declined once, in #2774, for a stated
reason: a false antecedent then makes the invariant a pass, and what a quantified
implication means at codegen commits four backends. That reason still holds and
this issue does not reopen it.

What is not a decision: the silence. Whatever implies should mean, the
compiler should not accept the word, discard half the assertion, and print
Typecheck OK. The narrow repair is to reject it by name until it has a
meaning -- the third state this repository already has a word for.

Refs #2774, #2869

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions