Skip to content

Commit 43af7f6

Browse files
committed
Check native total and partial Aeneas specifications
Keep independent native examples for successful returns, named assumptions and tuple results. Total contracts reject panic and divergence; partial contracts admit divergence while rejecting panic and incorrect successful returns. Validation: fresh pinned extraction and complete-model golden comparison; independent golden/live Lean builds, required-contract checks, and the transitive axiom audit. gherrit-pr-id: Gmokoduvrhwmesqj7moerqxqv6jjz63e5 Agent-authored-by: AI agent acting on joshlf's behalf
1 parent f72102d commit 43af7f6

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/- Copyright 2026 The Fuchsia Authors
2+
3+
Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4+
<LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5+
license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6+
This file may not be copied, modified, or distributed except according to
7+
those terms. -/
8+
9+
module
10+
public import ModelPrelude
11+
@[expose] public section
12+
13+
/-!
14+
These kernel-checked examples explain Aeneas's native total and partial
15+
contracts independently of our spec macro. They pin successful results, tuple
16+
postconditions, panic rejection, and divergence behavior. The independently
17+
written theorem types prevent a change in syntax from silently changing the
18+
claim. Total contracts require successful execution even for a True
19+
postcondition. Partial contracts may diverge, but still reject panic.
20+
-/
21+
open Aeneas Aeneas.Std AeneasSpecs
22+
namespace ContractTests
23+
24+
-- Omitted requirements mean True; a successful result is still required.
25+
theorem identity_spec (x : Nat) :
26+
Result.ok x
27+
⦃ ret => ret = x ⦄ := by
28+
exact WP.spec.ret rfl
29+
30+
-- Named requirements are available without introductions in the proof body.
31+
theorem transitive_spec (x y z : Nat)
32+
(hxy : x = y)
33+
(hyz : y = z) :
34+
Result.ok x
35+
⦃ ret => ret = z ⦄ := by
36+
exact WP.spec.ret (hxy.trans hyz)
37+
38+
-- Reuse Aeneas's tuple-aware binders for returns and mutable post-states.
39+
theorem tuple_spec (x : Nat) :
40+
Result.ok (x, x + 1)
41+
⦃ ret x' => ret = x ∧ x' = x + 1 ⦄ := by
42+
simp
43+
44+
theorem diverging_spec :
45+
(Result.div : Result Nat)
46+
⦃ _ => False ⦄div := by
47+
exact WP.dspec.div _
48+
49+
theorem partial_identity_spec (x : Nat) :
50+
Result.ok x
51+
⦃ ret => ret = x ⦄div := by
52+
exact WP.dspec.ret rfl
53+
54+
-- These independently written types pin the exported theorem types and outcomes.
55+
example : ∀ x : Nat, WP.spec (Result.ok x) (fun ret => ret = x) := identity_spec
56+
example : ∀ x y z : Nat, x = y → y = z →
57+
WP.spec (Result.ok x) (fun ret => ret = z) := transitive_spec
58+
example : ∀ x : Nat, WP.spec (Result.ok (x, x + 1))
59+
(fun (ret, x') => ret = x ∧ x' = x + 1) := tuple_spec
60+
example : WP.dspec (Result.div : Result Nat) (fun _ => False) := diverging_spec
61+
example : ∀ x : Nat, WP.dspec (Result.ok x) (fun ret => ret = x) := partial_identity_spec
62+
63+
theorem panic_rejected :
64+
¬ WP.spec (Result.fail Error.panic : Result Nat) (fun _ => True) := by simp
65+
66+
theorem divergence_rejected :
67+
¬ WP.spec (Result.div : Result Nat) (fun _ => True) := by simp
68+
69+
-- Partial correctness must reject panic and incorrect successful returns too.
70+
theorem partial_panic_rejected :
71+
¬ WP.dspec (Result.fail Error.panic : Result Nat) (fun _ => True) := by simp
72+
73+
theorem partial_wrong_return_rejected :
74+
¬ WP.dspec (Result.ok 0) (fun ret : Nat => ret = 1) := by simp
75+
76+
-- A generic identity shares one explicit dictionary and the same witness on
77+
-- input and output, even when the dictionary rejects some representations.
78+
theorem modeled_identity {Raw : Type} (provider : RustModel Raw) (raw : Raw)
79+
(value : provider.Model) (h : provider.decode raw = some value) :
80+
WP.spec (Result.ok raw) (fun out =>
81+
∃ decoded, provider.decode out = some decoded ∧ decoded = value) :=
82+
WP.spec.ret ⟨value, h, rfl⟩
83+
84+
-- A dependent ghost retains its exact domain in mathematical input scope.
85+
theorem modeled_position {Raw : Type} (provider : RustModel Raw)
86+
(length : provider.Model → Nat) (raw : Raw) (value : provider.Model)
87+
(h : provider.decode raw = some value) (position : Fin (length value)) :
88+
WP.spec (Result.ok raw) (fun out =>
89+
∃ decoded, provider.decode out = some decoded ∧ position.val < length decoded) :=
90+
WP.spec.ret ⟨value, h, position.isLt⟩
91+
92+
-- An accepted execution cannot return a decoder-rejected representation, even
93+
-- when its authored postcondition contributes no information.
94+
theorem rejected_output_rejected {Raw : Type} (provider : RustModel Raw)
95+
(raw : Raw) (h : provider.decode raw = none) :
96+
¬ WP.spec (Result.ok raw) (fun out =>
97+
∃ decoded, provider.decode out = some decoded ∧ True) := by
98+
simp [WP.spec_ok, h]
99+
100+
theorem partial_rejected_output_rejected {Raw : Type} (provider : RustModel Raw)
101+
(raw : Raw) (h : provider.decode raw = none) :
102+
¬ WP.dspec (Result.ok raw) (fun out =>
103+
∃ decoded, provider.decode out = some decoded ∧ True) := by
104+
simp [h]
105+
106+
-- Rust absence and Rust errors remain successful decoded constructor cases.
107+
example : (@modelOption Usize (modelUScalar .Usize)).decode none = some none := rfl
108+
example : (@modelRustResult Usize Unit (modelUScalar .Usize) modelUnit).decode
109+
(.Err ()) = some (.Err ()) := rfl
110+
111+
end ContractTests

0 commit comments

Comments
 (0)