Skip to content

Partial spec lemmas simplified by simp + some library application#4

Open
abentkamp wants to merge 14 commits into
devfrom
spec_partial2
Open

Partial spec lemmas simplified by simp + some library application#4
abentkamp wants to merge 14 commits into
devfrom
spec_partial2

Conversation

@abentkamp

Copy link
Copy Markdown

This PR changes the mechanism for simplifying hypotheses in the lemmas generated by partial @[step] lemmas. It uses Lean's simp now.

Moreover, this PR puts the new spec_partial predicate to use: It converts the old spec lemmas into spec_partial lemmas for arithmetic and array/slice operations.

Claude generated most of this PR, but it has been carefully reviewed and hand-edited.

@abentkamp abentkamp requested a review from clementblaudeau May 28, 2026 13:48
@abentkamp abentkamp changed the base branch from spec_partial to dev June 2, 2026 11:18

@clementblaudeau clementblaudeau left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of style questions that have several occurences: should we replace

(fun e => e = .arrayOutOfBounds ∧ i.val ≥ v.length)

with

(fun | .arrayOutOfBounds => i.val ≥ v.length | _ => False)

?

theorem Array.index_usize_spec {α : Type u} {n : Usize} (v: Array α n) (i: Usize) :
spec_partial (v.index_usize i)
(fun x => ∃ _ : i.val < v.length, x = v.val[i.val])
(fun e => e = .arrayOutOfBounds ∧ i.val ≥ v.length)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(fun e => e = .arrayOutOfBounds i.val ≥ v.length)
(fun | .arrayOutOfBounds => i.val ≥ v.length | _ => False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about direct matching there ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are three other occurences

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The e = .arrayOutOfBounds ∧ -form is what Son already agreed to, so that's why I'd like to leave it that way.
See: #Cryspen > mvcgen specs

The match-syntax looks nicer, but is also slightly harder to deal with when generating the spec-lemmas. Still, if Son agrees, I'd be happy to switch to matching.

(fun s =>
s.val = a.val.slice r.start.val r.end.val ∧
(∀ i, i + r.start.val < r.end.val → s.val[i]! = a.val[r.start.val + i]!))
(fun e => e = .panic ∧ ¬ (r.start.val < r.end.val ∧ r.end.val ≤ a.val.length))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be panic or Array out of bounds here ? And same style question as above

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further up in that file, subslice is defined to throw .panic, so unless we change that definition, we'll have to use .panic here as well. Is there a clear distinction in Rust between out-of-bounds and other panics? How do we make these distinctions?

(∀ i, i < r.start.val → na[i]! = a[i]!) ∧
(∀ i, r.start.val ≤ i → i < r.end.val → na[i]! = s[i - r.start.val]!) ∧
(∀ i, r.end.val ≤ i → i < n.val → na[i]! = a[i]!))
(fun e => e = .panic ∧

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question

(fun (s : Slice T) =>
s.val = a.val.slice r.start r.end ∧
s.length = r.end.val - r.start.val)
(fun e => e = .panic ∧ ¬ (r.start ≤ r.end ∧ r.end ≤ N))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same. There are more below

z.val = x.val + y.val ∧
z.bv = x.bv + y.bv
| fail _ => ¬ (UScalar.inBounds ty (x.val + y.val))
| fail e => e = .integerOverflow ∧ ¬ (UScalar.inBounds ty (x.val + y.val))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| fail e => e = .integerOverflow ¬ (UScalar.inBounds ty (x.val + y.val))
| fail .integerOverflow => ¬ (UScalar.inBounds ty (x.val + y.val))

z.val = x.val + y.val ∧
z.bv = x.bv + y.bv
| fail _ => ¬ (IScalar.inBounds ty (x.val + y.val))
| fail e => e = .integerOverflow ∧ ¬ (IScalar.inBounds ty (x.val + y.val))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

(∃ y, m = ok y ∧ P y) → spec m P := by
exact (spec_equiv_exists m P).2

/-- Partial-correctness variant of `spec`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the base branch be spec_partial instead of dev ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no! The spec_partial-PR has already been merged, but it had the wrong target branch when I merged it. So it's all a mess now. Maybe we can just merge everything with this PR?

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.

3 participants