@cella-lang/c 0.1.202610130811 → 0.1.202610131327

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGES.md CHANGED
@@ -4,6 +4,13 @@
4
4
 
5
5
  ## 未發佈
6
6
 
7
+ - **不相容**:記憶體改成一份統一記憶體(照 CH2O)。`c.Mem t`、`c.memNew t`、`c.Allocated t` 換成 `c.CMem`、`c.memNew`、`c.Allocated`;指標 `c.Ptr` 是 `c.nullP` 或 `c.cptr 配置 路徑 索引`(不再是 cpp 的指標),指向 struct 成員的指標(`c.memberPtr m p "k"`)與指向變數、陣列格的指標是同一種,收 `int*`/`char*` 的函式對它們一樣運作;越過成員或陣列邊界的指標算術是越界。
8
+ - 讀寫與函式庫多了明確的元素型別參數:`c.load t m q ok`、`c.store t m q v ok`、`c.addrOf t m v`、`c.declArrM t m n z`、`c.idx t …`、`c.strlen pl t m q ok`、`c.strcmp pl t …`、`c.argS t m q`、`c.readableNB t …`、`c.readK t …`、`c.writeK t …`、`c.readable2 t …`、`c.at2 t …`、`c.qsort pl t …`、`c.bsearch pl t …`、`c.memset t …`、`c.memchr t …`、`c.strchr t …`……;`c.malloc ct m n fails`、`c.calloc`、`c.realloc` 收元素的 `c.CType`(整數是 `c.ctInt t`)。struct 不再有自己的記憶體(`c.smemNew` 拿掉),`c.arrowSet`、`c.arrow` 的前置條件是 `refl`(`c.objWritableB`、`c.objOkB`)。
9
+ - 前置條件 `c.DerefOk t m q`/`c.StoreOk t m q` 是 c 的資料型別(建構子 `c.derefOk`、`c.storeOk`,具體的值照舊 `refl`),條件寫在統一記憶體上(`c.readOkC`、`c.writeOkC`);`c.CStr t m q` 等字串前置條件是聚焦之後的 cpp 條件。原本收 `Mem t` 的版本留作平面層,名字加 F(`c.readKF`、`c.qsortF`、`c.readNF`、`c.readStrF`、`c.derefOkF`……)。
10
+ - 指標的指標:`c.addrOfPtr`、`c.loadP`、`c.storeP`(指標也在同一份記憶體);`c.Argv`、`c.TokRes`、`c.Fgot` 等不再帶平台或型別參數。
11
+ - 引理在新的表示上重證:`c.loadStore`、`c.storeDerefOk`、`c.writableAfterWriteK`、`c.writable2AfterWrite`、`c.mallocStoreOk`、`c.mallocFreeOk`、`c.fullDerefOk`/`c.loadFullK`、`c.loadToCpp`;新增 `c.storeObj`、`c.mallocObjWritable`。
12
+ - 經 `unsigned*` 讀 `int` 的物件(對應的有號/無號型別,直接或經 `void*`)判 unknown(`model_stricter`,`c.signVariantAccessB`);其他型別不符是嚴格別名,reject。
13
+ - 除錯器:`c.ptrText`、`c.varJsonPtr`。
7
14
  - 模型比標準嚴的前置條件改由帶 `@[conservative]` 的定義表達:只卡在它們時判決是 **unknown**(`model_stricter`),不是 reject——讀不確定的值(`c.initElemB`、`c.initAtB`、`c.initNB`、`c.memberInitB`)、union 非作用中的成員(`c.uActiveB`)、`qsort` 比較相等的值相同(`c.qsortEqSameB`)、`bsearch` 至多一個相符(`c.bsearchUniqueB`)、對 `"r"` 開的檔案輸出(`c.notReadOnlyB`)、經 `void*` 用對應的有號/無號型別存取(`c.signVariantAccessB`)。翻譯器寫的證明照舊是 `refl`;以下前置條件的形狀改了(定義上相等,雜湊會變):
8
15
  - `c.derefOk` 收 `c.readOkB m q = true`(活著且在界內 `cpp.liveB`,而且已初始化);`c.readableB`、`c.readableNB` 拆成「界內」與「已初始化」的 and;`c.scalar`/`c.ptrScalar` 收 `c.memberInitB x = true`;`c.uget` 收 `c.uActiveB ms u k = true`;`c.UncastOk` 收 `c.uncastOkB …`;`c.canWriteB` 是 `isOpenB` 與 `notReadOnlyB` 的 and;`c.qsortOkB`、`c.bsearchOkB` 併入比標準嚴的那一部分。
9
16
  - 標準庫的 Int 名字改進 `Int` namespace(`add_int` → `Int.add`、`ltInt` → `Int.blt`、`addIntF` → `Int.addFast`…),套件的原始碼與產生的項都寫完整名稱。對照表見 cella-lang 的變更紀錄。
package/README.md CHANGED
@@ -52,46 +52,53 @@ def s : c.get (c.at a (c.litV (c.tInt p) (pos 3) refl) (c.readable refl)) = pos
52
52
  def t : c.Val (c.tInt p) := c.at a (c.litV (c.tInt p) (pos 5) refl) (c.readable refl) -- reject: a[5] is out of bounds
53
53
  ```
54
54
 
55
- Pointers and memory reuse the provenance memory model of `@cella-lang/cpp`: `c.Mem t` and `c.Ptr` (a pointer remembers its allocation
56
- block and offset). `c.addrOf m x` is `&x`, `c.load m q (c.derefOk refl)` is `*q` (not NULL, block alive, in bounds, initialized),
57
- `c.store m q v (c.storeOk refl)` is `*q = v`. Arrays decay with `c.arrToMem`; `c.ptrAdd` / `c.idx` require the result to stay inside the
58
- array or one past its end, `c.ptrDiff` / `c.ptrLt` require both pointers to be in the same array. `c.malloc m n fails` takes whether the
59
- allocation fails as a parameter (the environment's choice; failure returns NULL), so a verdict must hold for both outcomes — dereferencing
60
- without a NULL check does not prove. `c.calloc` zero-fills; `c.free` requires NULL or a live block from `malloc`, pointed at its start.
61
- `c.toVoid` / `c.fromVoid` convert through `void*`, back to the original type only.
55
+ Memory follows CH2O (Krebbers, *The C standard formalized in Coq*): a program has one memory `c.CMem` (`c.memNew` is empty), every
56
+ allocation is a tree of values (integers, pointers, arrays, struct and union members), and a pointer `c.Ptr` is NULL or (allocation, path,
57
+ index) — the path walks struct members and array cells down to an array (a scalar counts as an array of length 1, 6.5.6p7), and the index is
58
+ the position in it. So `&x`, `&a[i]`, `&s.k` and `p->name` are the same kind of pointer, and a function taking `int *` or `char *` works on
59
+ all of them. `c.addrOf t m x` is `&x`, `c.load t m q (c.derefOk refl)` is `*q` (not NULL, allocation alive, in bounds, initialized, and the
60
+ object has type t — reading an `int` through a `long *` is strict aliasing, 6.5p7), `c.store t m q v (c.storeOk refl)` is `*q = v`, and
61
+ `c.memberPtr m p "k"` is `&p->k`. Arrays decay with `c.arrToMem`; `c.ptrAdd` / `c.idx` require the result to stay inside the same array or
62
+ one past its end — pointer arithmetic does not cross into the next member — and `c.ptrDiff` / `c.ptrLt` require both pointers to be in
63
+ the same array. `c.malloc ct m n fails` takes whether the allocation fails as a parameter (the environment's choice; failure returns NULL),
64
+ so a verdict must hold for both outcomes — dereferencing without a NULL check does not prove. `c.calloc` zero-fills; `c.free` requires NULL
65
+ or a live block from `malloc`, pointed at its start. `c.toVoid` / `c.fromVoid` convert through `void*`, back to the original type only.
66
+ `c.loadStore` proves that `*q = v; *q` reads `v` for any memory and pointer.
62
67
 
63
68
  ```cella
64
69
  import c
65
70
  def t : cpp.IType := cpp.tInt cpp.lp64P
66
- def r : c.Allocated t := c.addrOf (c.memNew t) (c.litV t (pos 5) refl) -- int x = 5; int *q = &x;
67
- def s : c.get (c.load (c.amem r) (c.aptr r) (c.derefOk refl)) = pos 5 := refl -- accept: *q is 5
68
- def u : c.Val t := c.load (c.memNew t) c.nullP (c.derefOk refl) -- reject: *NULL
71
+ def r : c.Allocated := c.addrOf t c.memNew (c.litV t (pos 5) refl) -- int x = 5; int *q = &x;
72
+ def s : c.get (c.load t (c.amem r) (c.aptr r) (c.derefOk refl)) = pos 5 := refl -- accept: *q is 5
73
+ def u : c.Val t := c.load t c.memNew c.nullP (c.derefOk refl) -- reject: *NULL
69
74
  ```
70
75
 
71
76
  Strings are NUL-terminated `char` arrays in that memory. `c.strArr p m "abc"` is `char s[] = "abc"`, `c.strArrN p m 10 "abc" refl` is
72
77
  `char s[10] = "abc"` (zero-filled; exactly N bytes leaves no NUL), and `c.strLit p m "abc"` is `char *s = "abc"` — a read-only allocation, so
73
78
  writing through it does not prove. `<string.h>`: `c.strlen`, `c.strcmp`, `c.strncmp`, `c.strcpy`, `c.strncpy` (writes no NUL when the source is
74
79
  not shorter than n), `c.strcat`, `c.strchr`; their preconditions are `c.cstrOk refl` (a NUL is reachable), `c.cstrNOk refl` (the first n bytes
75
- or a NUL), and `c.strcpyOk refl refl` etc. (the destination is writable and large enough, no overlap). `printf` takes strings with
76
- `c.argS m q` for `%s` (width, `-`, precision; with a precision the array needs no NUL). `<ctype.h>`: `c.isdigit`, `isalpha`, `isspace`,
77
- `isupper`, `islower`, `toupper`, `tolower`, whose argument must be an `unsigned char` value or `EOF` (`c.ctypeArg refl`).
80
+ or a NUL), and `c.strcpyOk refl refl` etc. (the destination is writable and large enough, no overlap). They work on whatever array the pointer
81
+ is in, a member array included (`strcpy(p->name, "Ann")`). `printf` takes strings with `c.argS t m q` for `%s` (width, `-`, precision; with a
82
+ precision the array needs no NUL). `<ctype.h>`: `c.isdigit`, `isalpha`, `isspace`, `isupper`, `islower`, `toupper`, `tolower`, whose argument
83
+ must be an `unsigned char` value or `EOF` (`c.ctypeArg refl`).
78
84
 
79
85
  ```cella
80
86
  import c
81
87
  def p : cpp.Platform := c.lp64P
82
88
  def C : cpp.IType := c.tChar p
83
- def r : c.Allocated C := c.strLit p (c.memNew C) "hello"
84
- def s : c.format p "[%-7s]" [c.argS (c.amem r) (c.aptr r)] refl = "[hello ]" := refl -- accept
85
- def t : c.Mem C := c.store (c.amem r) (c.aptr r) (c.litV C (pos 72) refl) (c.storeOk refl) -- reject: writes a string literal
89
+ def r : c.Allocated := c.strLit p c.memNew "hello"
90
+ def s : c.format p "[%-7s]" [c.argS C (c.amem r) (c.aptr r)] refl = "[hello ]" := refl -- accept
91
+ def t : c.CMem := c.store C (c.amem r) (c.aptr r) (c.litV C (pos 72) refl) (c.storeOk refl) -- reject: writes a string literal
86
92
  ```
87
93
 
88
94
  Structs are described by their members, `c.mCons "x" (c.ctInt t) (c.mCons "next" c.ctPtr c.mNil)`, and a struct value has type
89
95
  `c.Struct ms`; every scalar member may be uninitialized on its own. `c.initS ms [c.iInt x, …] refl` is an initializer list (missing members
90
96
  are zero), `c.member ms "x" s` / `c.setMember ms "x" v s` access by name, and `c.scalar … refl` reads a member that has been initialized.
91
- In memory, a struct type gets its own `cpp.GMem (c.Struct ms)`: `c.malloc` and `c.free` work for any element type, `c.arrow ms "v" m q` refl is
92
- `q->v`, and `c.arrowSet ms "v" x m q (cpp.storeOk refl)` is `q->v = x` — a `malloc`ed struct is initialized member by member, so this
93
- needs no other member to be initialized. Arrays of structs: `c.structArr`; member arrays and strings are handled as values (`c.setElem`,
94
- `c.strcpyArr`, `c.strcmpArr`, `c.strcatArr`, `c.strcpyArrMem` / `c.strcpyMemArr` to and from memory, `c.argSArr` for `%s`). A linked list whose every `malloc` may fail is in `tests/t_linked_list.cella`.
97
+ In memory, `c.varOf (c.ctStruct ms) s m` allocates a struct and `c.malloc (c.ctStruct ms) m n fails` allocates n of them; `c.arrow ms "v" m q refl`
98
+ is `q->v` and `c.arrowSet ms "v" x m q refl` is `q->v = x` — a `malloc`ed struct is initialized member by member, so this needs no other
99
+ member to be initialized. A member can also be reached through a pointer to it (`c.memberPtr`), so `swap(&s.a, &s.b)` is the same `swap` as
100
+ for two variables. Arrays of structs: `c.structArr`. A linked list whose every `malloc` may fail is in `tests/t_linked_list.cella`, and
101
+ pointers to members in `tests/t_member_ptr.cella`.
95
102
 
96
103
  ```cella
97
104
  import c
@@ -99,23 +106,30 @@ def I : cpp.IType := c.tInt c.lp64P
99
106
  def Point : c.Members := c.mCons "x" (c.ctInt I) (c.mCons "y" (c.ctInt I) c.mNil)
100
107
  def p : c.Struct Point := c.initS Point [c.iInt (c.litV I (pos 1) refl)] refl
101
108
  def y : c.get (c.scalar (c.member Point "y" p) refl) = pos 0 := refl -- accept: y is zero-filled
102
- def u : c.Val I := c.scalar (c.member Point "y" (c.uninitS Point)) refl -- reject: y is uninitialized
109
+ def u : c.Val I := c.scalar (c.member Point "y" (c.uninitS Point)) refl -- unknown: y is uninitialized (stricter than C)
110
+ def r : c.Allocated := c.varOf (c.ctStruct Point) p c.memNew -- struct Point s = {1};
111
+ def py : c.Ptr := c.memberPtr (c.amem r) (c.aptr r) "y" -- int *py = &s.y;
112
+ def z : c.get (c.load I (c.amem r) py (c.derefOk refl)) = pos 0 := refl -- accept: *py is 0
103
113
  ```
104
114
 
105
115
  Array parameters are pointers, so a function does not know the length: `int sum(int a[], int n)` takes the precondition
106
- `c.readableNB m a n = true` ("n cells after a are readable"), and `c.readK m a n ok k h` reads `a[k]` for `h : k < n` — the function is
116
+ `c.readableNB t m a n = true` ("n cells after a are readable"), and `c.readK t m a n ok k h` reads `a[k]` for `h : k < n` — the function is
107
117
  checked once for any memory and pointer, and a caller with a concrete array proves the precondition by `refl`. Writing is the same with
108
118
  `c.writableNB` / `c.writeK`, and `c.writableAfterWriteK` keeps the precondition through a loop. Pointers can be stored in memory
109
- (`cpp.GMem c.Ptr`, `c.loadP` for `*pp`), and `c.mkArgv p ["prog", "x"]` builds `argv` (NUL-terminated strings, a NULL at `argv[argc]`).
119
+ (`c.addrOfPtr`, `c.loadP` for `*pp`), and `c.mkArgv p ["prog", "x"]` builds `argv` (NUL-terminated strings, a NULL at `argv[argc]`).
120
+
121
+ For a debugger, `c.ptrText names m q` shows a pointer the way the source would write it — `&x`, `a`, `&a[2]`, `a + 4`, `&s.k`, `NULL`
122
+ (`names` maps allocations to variable names; an unnamed allocation from `malloc` shows as `heap#b`) — and `c.varJsonPtr` gives a pointer
123
+ variable for the variables panel in the same JSON shape as the C++ and Python models.
110
124
 
111
125
  Input: `c.scanf p "%d %s" [c.tgtInt t, c.tgtChars 8] st refl` reads from the input state shared with the C++ model (`cpp.ios "12 abc"`)
112
126
  and returns what was read: `c.scanCount p r` (the number of assignments, or EOF), `c.assignInt r k t old` (the k-th assigned value, or the
113
127
  old value when the conversion failed), `c.scannedBytes r k` and `c.scanSt r` (the rest of the input). The precondition rejects a target of the
114
128
  wrong type, a result that does not fit (`%d` reading `99999999999`), and a `%s` that does not fit its array.
115
129
 
116
- Multi-dimensional arrays: `c.arr2ToMem m R C inits refl` declares `T a[R][C]` (nested or flat initializers), and `c.at2 m a R C i j hi hj ok`
130
+ Multi-dimensional arrays: `c.arr2ToMem t m R C inits refl` declares `T a[R][C]` (nested or flat initializers), and `c.at2 t m a R C i j hi hj ok`
117
131
  reads `a[i][j]` with the bounds checked per dimension — `a[0][C]` is out of bounds for the inner array even though it is inside the object.
118
- A function taking `int a[][C]` gets `c.readable2 m a n C = true` and reads with `c.read2K`.
132
+ A function taking `int a[][C]` gets `c.readable2 t m a n C = true` and reads with `c.read2K`.
119
133
 
120
134
  Floating point: `c.Double` and `c.Float` are IEEE 754 binary64 and binary32 (shared with the C++ model). `c.dlit m e` is the literal
121
135
  m × 10^e; `c.toInt t x (c.truncOk refl)` truncates toward zero with the result required to fit; division needs a nonzero divisor.
package/c.cell CHANGED
Binary file