m7_emit_call reimplements call emission and only carried over mem.destroy and
mem.replace, so every mem.create/mem.alloc_slice fell through to the generic
member path and emitted `fe_missing.create(0)`. wcc386 does not diagnose that
-- it terminates with exit 255, which wcl386 reports as "Unable to invoke
wcc386.exe" with no message at all.
The breakage covered all 17 allocation sites in m5/runtime.fe, and it was
invisible because m5-owned and m5-defer only emit C; m5-runtime is the one M5
fixture that compiles and links what was generated.
Also make runtime.fe legal: `run` used `try` while returning i32, which SPEC
allows only in a function returning an error union. It is `-> !i32` now, which
M7 accepts because contextual success construction lands with it, and
runtime.c takes the { error, value } struct the error union lowers to.
Found by calling wcc386 directly instead of through wcl386, which is the only
way to see a compiler crash here.
M1-M7: 183 passed.
101 lines
2.2 KiB
Plaintext
101 lines
2.2 KiB
Plaintext
unit m5_runtime;
|
|
|
|
fn take(p: ^i32) -> void { mem.destroy(p); }
|
|
|
|
pub fn run(mode: i32) -> !i32 {
|
|
var p: ^i32 = try mem.create(0);
|
|
defer { mem.destroy(p); }
|
|
p.^ = 7;
|
|
if mode == 1 {
|
|
p = try mem.create(0);
|
|
p.^ = 9;
|
|
return p.^;
|
|
}
|
|
while true { break; }
|
|
if mode == 2 { return 0; }
|
|
return p.^ - 7;
|
|
}
|
|
|
|
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(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 };
|
|
}
|