Skip to content

Commit 2554dcc

Browse files
committed
Neutralize moved-out match scrutinee payloads so enum drops cannot double-free.
Null variant union fields when pattern bindings take ownership, add cgen regression coverage for the test_fs_read shape, and regenerate example C snapshots where output changed.
1 parent f0abf31 commit 2554dcc

4 files changed

Lines changed: 232 additions & 53 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +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+
- **Match scrutinee move-out**: pattern payload bindings null `match_val_N.data.variant_*` fields when ownership transfers (`statement_match_payload_move_neutralizes_scrutinee`); whole-enum binding arms clear active variant payloads via `emit_match_scrutinee_whole_enum_moved_out`
1617
- **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`.
1718
- `Box`, `Vec`, `String` layout vs `runtime/ion_runtime.h`
1819
- Single-file merge (`merge_modules`) vs `--mode multi` divergences

examples/showcase.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ typedef struct Vec_int {
2727
size_t elem_size;
2828
} Vec_int;
2929

30-
typedef struct tuple_int_int {
31-
int f0;
32-
int f1;
33-
} tuple_int_int;
34-
3530
typedef struct tuple_ion_sender_t_ion_receiver_t {
3631
ion_sender_t f0;
3732
ion_receiver_t f1;
3833
} tuple_ion_sender_t_ion_receiver_t;
3934

35+
typedef struct tuple_int_int {
36+
int f0;
37+
int f1;
38+
} tuple_int_int;
39+
4040
typedef struct Point {
4141
int x;
4242
int y;

examples/text_summary/text_summary.c

Lines changed: 44 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-
4543
extern int open(uint8_t* path, int flags);
4644
extern int read(int fd, uint8_t* buf, int count);
4745
extern int close(int fd);
4846

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);
5152
void fmt_print_int(int ION_MAYBE_UNUSED n);
5253
void fmt_println_int(int ION_MAYBE_UNUSED n);
5354
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);
@@ -97,6 +97,7 @@ int main(void) {
9797
switch (match_val_0.tag) {
9898
case 0: { // Ok
9999
ion_string_t* content = match_val_0.data.variant_0.arg0;
100+
match_val_0.data.variant_0.arg0 = NULL;
100101
ion_string_t* text = content;
101102
int bytes = ((text) ? (int)((text)->len) : 0);
102103
TextCounts counts = summarize_text(text);
@@ -122,6 +123,46 @@ int main(void) {
122123
return ret_val;
123124
}
124125

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

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-
232233
void io_print(ion_string_t* ION_MAYBE_UNUSED s) {
233234
{
234235
int _result = write(1, s->data, (int)s->len);

0 commit comments

Comments
 (0)