feat: complete M5 ownership and cleanup

This commit is contained in:
2026-08-16 18:48:35 +09:00
parent 351e5dbb23
commit deafd27939
13 changed files with 545 additions and 81 deletions
+13
View File
@@ -0,0 +1,13 @@
unit m5_bad_consuming_close;
struct FileLike {
handle: i32,
fn close(self: Self) -> !void { self.handle = 0; }
fn drop(self: &mut Self) { self.handle = 0; }
}
fn bad() -> !void {
let file: FileLike = FileLike{ handle: 7 };
try file.close();
file.close();
}
+7
View File
@@ -0,0 +1,7 @@
unit m5_bad_loop_move;
fn take(p: ^i32) -> void { mem.destroy(p); }
fn bad(p: ^i32, again: bool) -> void {
while again { take(p); }
}
+8
View File
@@ -0,0 +1,8 @@
unit m5_bad_projection_move;
struct Holder { p: ^i32 }
fn take(p: ^i32) -> void { mem.destroy(p); }
fn bad(h: Holder) -> void {
take(h.p);
}
+2 -2
View File
@@ -1,8 +1,8 @@
unit m5_owned;
fn main() -> void {
var p: ^i32 = try mem.create(i32);
p = try mem.create(i32);
var p: ^i32 = try mem.create(0);
p = try mem.create(0);
p.^ = 7;
let value: i32 = p.^;
defer { mem.destroy(p); }
+55 -9
View File
@@ -4,21 +4,43 @@
#undef malloc
#undef free
extern void *malloc(size_t size);
extern void free(void *p);
extern long fe_m5_runtime_run(long mode);
extern void fe_m5_runtime_conditional(unsigned char flag);
extern void fe_m5_runtime_argument_cleanup(void);
extern unsigned short fe_m5_runtime_conditional(unsigned char flag);
extern unsigned short fe_m5_runtime_argument_cleanup(void);
extern unsigned short fe_m5_runtime_owned_slice(unsigned long n);
extern unsigned short fe_m5_runtime_replace_field(void);
extern unsigned short fe_m5_runtime_loop_cleanup(void);
extern unsigned short fe_m5_runtime_try_cleanup(void);
extern unsigned short fe_m5_runtime_field_order(void);
extern unsigned short fe_m5_runtime_defer_order(void);
extern unsigned short fe_m5_runtime_match_cleanup(unsigned char flag);
extern unsigned short fe_m5_runtime_close_once(void);
extern unsigned short fe_m5_runtime_reassign_struct(void);
static void *live_ptrs[64];
static unsigned live_count;
static unsigned alloc_count;
static unsigned free_count;
static unsigned double_free_count;
static long fail_after = -1;
static unsigned malloc_attempts;
static int track_order;
static void *order_ptrs[2];
static unsigned order_allocs;
static unsigned order_frees;
static unsigned order_bad;
void *m5_malloc(size_t size)
{
void *p = malloc(size);
void *p;
if (fail_after >= 0 && (long)malloc_attempts++ == fail_after) return 0;
p = malloc(size);
if (p && live_count < 64) live_ptrs[live_count++] = p;
if (p) ++alloc_count;
if (p && track_order && order_allocs < 2) order_ptrs[order_allocs++] = p;
return p;
}
@@ -28,6 +50,9 @@ void m5_free(void *p)
if (!p) return;
for (i = 0; i < live_count; ++i) {
if (live_ptrs[i] == p) {
if (track_order && order_frees < 2 &&
p != order_ptrs[1-order_frees]) ++order_bad;
if (track_order && order_frees < 2) ++order_frees;
live_ptrs[i] = live_ptrs[--live_count];
++free_count;
free(p);
@@ -42,11 +67,32 @@ int main(void)
if (fe_m5_runtime_run(0) != 0) return 1;
if (fe_m5_runtime_run(1) != 9) return 2;
if (fe_m5_runtime_run(2) != 0) return 3;
fe_m5_runtime_conditional(0);
fe_m5_runtime_conditional(1);
fe_m5_runtime_argument_cleanup();
if (double_free_count != 0) return 4;
if (live_count != 0) return 5;
if (alloc_count != free_count) return 6;
if (fe_m5_runtime_conditional(0) != 0) return 4;
if (fe_m5_runtime_conditional(1) != 0) return 5;
if (fe_m5_runtime_argument_cleanup() != 0) return 6;
if (fe_m5_runtime_owned_slice(17) != 0) return 7;
if (fe_m5_runtime_replace_field() != 0) return 8;
if (fe_m5_runtime_loop_cleanup() != 0) return 9;
fail_after=1;
malloc_attempts=0;
if (fe_m5_runtime_try_cleanup() == 0) return 10;
fail_after=-1;
track_order=1;
order_allocs=order_frees=order_bad=0;
if (fe_m5_runtime_field_order() != 0) return 11;
track_order=0;
if (order_allocs != 2 || order_frees != 2 || order_bad != 0) return 12;
track_order=1;
order_allocs=order_frees=order_bad=0;
if (fe_m5_runtime_defer_order() != 0) return 13;
track_order=0;
if (order_allocs != 2 || order_frees != 2 || order_bad != 0) return 14;
if (fe_m5_runtime_match_cleanup(0) != 0) return 15;
if (fe_m5_runtime_match_cleanup(1) != 0) return 16;
if (fe_m5_runtime_close_once() != 0) return 17;
if (fe_m5_runtime_reassign_struct() != 0) return 18;
if (double_free_count != 0) return 19;
if (live_count != 0) return 20;
if (alloc_count != free_count) return 21;
return 0;
}
+79 -6
View File
@@ -3,11 +3,11 @@ unit m5_runtime;
fn take(p: ^i32) -> void { mem.destroy(p); }
pub fn run(mode: i32) -> i32 {
var p: ^i32 = try mem.create(i32);
var p: ^i32 = try mem.create(0);
defer { mem.destroy(p); }
p.^ = 7;
if mode == 1 {
p = try mem.create(i32);
p = try mem.create(0);
p.^ = 9;
return p.^;
}
@@ -16,12 +16,85 @@ pub fn run(mode: i32) -> i32 {
return p.^ - 7;
}
pub fn conditional(flag: bool) -> void {
var p: ^i32 = try mem.create(i32);
pub fn conditional(flag: bool) -> !void {
var p: ^i32 = try mem.create(0);
if flag { take(p); }
}
pub fn argument_cleanup() -> void {
let p: ^i32 = try mem.create(i32);
pub fn argument_cleanup() -> !void {
let p: ^i32 = try mem.create(0);
take(p);
}
pub fn owned_slice(n: usize) -> !void {
let bytes: ^[]u8 = try mem.alloc_slice(u8, n);
}
struct Holder { p: ^i32 }
pub fn replace_field() -> !void {
let first: ^i32 = try mem.create(1);
var h: Holder = Holder{ p: first };
let second: ^i32 = try mem.create(2);
let old: ^i32 = mem.replace(&mut h.p, second);
mem.destroy(old);
}
pub fn loop_cleanup() -> !void {
var i: i32 = 0;
while i < 2 {
let p: ^i32 = try mem.create(i);
i += 1;
if i == 1 { continue; }
break;
}
}
pub fn try_cleanup() -> !void {
let first: ^i32 = try mem.create(1);
let second: ^i32 = try mem.create(2);
}
struct PairOwners { first: ^i32, second: ^i32 }
pub fn field_order() -> !void {
let first: ^i32 = try mem.create(1);
let second: ^i32 = try mem.create(2);
let pair: PairOwners = PairOwners{ first: first, second: second };
}
pub fn defer_order() -> !void {
let first: ^i32 = try mem.create(1);
defer { mem.destroy(first); }
let second: ^i32 = try mem.create(2);
}
enum Choice { A, B }
pub fn match_cleanup(flag: bool) -> !void {
var choice: Choice = Choice.A;
if flag { choice = Choice.B; }
let p: ^i32 = try mem.create(1);
match choice {
A => { take(p); }
B => { take(p); }
}
}
struct FileLike {
handle: i32,
fn close(self: Self) -> !void { self.handle = 0; }
fn drop(self: &mut Self) { self.handle = 0; }
}
pub fn close_once() -> !void {
let file: FileLike = FileLike{ handle: 7 };
try file.close();
}
pub fn reassign_struct() -> !void {
let first: ^i32 = try mem.create(1);
var owner: Holder = Holder{ p: first };
let second: ^i32 = try mem.create(2);
owner = Holder{ p: second };
}