Skip to content

emit: let an inline-Pulse postcondition determine the result - #247

Draft
nikswamy wants to merge 3 commits into
nswamy/pal-pr26-rewrites-tofrom
nswamy/pal-pr33-inline-pulse-ensures
Draft

emit: let an inline-Pulse postcondition determine the result#247
nikswamy wants to merge 3 commits into
nswamy/pal-pr26-rewrites-tofrom
nswamy/pal-pr33-inline-pulse-ensures

Conversation

@nikswamy

Copy link
Copy Markdown
Contributor

Part of the upstreaming of nswamy/pal-c-project-integration (PR33 of 35 PRs) — see PR_PLAN.md on that branch for the whole plan and the dependency graph.

Base: nswamy/pal-pr26-rewrites-to — a stacked PR. It depends on nswamy/pal-pr26-rewrites-to, nswamy/pal-pr06-pure-external-decls, so only the commits listed below are its own; it will be retargeted at main once its parents land.

It has more than one parent, so the diff against nswamy/pal-pr26-rewrites-to also carries the commits from nswamy/pal-pr06-pure-external-decls until those land.

Extends PR26's "does E mention the result" test to look inside an inline-Pulse fragment's
antiquotations, which is both sufficient and precise — that is the only way an inline
fragment can reach the result. It is also the only way to say "this function decides that
predicate", which the impure-spec elaborator needs a rewrites_to for. The fixture calls a
_pure external declaration, hence PR06.

Commits

  • emit: let an inline-Pulse postcondition determine the result

Testing

Verified: make rust lib, test/check-template.sh, cargo fmt --check, clang-format --dry-run --Werror, and F* verification of test/compound_literal_member, test/ensures_determines_result, test/nullable_out, test/nullable_out_struct, test/pure_external_contract, test/rvalue_member, test/rvalue_member_branch.

nikswamy and others added 3 commits August 18, 2026 15:26
A `_pure` function with a body is emitted as an F* `let`, but a `_pure`
function without one fell through to the ordinary external-declaration path
and became a state-passing `fn` with an unreachable body. That made external
pure operations unusable in specifications: Pulse rejects a stateful
application in a pre- or postcondition with "cannot use ... in impure spec".

Emit them as `assume val` instead, sharing the signature elaboration with the
defined case so both forms agree on parameters, the `Pure` effect wrapper, and
contract placement. Call sites already emit a plain application, so nothing
about calling such a function changes.

This is what lets a checked-arithmetic routine be given the contract its
header documents. Its result is only meaningful when its status says it did
not overflow, and that status is classified by another external pure
operation, so the postcondition has to name one external pure function to say
anything about the result of another.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 981f7d3c-6a91-47ad-84d7-7aadf747123d
(cherry picked from commit cddb926)
`_ensures(return == E)` is emitted as Pulse's `rewrites_to` when `E` does not
mention the result, which is what lets the prover substitute for the result
rather than carry an equation about it. The test for "does `E` mention the
result" answered conservatively -- `true` -- for any expression form it did not
recognise, and an inline-Pulse fragment was one of those. So a clause of the
shape

    _ensures(return == (_Bool)_inline_pulse(f $(A) $(B)))

was emitted as a plain equality instead.

That is the only way to say "this function decides that predicate", and it has
a consequence beyond the prover's convenience: the impure-spec elaborator,
which is what turns an assertion over a stateful call into a proposition, looks
for exactly a `rewrites_to` in the callee's postcondition. Without one it has
no term to name the result by, and an `_assert` over such a call fails to
elaborate with "cannot find rewrites_to in post".

An inline-Pulse fragment can only reach the result through an antiquotation, so
looking inside its antiquotations is both sufficient and precise; the verbatim
tokens around them cannot reintroduce it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 981f7d3c-6a91-47ad-84d7-7aadf747123d
(cherry picked from commit 7ddb7ed)
@gebner

gebner commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

!diff

@github-actions

Copy link
Copy Markdown

Generated F* output diff

Effect of this pull request on the F* code pal generates for the test suite (1a8c0f79b7e5f6).

Summary
 {base => head}/c_assert/Func_pal_c_assert_enabled.fst                 |  4 +---
 {base => head}/containing_record/Func_pal_c_assert_enabled.fst        |  4 +---
 {base => head}/dpe/Func_compare.fst                                   |  2 +-
 {base => head}/dpe/Func_compare.fsti                                  |  2 +-
 /dev/null => head/ensures_determines_result/Func_agree.fst            | 13 +++++++++++++
 /dev/null => head/ensures_determines_result/Func_agrees.fst           |  7 +++++++
 /dev/null => head/ensures_determines_result/Func_checked_combine.fst  | 21 +++++++++++++++++++++
 /dev/null => head/ensures_determines_result/Func_checked_combine.fsti | 15 +++++++++++++++
 /dev/null => head/ensures_determines_result/Func_combine.fst          | 16 ++++++++++++++++
 /dev/null => head/ensures_determines_result/Func_screen.fst           | 20 ++++++++++++++++++++
 /dev/null => head/ensures_determines_result/Func_screen.fsti          | 12 ++++++++++++
 /dev/null => head/ensures_determines_result/TranslationErrors.fst     |  1 +
 /dev/null => head/ensures_determines_result/Typedef___uint32_t.fst    | 13 +++++++++++++
 /dev/null => head/ensures_determines_result/Typedef_uint32_t.fst      | 13 +++++++++++++
 /dev/null => head/ensures_determines_result/diagnostics.json          |  1 +
 {base => head}/if_ensures/Func_pal_c_assert_enabled.fst               |  4 +---
 /dev/null => head/pure_external_contract/Func_checked_add.fst         | 25 +++++++++++++++++++++++++
 /dev/null => head/pure_external_contract/Func_failed.fst              |  6 ++++++
 /dev/null => head/pure_external_contract/Func_saturating_add.fst      | 27 +++++++++++++++++++++++++++
 /dev/null => head/pure_external_contract/Func_saturating_add.fsti     | 15 +++++++++++++++
 /dev/null => head/pure_external_contract/TranslationErrors.fst        |  1 +
 /dev/null => head/pure_external_contract/Typedef___uint32_t.fst       | 13 +++++++++++++
 /dev/null => head/pure_external_contract/Typedef_uint32_t.fst         | 13 +++++++++++++
 /dev/null => head/pure_external_contract/diagnostics.json             |  1 +
 24 files changed, 238 insertions(+), 11 deletions(-)

Full diff: full diff artifact

Diff
diff --git base/c_assert/Func_pal_c_assert_enabled.fst head/c_assert/Func_pal_c_assert_enabled.fst
index ad5d080..218c7b8 100644
--- base/c_assert/Func_pal_c_assert_enabled.fst
+++ head/c_assert/Func_pal_c_assert_enabled.fst
@@ -3,6 +3,4 @@ open Pulse
 open Pulse.Lib.C
 #lang-pulse
 
-divergent fn func_pal_c_assert_enabled ()
-  returns return_1 : bool
-{ assume pure False; unreachable () }
\ No newline at end of file
+assume val func_pal_c_assert_enabled () : bool
\ No newline at end of file
diff --git base/containing_record/Func_pal_c_assert_enabled.fst head/containing_record/Func_pal_c_assert_enabled.fst
index ad5d080..218c7b8 100644
--- base/containing_record/Func_pal_c_assert_enabled.fst
+++ head/containing_record/Func_pal_c_assert_enabled.fst
@@ -3,6 +3,4 @@ open Pulse
 open Pulse.Lib.C
 #lang-pulse
 
-divergent fn func_pal_c_assert_enabled ()
-  returns return_1 : bool
-{ assume pure False; unreachable () }
\ No newline at end of file
+assume val func_pal_c_assert_enabled () : bool
\ No newline at end of file
diff --git base/dpe/Func_compare.fst head/dpe/Func_compare.fst
index acf7f02..29abe4a 100644
--- base/dpe/Func_compare.fst
+++ head/dpe/Func_compare.fst
@@ -12,7 +12,7 @@ divergent fn func_compare
   preserves (array_pts_to_full var_a1 'p_a1_0 'val_a1_0)
   preserves (array_pts_to_full var_a2 'p_a2_0 'val_a2_0)
   returns return_1 : bool
-  ensures (with_pure (return_1 = (Seq.equal (array_value_of var_a1) (array_value_of var_a2))))
+  ensures (rewrites_to return_1 ((Seq.equal (array_value_of var_a1) (array_value_of var_a2))))
 {
   let mut var_len = var_len;
   let mut var_a1 = var_a1;
diff --git base/dpe/Func_compare.fsti head/dpe/Func_compare.fsti
index a15560d..8db351a 100644
--- base/dpe/Func_compare.fsti
+++ head/dpe/Func_compare.fsti
@@ -12,4 +12,4 @@ requires (with_pure ((reveal (length_of var_a2)) = (SizeT.v var_len)))
 preserves (array_pts_to_full var_a1 'p_a1_0 'val_a1_0)
 preserves (array_pts_to_full var_a2 'p_a2_0 'val_a2_0)
 returns return_1 : bool
-ensures (with_pure (return_1 = (Seq.equal (array_value_of var_a1) (array_value_of var_a2))))
\ No newline at end of file
+ensures (rewrites_to return_1 ((Seq.equal (array_value_of var_a1) (array_value_of var_a2))))
\ No newline at end of file
diff --git head/ensures_determines_result/Func_agree.fst head/ensures_determines_result/Func_agree.fst
new file mode 100644
index 0000000..7f905de
--- /dev/null
+++ head/ensures_determines_result/Func_agree.fst
@@ -0,0 +1,13 @@
+module Func_agree
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_agree (var_a: Typedef_uint32_t.ty_uint32_t) (var_b: Typedef_uint32_t.ty_uint32_t)
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  returns return_1 : bool
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  ensures (rewrites_to return_1 ((Func_agrees.func_agrees var_a var_b)))
+{ assume pure False; unreachable () }
\ No newline at end of file
diff --git head/ensures_determines_result/Func_agrees.fst head/ensures_determines_result/Func_agrees.fst
new file mode 100644
index 0000000..613b059
--- /dev/null
+++ head/ensures_determines_result/Func_agrees.fst
@@ -0,0 +1,7 @@
+module Func_agrees
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+assume val func_agrees (var_a: Typedef_uint32_t.ty_uint32_t) (var_b: Typedef_uint32_t.ty_uint32_t) :
+  bool
\ No newline at end of file
diff --git head/ensures_determines_result/Func_checked_combine.fst head/ensures_determines_result/Func_checked_combine.fst
new file mode 100644
index 0000000..0238ba3
--- /dev/null
+++ head/ensures_determines_result/Func_checked_combine.fst
@@ -0,0 +1,21 @@
+module Func_checked_combine
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_checked_combine
+    (var_a: Typedef_uint32_t.ty_uint32_t)
+    (var_b: Typedef_uint32_t.ty_uint32_t)
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  requires (with_pure (Func_agrees.func_agrees var_a var_b))
+  returns return_1 : Typedef_uint32_t.ty_uint32_t
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
+{
+  let mut var_a = var_a;
+  let mut var_b = var_b;
+  assert (with_pure (Func_agree.func_agree (!var_a) (!var_b)));
+  return (Func_combine.func_combine (!var_a) (!var_b));
+}
\ No newline at end of file
diff --git head/ensures_determines_result/Func_checked_combine.fsti head/ensures_determines_result/Func_checked_combine.fsti
new file mode 100644
index 0000000..7d11f38
--- /dev/null
+++ head/ensures_determines_result/Func_checked_combine.fsti
@@ -0,0 +1,15 @@
+module Func_checked_combine
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_checked_combine
+  (var_a: Typedef_uint32_t.ty_uint32_t)
+  (var_b: Typedef_uint32_t.ty_uint32_t)
+requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+requires (with_pure (Func_agrees.func_agrees var_a var_b))
+returns return_1 : Typedef_uint32_t.ty_uint32_t
+ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
\ No newline at end of file
diff --git head/ensures_determines_result/Func_combine.fst head/ensures_determines_result/Func_combine.fst
new file mode 100644
index 0000000..5b3a8e3
--- /dev/null
+++ head/ensures_determines_result/Func_combine.fst
@@ -0,0 +1,16 @@
+module Func_combine
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_combine
+    (var_a: Typedef_uint32_t.ty_uint32_t)
+    (var_b: Typedef_uint32_t.ty_uint32_t)
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  requires (with_pure (Func_agrees.func_agrees var_a var_b))
+  returns return_1 : Typedef_uint32_t.ty_uint32_t
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
+{ assume pure False; unreachable () }
\ No newline at end of file
diff --git head/ensures_determines_result/Func_screen.fst head/ensures_determines_result/Func_screen.fst
new file mode 100644
index 0000000..ff0ed68
--- /dev/null
+++ head/ensures_determines_result/Func_screen.fst
@@ -0,0 +1,20 @@
+module Func_screen
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_screen (var_a: Typedef_uint32_t.ty_uint32_t) (var_b: Typedef_uint32_t.ty_uint32_t)
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  returns return_1 : Typedef_uint32_t.ty_uint32_t
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
+{
+  let mut var_a = var_a;
+  let mut var_b = var_b;
+  if ((not (Func_agree.func_agree (!var_a) (!var_b)))) {
+    return (id #UInt32.t (Int.Cast.int32_to_uint32 0l));
+  } else {};
+  return (Func_checked_combine.func_checked_combine (!var_a) (!var_b));
+}
\ No newline at end of file
diff --git head/ensures_determines_result/Func_screen.fsti head/ensures_determines_result/Func_screen.fsti
new file mode 100644
index 0000000..2eacc64
--- /dev/null
+++ head/ensures_determines_result/Func_screen.fsti
@@ -0,0 +1,12 @@
+module Func_screen
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_screen (var_a: Typedef_uint32_t.ty_uint32_t) (var_b: Typedef_uint32_t.ty_uint32_t)
+requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+returns return_1 : Typedef_uint32_t.ty_uint32_t
+ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
\ No newline at end of file
diff --git head/ensures_determines_result/TranslationErrors.fst head/ensures_determines_result/TranslationErrors.fst
new file mode 100644
index 0000000..bf62561
--- /dev/null
+++ head/ensures_determines_result/TranslationErrors.fst
@@ -0,0 +1 @@
+module TranslationErrors
diff --git head/ensures_determines_result/Typedef___uint32_t.fst head/ensures_determines_result/Typedef___uint32_t.fst
new file mode 100644
index 0000000..b9a5b44
--- /dev/null
+++ head/ensures_determines_result/Typedef___uint32_t.fst
@@ -0,0 +1,13 @@
+module Typedef___uint32_t
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+unfold
+let ty___uint32_t : Type = UInt32.t
+[@@pulse_eager_unfold] let predicate ty___uint32_t__pred ([@@@mkey] this: ty___uint32_t) (p: perm) =
+  emp
+[@@pulse_eager_unfold] let predicate ty___uint32_t__uninit_pred ([@@@mkey] this: ty___uint32_t) =
+  emp
+instance has_zero_default_ty___uint32_t : (has_zero_default ty___uint32_t) =
+  { zero_default = (UInt32.uint_to_t 0) }
\ No newline at end of file
diff --git head/ensures_determines_result/Typedef_uint32_t.fst head/ensures_determines_result/Typedef_uint32_t.fst
new file mode 100644
index 0000000..aa0f488
--- /dev/null
+++ head/ensures_determines_result/Typedef_uint32_t.fst
@@ -0,0 +1,13 @@
+module Typedef_uint32_t
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+unfold
+let ty_uint32_t : Type = Typedef___uint32_t.ty___uint32_t
+[@@pulse_eager_unfold] let predicate ty_uint32_t__pred ([@@@mkey] this: ty_uint32_t) (p: perm) =
+  ((Typedef___uint32_t.ty___uint32_t__pred this p))
+[@@pulse_eager_unfold] let predicate ty_uint32_t__uninit_pred ([@@@mkey] this: ty_uint32_t) =
+  ((Typedef___uint32_t.ty___uint32_t__uninit_pred this))
+instance has_zero_default_ty_uint32_t : (has_zero_default ty_uint32_t) =
+  { zero_default = zero_default }
\ No newline at end of file
diff --git head/ensures_determines_result/diagnostics.json head/ensures_determines_result/diagnostics.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ head/ensures_determines_result/diagnostics.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git base/if_ensures/Func_pal_c_assert_enabled.fst head/if_ensures/Func_pal_c_assert_enabled.fst
index ad5d080..218c7b8 100644
--- base/if_ensures/Func_pal_c_assert_enabled.fst
+++ head/if_ensures/Func_pal_c_assert_enabled.fst
@@ -3,6 +3,4 @@ open Pulse
 open Pulse.Lib.C
 #lang-pulse
 
-divergent fn func_pal_c_assert_enabled ()
-  returns return_1 : bool
-{ assume pure False; unreachable () }
\ No newline at end of file
+assume val func_pal_c_assert_enabled () : bool
\ No newline at end of file
diff --git head/pure_external_contract/Func_checked_add.fst head/pure_external_contract/Func_checked_add.fst
new file mode 100644
index 0000000..d44fbda
--- /dev/null
+++ head/pure_external_contract/Func_checked_add.fst
@@ -0,0 +1,25 @@
+module Func_checked_add
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_checked_add
+    (var_a: Typedef_uint32_t.ty_uint32_t)
+    (var_b: Typedef_uint32_t.ty_uint32_t)
+    (var_result: (ref Typedef_uint32_t.ty_uint32_t))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  requires ((Pulse.Lib.Reference.pts_to_uninit var_result))
+  returns return_1 : Int32.t
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_a 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_b 1.0R))
+  ensures
+    exists* (val_result_0: Typedef_uint32_t.ty_uint32_t).
+    ((Pulse.Lib.Reference.pts_to var_result #1.0R val_result_0) **
+      (Typedef_uint32_t.ty_uint32_t__pred (!var_result) 1.0R))
+  ensures
+    (with_pure
+      ((Func_failed.func_failed return_1) ||
+        (((!var_result) = (var_a `Pulse.Lib.C.UInt32.add_wrap` var_b)) &&
+          (var_a `UInt32.lte` (!var_result)))))
+{ assume pure False; unreachable () }
\ No newline at end of file
diff --git head/pure_external_contract/Func_failed.fst head/pure_external_contract/Func_failed.fst
new file mode 100644
index 0000000..9a8eaf4
--- /dev/null
+++ head/pure_external_contract/Func_failed.fst
@@ -0,0 +1,6 @@
+module Func_failed
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+assume val func_failed (var_status: Int32.t) : bool
\ No newline at end of file
diff --git head/pure_external_contract/Func_saturating_add.fst head/pure_external_contract/Func_saturating_add.fst
new file mode 100644
index 0000000..01160fb
--- /dev/null
+++ head/pure_external_contract/Func_saturating_add.fst
@@ -0,0 +1,27 @@
+module Func_saturating_add
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_saturating_add
+    (var_x: Typedef_uint32_t.ty_uint32_t)
+    (var_y: Typedef_uint32_t.ty_uint32_t)
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_x 1.0R))
+  requires ((Typedef_uint32_t.ty_uint32_t__pred var_y 1.0R))
+  returns return_1 : Typedef_uint32_t.ty_uint32_t
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_x 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred var_y 1.0R))
+  ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
+  ensures (pure (FStar.UInt32.v return_1 <= FStar.UInt32.v var_x + FStar.UInt32.v var_y))
+{
+  let mut var_x = var_x;
+  let mut var_y = var_y;
+  let mut var_sum : Typedef_uint32_t.ty_uint32_t;
+  var_sum := (id #UInt32.t (Int.Cast.int32_to_uint32 0l));
+  let mut var_status_1 : Int32.t;
+  var_status_1 := (Func_checked_add.func_checked_add (!var_x) (!var_y) var_sum);
+  if ((Func_failed.func_failed (!var_status_1))) {
+    var_sum := (id #UInt32.t (Int.Cast.int32_to_uint32 0l));
+  } else {};
+  return (!var_sum);
+}
\ No newline at end of file
diff --git head/pure_external_contract/Func_saturating_add.fsti head/pure_external_contract/Func_saturating_add.fsti
new file mode 100644
index 0000000..93c8b38
--- /dev/null
+++ head/pure_external_contract/Func_saturating_add.fsti
@@ -0,0 +1,15 @@
+module Func_saturating_add
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+divergent fn func_saturating_add
+  (var_x: Typedef_uint32_t.ty_uint32_t)
+  (var_y: Typedef_uint32_t.ty_uint32_t)
+requires ((Typedef_uint32_t.ty_uint32_t__pred var_x 1.0R))
+requires ((Typedef_uint32_t.ty_uint32_t__pred var_y 1.0R))
+returns return_1 : Typedef_uint32_t.ty_uint32_t
+ensures ((Typedef_uint32_t.ty_uint32_t__pred var_x 1.0R))
+ensures ((Typedef_uint32_t.ty_uint32_t__pred var_y 1.0R))
+ensures ((Typedef_uint32_t.ty_uint32_t__pred return_1 1.0R))
+ensures (pure (FStar.UInt32.v return_1 <= FStar.UInt32.v var_x + FStar.UInt32.v var_y))
\ No newline at end of file
diff --git head/pure_external_contract/TranslationErrors.fst head/pure_external_contract/TranslationErrors.fst
new file mode 100644
index 0000000..bf62561
--- /dev/null
+++ head/pure_external_contract/TranslationErrors.fst
@@ -0,0 +1 @@
+module TranslationErrors
diff --git head/pure_external_contract/Typedef___uint32_t.fst head/pure_external_contract/Typedef___uint32_t.fst
new file mode 100644
index 0000000..b9a5b44
--- /dev/null
+++ head/pure_external_contract/Typedef___uint32_t.fst
@@ -0,0 +1,13 @@
+module Typedef___uint32_t
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+unfold
+let ty___uint32_t : Type = UInt32.t
+[@@pulse_eager_unfold] let predicate ty___uint32_t__pred ([@@@mkey] this: ty___uint32_t) (p: perm) =
+  emp
+[@@pulse_eager_unfold] let predicate ty___uint32_t__uninit_pred ([@@@mkey] this: ty___uint32_t) =
+  emp
+instance has_zero_default_ty___uint32_t : (has_zero_default ty___uint32_t) =
+  { zero_default = (UInt32.uint_to_t 0) }
\ No newline at end of file
diff --git head/pure_external_contract/Typedef_uint32_t.fst head/pure_external_contract/Typedef_uint32_t.fst
new file mode 100644
index 0000000..aa0f488
--- /dev/null
+++ head/pure_external_contract/Typedef_uint32_t.fst
@@ -0,0 +1,13 @@
+module Typedef_uint32_t
+open Pulse
+open Pulse.Lib.C
+#lang-pulse
+
+unfold
+let ty_uint32_t : Type = Typedef___uint32_t.ty___uint32_t
+[@@pulse_eager_unfold] let predicate ty_uint32_t__pred ([@@@mkey] this: ty_uint32_t) (p: perm) =
+  ((Typedef___uint32_t.ty___uint32_t__pred this p))
+[@@pulse_eager_unfold] let predicate ty_uint32_t__uninit_pred ([@@@mkey] this: ty_uint32_t) =
+  ((Typedef___uint32_t.ty___uint32_t__uninit_pred this))
+instance has_zero_default_ty_uint32_t : (has_zero_default ty_uint32_t) =
+  { zero_default = zero_default }
\ No newline at end of file
diff --git head/pure_external_contract/diagnostics.json head/pure_external_contract/diagnostics.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ head/pure_external_contract/diagnostics.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file

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