Skip to content

Commit f0abf31

Browse files
committed
Unify function return lowering via emit_function_return for rvalue match arms.
Route diverging rvalue-match returns through scope_emit_return_unwind and goto epilogue, flip the regression test, regenerate text_summary.c, and note the fix in CHANGELOG and bug-hotspots.
1 parent 736aaac commit f0abf31

4 files changed

Lines changed: 141 additions & 167 deletions

File tree

.cursor/skills/finding-ion-bugs/references/bug-hotspots.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ CLI errors use `TypeCheckError` Debug form (`UseAfterMove { ... }`). LSP reforma
1313
## Codegen (`src/cgen/`)
1414

1515
- Drop order and `ion_drop_*` for moved fields
16-
- **Return unwind**: statement `return` uses `scope_emit_return_unwind` in `generate_stmt` (`if`/`while`/`loop`/statement `match`/`unsafe`). Rvalue `match` diverging arms use `emit_match_arm_result_from_stmts`, which emits bare C `return` without unwind; confirmed by `rvalue_match_divergent_return_omits_owned_unwind` (tc rejects the shape in `test_match_arm_divergent_rvalue.ion`).
16+
- **Return unwind**: all function exits use `emit_function_return` (`ret_val`, `scope_emit_return_unwind`, `goto epilogue`), including diverging `return` inside rvalue `match` arms (`rvalue_match_divergent_return_unwinds_owned`). Value-producing rvalue arms still assign and `break` from the `switch`.
1717
- `Box`, `Vec`, `String` layout vs `runtime/ion_runtime.h`
1818
- Single-file merge (`merge_modules`) vs `--mode multi` divergences
1919
- `extern "C"` calls only inside `unsafe` blocks in source; cgen must not strip guards

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, and mark call arguments moved at emission to avoid double-free on unwind. 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`. 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

examples/text_summary/text_summary.c

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ static ION_MAYBE_UNUSED ReadResult ReadResult_Err_new(int arg0) {
4040
return result;
4141
}
4242

43+
extern int write(int fd, uint8_t* buf, int count);
44+
4345
extern int open(uint8_t* path, int flags);
4446
extern int read(int fd, uint8_t* buf, int count);
4547
extern int close(int fd);
4648

47-
extern int write(int fd, uint8_t* buf, int count);
48-
4949
TextCounts summarize_text(ion_string_t* ION_MAYBE_UNUSED text);
5050
int main(void);
51-
ReadResult fs_read_to_string_result(ion_string_t* ION_MAYBE_UNUSED path);
5251
void fmt_print_int(int ION_MAYBE_UNUSED n);
5352
void fmt_println_int(int ION_MAYBE_UNUSED n);
5453
ion_string_t* fmt_int_to_string(int ION_MAYBE_UNUSED n);
54+
ReadResult fs_read_to_string_result(ion_string_t* ION_MAYBE_UNUSED path);
5555
void io_print(ion_string_t* ION_MAYBE_UNUSED s);
5656
void io_println(ion_string_t* ION_MAYBE_UNUSED s);
5757
void io_print_str(uint8_t* ION_MAYBE_UNUSED s, int ION_MAYBE_UNUSED len);
@@ -122,46 +122,6 @@ int main(void) {
122122
return ret_val;
123123
}
124124

125-
ReadResult fs_read_to_string_result(ion_string_t* ION_MAYBE_UNUSED path) {
126-
ReadResult ret_val = {0};
127-
{
128-
int fd = open(path->data, 0);
129-
if (fd < 0) {
130-
ret_val = ReadResult_Err_new((-1));
131-
if (path) { ion_string_free(path); }
132-
goto epilogue;
133-
}
134-
ion_string_t* text = ion_string_new();
135-
uint8_t buf[256] = {0};
136-
int n = read(fd, &buf[0], 256);
137-
while (n > 0) {
138-
int i = 0;
139-
while (i < n) {
140-
ion_string_push_byte(text, (unsigned char)(buf[i]));
141-
i = (i + 1);
142-
}
143-
n = read(fd, &buf[0], 256);
144-
}
145-
if (n < 0) {
146-
int _closed = close(fd);
147-
(void)_closed;
148-
ret_val = ReadResult_Err_new((-2));
149-
if (text) { ion_string_free(text); }
150-
if (path) { ion_string_free(path); }
151-
goto epilogue;
152-
}
153-
int _closed = close(fd);
154-
(void)_closed;
155-
ret_val = ReadResult_Ok_new(text);
156-
if (path) { ion_string_free(path); }
157-
goto epilogue;
158-
}
159-
if (path) { ion_string_free(path); }
160-
goto epilogue;
161-
epilogue:
162-
return ret_val;
163-
}
164-
165125
void fmt_print_int(int ION_MAYBE_UNUSED n) {
166126
io_print_int(n);
167127
goto epilogue;
@@ -229,6 +189,46 @@ ion_string_t* fmt_int_to_string(int ION_MAYBE_UNUSED n) {
229189
return ret_val;
230190
}
231191

192+
ReadResult fs_read_to_string_result(ion_string_t* ION_MAYBE_UNUSED path) {
193+
ReadResult ret_val = {0};
194+
{
195+
int fd = open(path->data, 0);
196+
if (fd < 0) {
197+
ret_val = ReadResult_Err_new((-1));
198+
if (path) { ion_string_free(path); }
199+
goto epilogue;
200+
}
201+
ion_string_t* text = ion_string_new();
202+
uint8_t buf[256] = {0};
203+
int n = read(fd, &buf[0], 256);
204+
while (n > 0) {
205+
int i = 0;
206+
while (i < n) {
207+
ion_string_push_byte(text, (unsigned char)(buf[i]));
208+
i = (i + 1);
209+
}
210+
n = read(fd, &buf[0], 256);
211+
}
212+
if (n < 0) {
213+
int _closed = close(fd);
214+
(void)_closed;
215+
ret_val = ReadResult_Err_new((-2));
216+
if (text) { ion_string_free(text); }
217+
if (path) { ion_string_free(path); }
218+
goto epilogue;
219+
}
220+
int _closed = close(fd);
221+
(void)_closed;
222+
ret_val = ReadResult_Ok_new(text);
223+
if (path) { ion_string_free(path); }
224+
goto epilogue;
225+
}
226+
if (path) { ion_string_free(path); }
227+
goto epilogue;
228+
epilogue:
229+
return ret_val;
230+
}
231+
232232
void io_print(ion_string_t* ION_MAYBE_UNUSED s) {
233233
{
234234
int _result = write(1, s->data, (int)s->len);

0 commit comments

Comments
 (0)