Skip to content

Commit ed4b08c

Browse files
committed
Add int::MIN/MAX, Vec struct inference, and fix for-loop/Vec push codegen for non-primitive elements.
1 parent 9b4ea90 commit ed4b08c

25 files changed

Lines changed: 483 additions & 186 deletions

.cursor/skills/ion-lsp-vscode/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Adjust path for OS (no `.exe` on Linux/macOS).
6868
3. **Type-check** - `tc::TypeChecker::check_program_collecting_with_source` on merged program, symbols seeded from buffer AST (`check_program_collecting` for unit tests)
6969
4. Publish diagnostics from lexer, parser, import resolution, or type-check errors
7070
5. **Hover** - expression types, symbol docs, builtin signatures
71-
6. **Completion** - prefix-filtered; context-aware for `alias::` and `Type.` / `expr.`
71+
6. **Completion** - prefix-filtered; context-aware for `alias::` and `Type.` / `expr.` (`int`, `i8`-`i64`, `u8`-`u64`, `uint` expose `MIN`/`MAX` via `BUILTIN_TYPE_MEMBERS` in `util.rs`)
7272
7. **Go to definition** - variables, calls, methods, fields, variants, type aliases; cross-file via `module_paths`
7373
8. **References**, **document symbols**, **signature help**, **semantic tokens**
7474
9. **`did_change_watched_files`** - re-check open files whose import dependencies changed

.cursor/skills/writing-ion-code/SKILL.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ Import paths like `import "stdlib/io.ion" as io;` resolve via stdlib search path
6565
```ion
6666
let x: int = 10;
6767
let mut v: Vec<int> = Vec::new();
68+
let items: Vec<MyStruct> = Vec::new(); // element type from annotation
69+
```
70+
71+
**Integer limits**
72+
73+
```ion
74+
if n == int::MIN { ... }
75+
let cap: int = int::MAX;
6876
```
6977

7078
**Structs and enums**

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- **LSP**: diagnostics, hover, completion, go-to-definition; multi-error reporting; symbol table mirroring; diagnostics cleared on close; hover fixes for `let` bindings and module-qualified calls. Contiguous `//` doc comments attached to declarations (including `pub` items) for hover.
1010
- **Tooling**: GitHub Actions CI (Linux and Windows), pinned toolchain (1.96.0), `test_expectations.tsv` manifest, `--version`, line-numbered errors, Cursor agent skills. Split `tc` and `cgen` into submodules. `ion-build` driver and `ion.toml` manifests (`single`/`multi`, `out_dir`, `cflags`, `ldflags`, `stdlib_paths`, `emit_in_source`); per-example manifests and `build_hello`/`build_bad_main` harness smoke tests. Shared `discover_import_config` and stdlib search paths for `ion-compiler`, `ion-build`, and LSP. Integration harness precompiles `runtime/ion_runtime.c` once per run (`RUNTIME_OBJ`). `writing-ion-code` agent skill; `creating-ion-skills` examples index lists all eight project skills. Documented checked-in `examples/*.c` codegen snapshots in README and integration-test skill; regenerated example C output. Fixed `researching-pl-literature` skill `paper-seeds` reference formatting.
1111
- **Docs**: README, CONTRIBUTING, ION_SPEC, and agent skills aligned on project layout, `ion-build` workflow, `emit_in_source`, stdlib import order, LSP features and limitations, `src/build/` checklists, and portable Git Bash paths for `test_runner.sh`.
12-
- **Fixes**: match rvalue codegen, `Vec` struct drops, channel codegen, parser handling of `alias::call()`, scope-drop for moved-into-call bindings, HTTP server on Windows, integration harness on Windows. Cgen return-unwind: stop marking bindings dropped after inner-branch `return` (restores outer-path drops), dedupe `_`-prefixed unused silences, mark call arguments moved at emission to avoid double-free on unwind, and unify all function returns via `emit_function_return` (including diverging rvalue `match` arms). Integer indexing and `Vec<i32>` inference in the type checker. Match rvalue arms: reject diverging arms mixed with value arms; structural control-flow analysis for arm bodies (nested `if`/`else`, `loop` without `break`, `unsafe` blocks); reject mixed diverge and value-producing paths within one arm; cgen assigns through `if`/`else` value branches. `fmt::int_to_string` uses `String::push_byte` per digit instead of per-digit `push_str` branches; integration test asserts `0`, negatives, and `INT_MIN`. Clippy fix in `portable_source_label`.
12+
- **Fixes**: match rvalue codegen, `Vec` struct drops, channel codegen, parser handling of `alias::call()`, scope-drop for moved-into-call bindings, HTTP server on Windows, integration harness on Windows. Cgen return-unwind: stop marking bindings dropped after inner-branch `return` (restores outer-path drops), dedupe `_`-prefixed unused silences, mark call arguments moved at emission to avoid double-free on unwind, and unify all function returns via `emit_function_return` (including diverging rvalue `match` arms). Integer indexing and `Vec<i32>` inference in the type checker. Match rvalue arms: reject diverging arms mixed with value arms; structural control-flow analysis for arm bodies (nested `if`/`else`, `loop` without `break`, `unsafe` blocks); reject mixed diverge and value-producing paths within one arm; cgen assigns through `if`/`else` value branches. `fmt::int_to_string` uses `String::push_byte` per digit instead of per-digit `push_str` branches; integration test asserts `0`, negatives, and `int::MIN`. `int::MIN`/`int::MAX` on integer primitives; `Vec::new()`/`with_capacity()` infer `T` from a `let` type annotation. Clippy fix in `portable_source_label`.
1313
- **Examples**: `text_summary` (fixture file), `data_lib` (multi-module), `channel_worker` (flat single-file). Per-example `*.toml` manifests for `ion-build`.
1414
- **Multi-file fixes**: merge private struct types for cross-module type checking; per-module C symbol prefixes in multi-file codegen (`io_print_int`); walk-up `runtime/` discovery for nested build directories. Integration tests `test_multi_struct`, `test_multi_fmt_io`. Example policy: single-file examples commit `.c` snapshots; `data_lib` multi-file keeps `.ion` only.
1515

ION_SPEC.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ Ion includes the following primitive types:
488488
- Boolean: `bool`
489489
- Unit / void: `void` (function return type with no value)
490490

491+
Each integer primitive exposes compile-time limits as `Type::MIN` and `Type::MAX` (for example `int::MIN`, `i32::MAX`, `u8::MIN`). These lower to C-safe literals (avoiding `-2147483648`-style unary overflow in generated C).
492+
491493
Additional built-in generic types:
492494

493495
- `Box<T>` – heap-allocated `T` with owning semantics (`Box::new()`, `Box::unwrap()`)

examples/access_log.c

Lines changed: 73 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ int parse_line_at(int ION_MAYBE_UNUSED index);
5353
int scan_logs(ion_sender_t ION_MAYBE_UNUSED code_tx, int ION_MAYBE_UNUSED expected);
5454
int valid_log_count(void);
5555
int main(void);
56+
void fmt_print_int(int ION_MAYBE_UNUSED n);
57+
void fmt_println_int(int ION_MAYBE_UNUSED n);
58+
ion_string_t* fmt_int_to_string(int ION_MAYBE_UNUSED n);
5659
void io_print(ion_string_t* ION_MAYBE_UNUSED s);
5760
void io_println(ion_string_t* ION_MAYBE_UNUSED s);
5861
void io_print_str(uint8_t* ION_MAYBE_UNUSED s, int ION_MAYBE_UNUSED len);
5962
void io_print_int(int ION_MAYBE_UNUSED n);
60-
void fmt_print_int(int ION_MAYBE_UNUSED n);
61-
void fmt_println_int(int ION_MAYBE_UNUSED n);
62-
ion_string_t* fmt_int_to_string(int ION_MAYBE_UNUSED n);
6363
ion_string_t* line_at(int ION_MAYBE_UNUSED index) {
6464
ion_string_t* ret_val = 0;
6565
if (index == 0) {
@@ -242,6 +242,73 @@ int main(void) {
242242
return ret_val;
243243
}
244244

245+
void fmt_print_int(int ION_MAYBE_UNUSED n) {
246+
io_print_int(n);
247+
goto epilogue;
248+
epilogue:
249+
return;
250+
}
251+
252+
void fmt_println_int(int ION_MAYBE_UNUSED n) {
253+
io_print_int(n);
254+
{
255+
int _newline = write(1, (uint8_t*)"\n", 1);
256+
(void)_newline;
257+
}
258+
goto epilogue;
259+
epilogue:
260+
return;
261+
}
262+
263+
ion_string_t* fmt_int_to_string(int ION_MAYBE_UNUSED n) {
264+
ion_string_t* ret_val = 0;
265+
uint8_t buf[12] = {0};
266+
int len = 0;
267+
int negative = 0;
268+
int value = n;
269+
ion_string_t* result = ion_string_new();
270+
if (value == 0) {
271+
ret_val = ion_string_from_literal("0", 1);
272+
(void)result;
273+
(void)negative;
274+
(void)len;
275+
(void)buf;
276+
if (result) { ion_string_free(result); }
277+
goto epilogue;
278+
}
279+
if (value < 0) {
280+
negative = 1;
281+
if (value == (0 - 2147483647 - 1)) {
282+
ret_val = ion_string_from_literal("-2147483648", 11);
283+
(void)result;
284+
(void)negative;
285+
(void)len;
286+
(void)buf;
287+
if (result) { ion_string_free(result); }
288+
goto epilogue;
289+
}
290+
value = (0 - value);
291+
}
292+
while (value > 0) {
293+
int digit = (value % 10);
294+
buf[len] = (uint8_t)(48 + digit);
295+
len = (len + 1);
296+
value = (value / 10);
297+
}
298+
if (negative) {
299+
ion_string_push_byte(result, (unsigned char)((uint8_t)45));
300+
}
301+
int i = (len - 1);
302+
while (i >= 0) {
303+
ion_string_push_byte(result, (unsigned char)(({ int __ion_idx_2 = i; (__ion_idx_2 >= 0 && __ion_idx_2 < 12) ? buf[__ion_idx_2] : (ion_panic("Array index out of bounds"), buf[0]); })));
304+
i = (i - 1);
305+
}
306+
ret_val = result;
307+
goto epilogue;
308+
epilogue:
309+
return ret_val;
310+
}
311+
245312
void io_print(ion_string_t* ION_MAYBE_UNUSED s) {
246313
{
247314
int _result = write(1, s->data, (int)s->len);
@@ -296,7 +363,7 @@ void io_print_int(int ION_MAYBE_UNUSED n) {
296363
}
297364
if (value < 0) {
298365
negative = 1;
299-
if (value == (-2147483648)) {
366+
if (value == (0 - 2147483647 - 1)) {
300367
{
301368
int _result = write(1, (uint8_t*)"-2147483648", 11);
302369
(void)_result;
@@ -316,8 +383,8 @@ void io_print_int(int ION_MAYBE_UNUSED n) {
316383
}
317384
int i = 0;
318385
while (i < (len / 2)) {
319-
uint8_t tmp = ({ int __ion_idx_2 = i; (__ion_idx_2 >= 0 && __ion_idx_2 < 12) ? buf[__ion_idx_2] : (ion_panic("Array index out of bounds"), buf[0]); });
320-
buf[i] = ({ int __ion_idx_3 = ((len - 1) - i); (__ion_idx_3 >= 0 && __ion_idx_3 < 12) ? buf[__ion_idx_3] : (ion_panic("Array index out of bounds"), buf[0]); });
386+
uint8_t tmp = ({ int __ion_idx_3 = i; (__ion_idx_3 >= 0 && __ion_idx_3 < 12) ? buf[__ion_idx_3] : (ion_panic("Array index out of bounds"), buf[0]); });
387+
buf[i] = ({ int __ion_idx_4 = ((len - 1) - i); (__ion_idx_4 >= 0 && __ion_idx_4 < 12) ? buf[__ion_idx_4] : (ion_panic("Array index out of bounds"), buf[0]); });
321388
buf[((len - 1) - i)] = tmp;
322389
i = (i + 1);
323390
}
@@ -336,73 +403,6 @@ void io_print_int(int ION_MAYBE_UNUSED n) {
336403
return;
337404
}
338405

339-
void fmt_print_int(int ION_MAYBE_UNUSED n) {
340-
io_print_int(n);
341-
goto epilogue;
342-
epilogue:
343-
return;
344-
}
345-
346-
void fmt_println_int(int ION_MAYBE_UNUSED n) {
347-
io_print_int(n);
348-
{
349-
int _newline = write(1, (uint8_t*)"\n", 1);
350-
(void)_newline;
351-
}
352-
goto epilogue;
353-
epilogue:
354-
return;
355-
}
356-
357-
ion_string_t* fmt_int_to_string(int ION_MAYBE_UNUSED n) {
358-
ion_string_t* ret_val = 0;
359-
uint8_t buf[12] = {0};
360-
int len = 0;
361-
int negative = 0;
362-
int value = n;
363-
ion_string_t* result = ion_string_new();
364-
if (value == 0) {
365-
ret_val = ion_string_from_literal("0", 1);
366-
(void)result;
367-
(void)negative;
368-
(void)len;
369-
(void)buf;
370-
if (result) { ion_string_free(result); }
371-
goto epilogue;
372-
}
373-
if (value < 0) {
374-
negative = 1;
375-
if (value == (-2147483648)) {
376-
ret_val = ion_string_from_literal("-2147483648", 11);
377-
(void)result;
378-
(void)negative;
379-
(void)len;
380-
(void)buf;
381-
if (result) { ion_string_free(result); }
382-
goto epilogue;
383-
}
384-
value = (0 - value);
385-
}
386-
while (value > 0) {
387-
int digit = (value % 10);
388-
buf[len] = (uint8_t)(48 + digit);
389-
len = (len + 1);
390-
value = (value / 10);
391-
}
392-
if (negative) {
393-
ion_string_push_byte(result, (unsigned char)((uint8_t)45));
394-
}
395-
int i = (len - 1);
396-
while (i >= 0) {
397-
ion_string_push_byte(result, (unsigned char)(({ int __ion_idx_4 = i; (__ion_idx_4 >= 0 && __ion_idx_4 < 12) ? buf[__ion_idx_4] : (ion_panic("Array index out of bounds"), buf[0]); })));
398-
i = (i - 1);
399-
}
400-
ret_val = result;
401-
goto epilogue;
402-
epilogue:
403-
return ret_val;
404-
}
405-
406406

407407
static void* ion_spawn_entry_0(void* arg) {
408408
ion_spawn_ctx_0* ctx = (ion_spawn_ctx_0*)arg;

examples/hello_world_safe.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ void io_print_int(int ION_MAYBE_UNUSED n) {
8383
}
8484
if (value < 0) {
8585
negative = 1;
86-
if (value == (-2147483648)) {
86+
if (value == (0 - 2147483647 - 1)) {
8787
{
8888
int _result = write(1, (uint8_t*)"-2147483648", 11);
8989
(void)_result;

0 commit comments

Comments
 (0)