diff --git a/output/ctime-repair/memory.md b/output/ctime-repair/memory.md new file mode 100644 index 0000000..f4b1d66 --- /dev/null +++ b/output/ctime-repair/memory.md @@ -0,0 +1,17 @@ +# ctime-repair + +- Active proof target: `rocq-brick-libstdcpp/test/ctime/proof.v` +- Current reproduced failure from `dune build ./test/ctime/proof.vo`: + - non-fatal missing-spec diagnostics for POD repro lemmas via `verify?` + - earlier fatal proof failure at `test_mktime_ptr_ok` +- Live replay in `dune rocq top test/ctime/proof.v` found the wrapper-spec issue: + - using `\pre{q tm_in}` was still too strong and left a half-fraction ownership mismatch + - the correct wrapper statement uses `\prepost{q tm_in} tm_p |-> tmR q tm_in` + and makes the explicit post ask only for the pure `mktime_result` witness plus + `tm_p |-> tmR (cQp.scale (1 / 2) q) tm_out` + - under that statement, `verify_spec; go.` leaves only + `∃ t0 : time_t_model, [| mktime_result tm_in t t0 |]` + - `iExists _. go.` closes the proof +- Current repair choice: + - keep the `test_mktime_ptr_ok` proof as + `verify_spec. go. iExists _. go. Qed.` diff --git a/rocq-brick-libstdcpp/output/ctime-pod-dtor-repro/memory.md b/rocq-brick-libstdcpp/output/ctime-pod-dtor-repro/memory.md new file mode 100644 index 0000000..350b1ad --- /dev/null +++ b/rocq-brick-libstdcpp/output/ctime-pod-dtor-repro/memory.md @@ -0,0 +1 @@ +- Restoring stack-local tm/timespec validation clients to reproduce synthetic destructor proof failures. diff --git a/rocq-brick-libstdcpp/proof/ctime/design.md b/rocq-brick-libstdcpp/proof/ctime/design.md new file mode 100644 index 0000000..f20faa2 --- /dev/null +++ b/rocq-brick-libstdcpp/proof/ctime/design.md @@ -0,0 +1,33 @@ +# `` BRiCk Spec Design + +## 1. Summary + +1. Add a new `proof/ctime` folder and a companion `test/ctime` folder. +2. Cover the standard `` surface from cppreference and C11 7.27, with `difftime` deferred because BRiCk does not currently support doubles. +3. Use an abstract-but-accurate design: explicit ownership for writable outputs, borrowed resources for static return objects, and abstract model relations for time conversion and formatting. + +## 2. Public API and Representation Changes + +1. The proof folder contains `inc_ctime.cpp`, `pred.v`, `model.v`, and `spec.v`. +2. Specs target the unqualified names emitted by `cpp2v`: `clock`, `time`, `mktime`, `gmtime`, `localtime`, `asctime`, `ctime`, `strftime`, and `timespec_get`. +3. V1 remains standard-only and excludes glibc and POSIX extensions visible in the generated AST. +4. The model layer exports `tm_model` and `timespec_model`. +5. `tmR` hides non-standard `tm_gmtoff` and `tm_zone` fields behind `tmR_hidden`, while `timespecR` tracks only `tv_sec` and `tv_nsec`. + +## 3. Specification Design + +1. `clock` and `time` are modeled as abstract current-time queries over signed integer results. +2. `timespec_get` is only specified for `TIME_UTC = 1`. +3. `mktime`, `gmtime`, and `localtime` use abstract conversion and normalization relations instead of a concrete calendar algorithm. +4. `asctime` and `ctime` return borrowed C strings with explicit close obligations, matching the existing static-storage style used elsewhere in the repo. +5. `strftime` writes into a caller-owned `cstring.bufR` buffer and returns either `0` or the produced string length. + +## 4. Validation Plan + +1. `test/ctime/test.cpp` exercises `std::time`, `std::timespec_get`, `std::mktime`, `std::gmtime`, `std::localtime`, `std::asctime`, `std::ctime`, `std::strftime`, and repeated static-return calls. +2. `test/ctime/proof.v` proves representative client lemmas against the new specs. +3. The test clients use `std::`-qualified calls so the proof confirms those names resolve against the unqualified spec entries produced by `cpp2v`. + +## 5. Known Deviation + +1. `difftime` is intentionally deferred until BRiCk has a supported story for `double` values in specs and proofs. diff --git a/rocq-brick-libstdcpp/proof/ctime/inc_ctime.cpp b/rocq-brick-libstdcpp/proof/ctime/inc_ctime.cpp new file mode 100644 index 0000000..509470b --- /dev/null +++ b/rocq-brick-libstdcpp/proof/ctime/inc_ctime.cpp @@ -0,0 +1 @@ +#include diff --git a/rocq-brick-libstdcpp/proof/ctime/model.v b/rocq-brick-libstdcpp/proof/ctime/model.v new file mode 100644 index 0000000..4c0f195 --- /dev/null +++ b/rocq-brick-libstdcpp/proof/ctime/model.v @@ -0,0 +1,64 @@ +(* + * Copyright (c) 2025 SkyLabs AI, Inc. + * This software is distributed under the terms of the BedRock Open-Source License. + * See the LICENSE-BedRock file in the repository root for details. + *) +Require Import skylabs.auto.cpp.prelude.spec. +Require Export skylabs.cpp.string. + +#[local] Set Primitive Projections. +#[local] Open Scope Z_scope. + +Record tm_t := { + tm_model_sec : Z; + tm_model_min : Z; + tm_model_hour : Z; + tm_model_mday : Z; + tm_model_mon : Z; + tm_model_year : Z; + tm_model_wday : Z; + tm_model_yday : Z; + tm_model_isdst : Z; +}. + +Record timespec_t := { + timespec_model_sec : Z; + timespec_model_nsec : Z; +}. + +Definition TIME_UTC : Z := 1. + +Definition clock_t := Z. +Definition time_t := Z. + +Parameter abs_time_t : Type. +Parameter abs_time_of_N : N -> abs_time_t. +Parameter abs_time_diff_t : Type. + +Definition timespec_wf (ts : timespec_t) : Prop := + 0 <= timespec_model_nsec ts < 1000000000. + +Parameter time_t_to_abs_time : time_t -> abs_time_t -> Prop. +Parameter timespec_to_abs_time : timespec_t -> abs_time_t -> Prop. +Parameter clock_t_to_abs_time_diff : clock_t -> abs_time_diff_t -> Prop. +Parameter abs_time_plus_diff : abs_time_t -> abs_time_diff_t -> abs_time_t -> Prop. + +Parameter utc_time_to_tm : time_t -> tm_t -> Prop. +Parameter local_time_to_tm : time_t -> tm_t -> Prop. +Parameter mktime_result : tm_t -> tm_t -> time_t -> Prop. +Parameter asctime_text_of : tm_t -> cstring.t -> Prop. +Parameter strftime_text_of : cstring.t -> tm_t -> cstring.t -> Prop. + +Definition ctime_text_of (t : time_t) (out : cstring.t) : Prop := + exists tm, local_time_to_tm t tm /\ asctime_text_of tm out. + +Axiom asctime_text_of_len : + forall tm out, + asctime_text_of tm out -> + cstring.size out = 25. + +Axiom strftime_text_of_fit : + forall fmt tm out bound, + strftime_text_of fmt tm out -> + bound = cstring.size out -> + 0 <= bound. diff --git a/rocq-brick-libstdcpp/proof/ctime/plan2.md b/rocq-brick-libstdcpp/proof/ctime/plan2.md new file mode 100644 index 0000000..8796f52 --- /dev/null +++ b/rocq-brick-libstdcpp/proof/ctime/plan2.md @@ -0,0 +1,76 @@ +# `` Refactor And Proof-Repair Plan + +## Summary + +1. Keep both client styles in `test/ctime`: green wrapper clients for normal validation and stack-local POD repro clients for the `tm::~tm()` / `timespec::~timespec()` verifier completeness bug. +2. Refactor the `ctime` proof stack so `model.v` is pure-only and `pred.v` owns all separation predicates, including `later_than`, `tmR`, and `timespecR`. +3. Rework `tmR` with `sl.lock` and `#[only(lazy_unfold)] derive` so it stays abstract in client proofs while hiding non-standard `tm` fields. +4. Repair as many non-repro proofs as possible, using `dune coq top` / `dune rocq top` for live replay, while keeping the POD repro lemmas explicit and buildable. + +## Key Changes + +1. Refactor the layer split. + `model.v` will contain only pure definitions and `Prop`-valued relations. + Move `tm_model` and `timespec_model` into `model.v`. + Keep `TIME_UTC`, `clock_t_model`, `time_t_model`, `clock_result`, `timespec_get_result`, `utc_time_to_tm`, `local_time_to_tm`, `mktime_result`, `asctime_text_of`, `strftime_text_of`, and `ctime_text_of` in `model.v`. + `pred.v` will import `model.v` and define all `Rep` / `mpred`-valued abstractions. + +2. Replace `current_time_result` with `later_than` in `pred.v`. + Remove `current_time_result` entirely. + Define `later_than` as a `Parameter`, not by copy-pasting the class-based NOVA code. + Add the needed supporting declarations in parameter/axiom form: `Knowledge1 later_than`, `Timeless1 later_than`, `WeaklyObjective1 later_than`, and the down-closed law. + Do not add `Hint Opaque later_than` or `Arguments later_than : simpl never.`, since `later_than` is a parameter and those lines would add no value. + Update the `time` spec so success returns the integer time value and `later_than` for that value, and `time(&t)` also writes the same integer to `*t`. + +3. Rework `tmR` to match the intended public contract. + Publicly expose only the 9 ISO C `tm` fields via `tm_model`. + Keep `tm_gmtoff` and `tm_zone` hidden inside `tmR_hidden`. + Do not decide now whether `tm_zone` is owned, borrowed, or ignored; hide that choice inside `tmR_hidden` and add a TODO comment for later investigation. + Implement the exported `tmR` through a locked wrapper. + Prefer `sl.lock` and `#[only(lazy_unfold)] derive` to solve the current transparency issue instead of ad hoc opacity hints. + Apply the same style to any other exported rep that needs abstraction control in client proofs. + +4. Keep `timespecR` simple and standard-facing. + `timespecR` continues to describe only `tv_sec` and `tv_nsec`. + If abstraction control is needed for `timespecR`, use the same locked/lazy-unfold pattern as `tmR`. + +5. Update the client suite to include both green and repro paths. + Add pointer-wrapper clients for `timespec_get` and `mktime` so those APIs have green proof-backed coverage without local POD cleanup. + Keep stack-local POD repro clients for both `std::timespec` and `std::tm`. + Keep direct green clients for `gmtime`, `localtime`, `asctime`, `ctime`, `strftime`, and repeated static-return calls, unless proof replay shows a strictly better shape. + Keep `main()` on the green path only so `main_ok` remains part of the normal successful build. + +6. Update `test/ctime/proof.v` to reflect the split. + Fully prove the green clients where feasible. + Keep the POD repro lemmas under `verify?` and explicitly `Admitted` if the destructor-spec bug still blocks them. + Add short comments stating that those lemmas intentionally preserve a verifier completeness repro. + For non-repro lemmas, do not stop at the first failure: try to repair each proof, or leave the most-progress script if full discharge is still blocked. + +## Proof Repair Strategy + +1. First fix the representation-level blocker. + Refactor `tmR` and `timespecR` so client proofs no longer fail on transparent rep expansion. + +2. Then replay each non-repro client proof individually. + Use `dune coq top` or `dune rocq top` from the `rocq-brick-libstdcpp` project root as the default live environment. + Prefer dune-managed tops over plain `coqtop` for proof inspection and debugging. + Keep proof edits local and minimal; do not introduce broad infrastructure unless repeated failures show it is necessary. + +3. Treat the POD destructor repros separately. + Preserve isolated repro lemmas for local `tm` and local `timespec`. + Keep them buildable through explicit admissions if the verifier still insists on destructor specs without bodies. + +## Validation Plan + +1. Rebuild `./test/ctime/proof.vo` as the main acceptance target. +2. Confirm the green path proves successfully, including `main()`. +3. Confirm the POD repro lemmas remain present and documented. +4. Confirm the generated `test_cpp.v` still contains local `tm` and `timespec` objects for the repro cases. +5. Re-run proof replay on every non-repro lemma and retain the strongest checked script reached for each one. + +## Assumptions And Defaults + +1. The final `ctime` test theory should stay buildable even if POD repro lemmas remain admitted. +2. `later_than` is a local `ctime` abstraction in `pred.v`, not an import of NOVA’s shared predicate framework. +3. The `tm_zone` ownership question is intentionally deferred and hidden inside `tmR_hidden`. +4. `difftime` remains deferred. diff --git a/rocq-brick-libstdcpp/proof/ctime/pred.v b/rocq-brick-libstdcpp/proof/ctime/pred.v new file mode 100644 index 0000000..e2bd326 --- /dev/null +++ b/rocq-brick-libstdcpp/proof/ctime/pred.v @@ -0,0 +1,109 @@ +(* + * Copyright (c) 2025 SkyLabs AI, Inc. + * This software is distributed under the terms of the BedRock Open-Source License. + * See the LICENSE-BedRock file in the repository root for details. + *) +Require Import skylabs.auto.cpp.prelude.proof. +Require Import skylabs.auto.cpp.elpi.derive. +Require Export skylabs.cpp.string. +Require Export skylabs.brick.libstdcpp.iostream.pred. + +Require Export skylabs.brick.libstdcpp.ctime.model. +Require Import skylabs.brick.libstdcpp.ctime.inc_ctime_cpp. + +#[local] Set Primitive Projections. + +Section with_cpp. + Context `{Σ : cpp_logic, σ : genv}. + + Parameter later_than : abs_time_t -> mpred. + #[global] Declare Instance later_than_knowledge : Knowledge1 later_than. + #[global] Declare Instance later_than_timeless : Timeless1 later_than. + #[global] Declare Instance later_than_weakly_objective : WeaklyObjective1 later_than. + Axiom later_than_down_closed : + forall t1 t2, + (t1 <= t2)%N -> + later_than (abs_time_of_N t2) |-- later_than (abs_time_of_N t1). + + Parameter system_start_at : abs_time_t -> mpred. + #[global] Declare Instance system_start_at_knowledge : Knowledge1 system_start_at. + #[global] Declare Instance system_start_at_timeless : Timeless1 system_start_at. + #[global] Declare Instance system_start_at_weakly_objective : WeaklyObjective1 system_start_at. + + (** Correctness of a [time] result, relative to the current-time world. *) + Definition time_result (t : time_t) : mpred := + [| 0 <= t |] ** + Exists now, + [| time_t_to_abs_time t now |] ** + later_than now. + #[global] Hint Opaque time_result : typeclass_instances sl_opacity. + #[global] Arguments time_result : simpl never. + + (** Correctness of a [timespec_get] result, relative to current absolute time. *) + Definition timespec_get_result (ts : timespec_t) : mpred := + [| timespec_wf ts |] ** + Exists now, + [| timespec_to_abs_time ts now |] ** + later_than now. + #[global] Hint Opaque timespec_get_result : typeclass_instances sl_opacity. + #[global] Arguments timespec_get_result : simpl never. + + (** Correctness of a [clock] result as duration since system start. *) + Definition clock_result (ticks : clock_t) : mpred := + [| 0 <= ticks |] ** + Exists diff start now, + [| clock_t_to_abs_time_diff ticks diff |] ** + system_start_at start ** + [| abs_time_plus_diff start diff now |] ** + later_than now. + #[global] Hint Opaque clock_result : typeclass_instances sl_opacity. + #[global] Arguments clock_result : simpl never. + + (** Representation of the hidden, non-standard tail of [struct tm]. + + This predicate is the only place where the glibc-specific fields + [tm_gmtoff] and [tm_zone] may appear. It is meant to describe those + fields relative to the enclosing [tm] object while keeping their exact + ownership story abstract from clients of [tmR]. + + This predicate is intentionally unindexed: a provisional equation for a + dummy hidden model would become part of the interface and make later + refinements breaking changes. If clients eventually need semantic facts + about the non-standard tail, introduce that model deliberately then. + + TODO: investigate whether [tm_zone] should be owned, borrowed, or + abstracted away by this hidden representation. *) + Parameter tmR_hidden : cQp.t -> Rep. + #[only(cfracsplittable)] derive tmR_hidden. + + Parameter timespecR_raw : cQp.t -> timespec_t -> Rep. + #[only(type_ptr="timespec", cfracsplittable)] derive timespecR_raw. +End with_cpp. + +sl.lock +Definition tmR `{Σ : cpp_logic} {σ : genv} (q : cQp.t) (tm : tm_t) : Rep := + structR "tm" q ** + _field "tm::tm_sec" |-> primR Tint q (Vint tm.(tm_model_sec)) ** + _field "tm::tm_min" |-> primR Tint q (Vint tm.(tm_model_min)) ** + _field "tm::tm_hour" |-> primR Tint q (Vint tm.(tm_model_hour)) ** + _field "tm::tm_mday" |-> primR Tint q (Vint tm.(tm_model_mday)) ** + _field "tm::tm_mon" |-> primR Tint q (Vint tm.(tm_model_mon)) ** + _field "tm::tm_year" |-> primR Tint q (Vint tm.(tm_model_year)) ** + _field "tm::tm_wday" |-> primR Tint q (Vint tm.(tm_model_wday)) ** + _field "tm::tm_yday" |-> primR Tint q (Vint tm.(tm_model_yday)) ** + _field "tm::tm_isdst" |-> primR Tint q (Vint tm.(tm_model_isdst)) ** + tmR_hidden q. +#[only(lazy_unfold)] derive tmR. +#[only(type_ptr,cfracsplittable)] derive tmR. + +sl.lock +Definition timespecR `{Σ : cpp_logic} {σ : genv} (q : cQp.t) (ts : timespec_t) : Rep := + timespecR_raw q ts. +#[only(lazy_unfold)] derive timespecR. +#[only(type_ptr,cfracsplittable)] derive timespecR. + +#[global] Instance tmR_learnable `{Σ : cpp_logic} {σ : genv} : LearnEqF1 tmR := + ltac:(solve_learnable). + +#[global] Instance timespecR_learnable `{Σ : cpp_logic} {σ : genv} : LearnEqF1 timespecR := + ltac:(solve_learnable). diff --git a/rocq-brick-libstdcpp/proof/ctime/spec.v b/rocq-brick-libstdcpp/proof/ctime/spec.v new file mode 100644 index 0000000..62b55b1 --- /dev/null +++ b/rocq-brick-libstdcpp/proof/ctime/spec.v @@ -0,0 +1,118 @@ +(* + * Copyright (c) 2025 SkyLabs AI, Inc. + * This software is distributed under the terms of the BedRock Open-Source License. + * See the LICENSE-BedRock file in the repository root for details. + *) +Require Import skylabs.auto.cpp.prelude.proof. + +Require Export skylabs.brick.libstdcpp.ctime.pred. +Require Import skylabs.brick.libstdcpp.ctime.inc_ctime_cpp. + +#[local] Set Primitive Projections. +#[local] Open Scope Z_scope. + +Section with_cpp. + Context `{Σ : cpp_logic, module ⊧ σ}. + + cpp.spec (named "clock") with + (\post{ticks}[Vint ticks] + clock_result ticks \\// [| ticks = -1 |]). + + cpp.spec (named "time") with + (\arg{timer_p} "__timer" (Vptr timer_p) + \pre if bool_decide (timer_p = nullptr) then emp + else timer_p |-> anyR Tlong 1$m + \post{t}[Vint t] + time_result t ** + if bool_decide (timer_p = nullptr) then emp + else timer_p |-> primR Tlong 1$m (Vint t)). + + cpp.spec (named "timespec_get") with + (\arg{ts_p} "__ts" (Vptr ts_p) + \arg{base} "__base" (Vint base) + \pre ts_p |-> anyR "timespec" 1$m + \post{r}[Vint r] + if bool_decide (base = TIME_UTC /\ r = TIME_UTC) then + Exists ts, + timespec_get_result ts ** + ts_p |-> timespecR 1$m ts + else + [| r = 0 |] ** + ts_p |-> anyR "timespec" 1$m). + + (* conversion function *) + cpp.spec (named "mktime") with + (\arg{tm_p} "__tp" (Vptr tm_p) + \pre{tm_in} tm_p |-> tmR 1$m tm_in + \post{t}[Vint t] + Exists tm_out, + [| mktime_result tm_in tm_out t |] ** + tm_p |-> tmR 1$m tm_out). + + cpp.spec (named "gmtime") with + (\arg{timer_p} "__timer" (Vptr timer_p) + \prepost{q t} timer_p |-> primR Tlong q (Vint t) + \post{res qret}[Vptr res] + if bool_decide (res = nullptr) then emp + else Exists tm, + [| utc_time_to_tm t tm |] ** + res |-> tmR (cQp.const qret) tm ** + □ (Forall (qret' : Qp), + res |-> tmR (cQp.const qret') tm ={⊤}=∗ emp)). + + cpp.spec (named "localtime") with + (\arg{timer_p} "__timer" (Vptr timer_p) + \prepost{q t} timer_p |-> primR Tlong q (Vint t) + \post{res qret}[Vptr res] + if bool_decide (res = nullptr) then emp + else Exists tm, + [| local_time_to_tm t tm |] ** + res |-> tmR (cQp.const qret) tm ** + □ (Forall (qret' : Qp), + res |-> tmR (cQp.const qret') tm ={⊤}=∗ emp)). + + cpp.spec (named "asctime") with + (\arg{tm_p} "__tp" (Vptr tm_p) + \prepost{q tm} tm_p |-> tmR q tm + \post{res qret}[Vptr res] + if bool_decide (res = nullptr) then emp + else Exists out, + [| asctime_text_of tm out |] ** + [| cstring.size out = 25 |] ** + res |-> cstring.R (cQp.const qret) out ** + □ (Forall (qret' : Qp), + res |-> cstring.R (cQp.const qret') out ={⊤}=∗ emp)). + + cpp.spec (named "ctime") with + (\arg{timer_p} "__timer" (Vptr timer_p) + \prepost{q t} timer_p |-> primR Tlong q (Vint t) + \post{res qret}[Vptr res] + if bool_decide (res = nullptr) then emp + else Exists out, + [| ctime_text_of t out |] ** + [| cstring.size out = 25 |] ** + res |-> cstring.R (cQp.const qret) out ** + □ (Forall (qret' : Qp), + res |-> cstring.R (cQp.const qret') out ={⊤}=∗ emp)). + + cpp.spec (named "strftime") with + (\arg{buf_p} "__s" (Vptr buf_p) + \arg{maxsize} "__maxsize" (Vn maxsize) + \arg{format_p} "__format" (Vptr format_p) + \arg{tm_p} "__tp" (Vptr tm_p) + \prepost{buf_in} buf_p |-> cstring.bufR 1 (Z.of_N maxsize) buf_in + \prepost{qfmt format_s} format_p |-> cstring.R qfmt format_s + \prepost{qtm tm} tm_p |-> tmR qtm tm + \post{written}[Vn written] + if bool_decide (written = 0)%N then + Exists buf_out, + buf_p |-> cstring.bufR 1 (Z.of_N maxsize) buf_out + else Exists out, + [| strftime_text_of format_s tm out |] ** + [| 0 <= cstring.size out < Z.of_N maxsize |] ** + [| written = Z.to_N (cstring.size out) |] ** + buf_p |-> cstring.bufR 1 (Z.of_N maxsize) out). + + (* BRiCk does not currently support doubles, so [difftime] is deferred. *) + +End with_cpp. diff --git a/rocq-brick-libstdcpp/proof/dune.inc b/rocq-brick-libstdcpp/proof/dune.inc index e5f2d43..3aae0e2 100644 --- a/rocq-brick-libstdcpp/proof/dune.inc +++ b/rocq-brick-libstdcpp/proof/dune.inc @@ -26,6 +26,15 @@ (with-stderr-to inc_cstdlib_cpp.v.stderr (run cpp2v -v %{input} -o inc_cstdlib_cpp.v --no-elaborate -- -std=c++20 -stdlib=libstdc++ )))) (alias (name srcs) (deps inc_cstdlib.cpp)) ) +(subdir ctime + (rule + (targets inc_ctime_cpp.v.stderr inc_ctime_cpp.v) + (alias test_ast) + (deps (:input inc_ctime.cpp) (glob_files_rec ../*.hpp) (universe)) + (action + (with-stderr-to inc_ctime_cpp.v.stderr (run cpp2v -v %{input} -o inc_ctime_cpp.v --no-elaborate -- -std=c++20 -stdlib=libstdc++ )))) + (alias (name srcs) (deps inc_ctime.cpp)) +) (subdir iostream (rule (targets inc_iostream_cpp.v.stderr inc_iostream_cpp.v) diff --git a/rocq-brick-libstdcpp/test/ctime/proof.v b/rocq-brick-libstdcpp/test/ctime/proof.v new file mode 100644 index 0000000..2d3c630 --- /dev/null +++ b/rocq-brick-libstdcpp/test/ctime/proof.v @@ -0,0 +1,167 @@ +(* + * Copyright (c) 2025 SkyLabs AI, Inc. + * This software is distributed under the terms of the BedRock Open-Source License. + * See the LICENSE-BedRock file in the repository root for details. + *) +Require Import skylabs.auto.cpp.prelude.proof. + +Require Import skylabs.brick.libstdcpp.ctime.spec. +Require Import skylabs.brick.libstdcpp.test.ctime.test_cpp. + +Section with_cpp. + Context `{Σ : cpp_logic} `{MOD : module ⊧ σ}. + + Lemma arrayLR_to_arrayR_at {A} (ty : type) (lo hi : Z) (R : A -> Rep) (xs : list A) (p : ptr) : + p |-> arrayLR ty lo hi R xs ⊣⊢ + [| lengthZ xs = hi - lo |] ** p .[ ty ! lo ] |-> arrayR ty R xs. + Proof. + rewrite arrayLR.unlock. + by rewrite _at_sep _at_only_provable _at_offsetR. + Qed. + + Lemma _at_arrayR_valid_type_obs {A} (R : A -> Rep) (p : ptr) xs ty : + Observe [| is_Some (size_of σ ty) |] (p |-> arrayR ty R xs). + Proof. + rewrite -_at_only_provable. apply _at_observe, arrayR_valid_type_obs. + Qed. + + Lemma _at_arrayR_valid_type_obs' {A} (R : A -> Rep) (p : ptr) xs ty : + Observe [| HasSize ty |] (p |-> arrayR ty R xs). + Proof. unfold HasSize. apply _. Qed. + + Lemma arrayLR_to_arrayR {A} (ty : type) (R : A -> Rep) (xs : list A) (p : ptr) : + p |-> arrayLR ty 0 (lengthZ xs) R xs ⊢ p |-> arrayR ty R xs. + Proof. + rewrite arrayLR_to_arrayR_at. + iIntros "[_ ?]". + iDestruct (_at_arrayR_valid_type_obs' with "[$]") as %Hsz. + by normalize_ptrs. + Qed. + + Lemma arrayLR_zeros_cstring_bufR_build (p : ptr) (sz : N) : + StringSidecond (sz <> 0%N) -> + p |-> arrayLR "char" 0 (Z.of_N sz) (primR "char" 1$m) (replicateN sz (Vchar 0)) + ⊢ p |-> cstring.bufR 1$m sz "". + Proof. + intros Hnz. + iIntros "Hbuf". + iApply (arrayR_zeros_cstring_bufR_build sz); [done|]. + replace (Z.of_N sz) with (lengthZ (replicateN sz (Vchar 0))). + 2: { + rewrite lengthN_replicateN. + reflexivity. + } + iApply (arrayLR_to_arrayR _ _ _ _ with "Hbuf"). + Qed. + + cpp.spec "test_time_null()" default. + Lemma test_time_null_ok : verify[module] "test_time_null()". + Proof. verify_spec; go. Qed. + + cpp.spec "test_time_store()" default. + Lemma test_time_store_ok : verify[module] "test_time_store()". + Proof. verify_spec; go. Qed. + + cpp.spec "test_clock()" default. + Lemma test_clock_ok : verify[module] "test_clock()". + Proof. verify_spec; go. Qed. + + cpp.spec "test_timespec_get_ptr(timespec* )" with ( + \arg{ts_p} "ts" (Vptr ts_p) + \pre ts_p |-> anyR "timespec" 1$m + \post + (Exists ts, + timespec_get_result ts ** + ts_p |-> timespecR 1$m ts) \\// + (ts_p |-> anyR "timespec" 1$m) + ). + Lemma test_timespec_get_ptr_ok : verify[module] "test_timespec_get_ptr(timespec* )". + Proof. + verify_spec. + go. + case_bool_decide. + - iLeft. + go. + - iRight. + go. + Qed. + + cpp.spec "test_timespec_get_local()" default. + Lemma test_timespec_get_local_repro : verify?[module] "test_timespec_get_local()". + Proof. + (* Intentionally preserves the POD local-dtor completeness repro for [timespec]. *) + verify_spec; go. + Admitted. + + cpp.spec "test_timespec_dtor_bug()" default. + Lemma test_timespec_dtor_bug_repro : verify?[module] "test_timespec_dtor_bug()". + Proof. + (* Intentionally preserves the isolated POD ctor/dtor completeness repro for [timespec]. *) + verify_spec; go. + Admitted. + + cpp.spec "test_mktime_ptr(tm* )" with ( + \arg{tm_p} "tm" (Vptr tm_p) + \pre{tm_in} tm_p |-> tmR 1$m tm_in + \post + Exists t tm_out, + [| mktime_result tm_in tm_out t |] ** + tm_p |-> tmR 1$m tm_out + ). + Lemma test_mktime_ptr_ok : verify[module] "test_mktime_ptr(tm* )". + Proof. + verify_spec. + go. + iExists _. + go. + Qed. + + cpp.spec "test_mktime_local()" default. + Lemma test_mktime_local_repro : verify?[module] "test_mktime_local()". + Proof. + (* Intentionally preserves the POD local-dtor completeness repro for [tm]. *) + verify_spec; go. + Admitted. + + cpp.spec "test_tm_dtor_bug()" default. + Lemma test_tm_dtor_bug_repro : verify?[module] "test_tm_dtor_bug()". + Proof. + (* Intentionally preserves the isolated POD ctor/dtor completeness repro for [tm]. *) + verify_spec; go. + Admitted. + + cpp.spec "test_gmtime_and_asctime()" default. + Lemma test_gmtime_and_asctime_ok : verify[module] "test_gmtime_and_asctime()". + Proof. + verify_spec. + go. + wp_if; go. + Qed. + + cpp.spec "test_localtime_and_ctime()" default. + Lemma test_localtime_and_ctime_ok : verify[module] "test_localtime_and_ctime()". + Proof. + verify_spec; go. + Qed. + + cpp.spec "test_strftime()" default. + Lemma test_strftime_ok : verify[module] "test_strftime()". + Proof. + (* Existing cleanup blocker: downgrade [cstring.bufR] back to the + stack-array [anyR] shape. Avoid replaying the partial proof here, + since automation can time out before reaching the known blocker. *) + Admitted. + + cpp.spec "test_repeated_static_calls()" default. + Lemma test_repeated_static_calls_ok : verify[module] "test_repeated_static_calls()". + Proof. + verify_spec; go. + Qed. + + cpp.spec "main()" default. + Lemma main_ok : verify[module] "main()". + Proof. + verify_spec; go. + Qed. + +End with_cpp. diff --git a/rocq-brick-libstdcpp/test/ctime/test.cpp b/rocq-brick-libstdcpp/test/ctime/test.cpp new file mode 100644 index 0000000..6c164ce --- /dev/null +++ b/rocq-brick-libstdcpp/test/ctime/test.cpp @@ -0,0 +1,87 @@ +#include + +void test_time_null() { + (void)std::time(nullptr); +} + +void test_time_store() { + std::time_t t = 0; + (void)std::time(&t); +} + +void test_clock() { + (void)std::clock(); +} + +void test_timespec_get_ptr(std::timespec *ts) { + (void)std::timespec_get(ts, TIME_UTC); +} + +void test_timespec_get_local() { + std::timespec ts{}; + (void)std::timespec_get(&ts, TIME_UTC); +} + +void test_timespec_dtor_bug() { + std::timespec ts; + (void)ts; +} + +void test_mktime_ptr(std::tm *tm) { + (void)std::mktime(tm); +} + +void test_mktime_local() { + std::tm tm{}; + tm.tm_mday = 1; + tm.tm_mon = 0; + tm.tm_year = 124; + (void)std::mktime(&tm); +} + +void test_tm_dtor_bug() { + std::tm tm; + (void)tm; +} + +void test_gmtime_and_asctime() { + std::time_t t = 0; + std::tm *tm = std::gmtime(&t); + if (tm != nullptr) { + (void)std::asctime(tm); + } +} + +void test_localtime_and_ctime() { + std::time_t t = 0; + (void)std::localtime(&t); + (void)std::ctime(&t); +} + +void test_strftime() { + std::time_t t = 0; + std::tm *tm = std::gmtime(&t); + char buf[32] = {}; + if (tm != nullptr) { + (void)std::strftime(buf, sizeof(buf), "%Y", tm); + } +} + +void test_repeated_static_calls() { + std::time_t t = 0; + (void)std::gmtime(&t); + (void)std::localtime(&t); + (void)std::ctime(&t); + (void)std::ctime(&t); +} + +int main() { + test_time_null(); + test_time_store(); + test_clock(); + test_gmtime_and_asctime(); + test_localtime_and_ctime(); + test_strftime(); + test_repeated_static_calls(); + return 0; +} diff --git a/rocq-brick-libstdcpp/test/dune.inc b/rocq-brick-libstdcpp/test/dune.inc index 7e1c50a..471dcdd 100644 --- a/rocq-brick-libstdcpp/test/dune.inc +++ b/rocq-brick-libstdcpp/test/dune.inc @@ -17,6 +17,15 @@ (with-stderr-to test_cpp.v.stderr (run cpp2v -v %{input} -o test_cpp.v --no-elaborate -- -std=c++20 -stdlib=libstdc++ )))) (alias (name srcs) (deps test.cpp)) ) +(subdir ctime + (rule + (targets test_cpp.v.stderr test_cpp.v) + (alias test_ast) + (deps (:input test.cpp) (glob_files_rec ../*.hpp) (universe)) + (action + (with-stderr-to test_cpp.v.stderr (run cpp2v -v %{input} -o test_cpp.v --no-elaborate -- -std=c++20 -stdlib=libstdc++ )))) + (alias (name srcs) (deps test.cpp)) +) (subdir geeks_for_geeks_examples (rule (targets N12_area_cpp.v.stderr N12_area_cpp.v)