mem: delete .heap_alloc/.heap_free IR ops + the silent libc-malloc escape
allocViaContext used to fall back to a direct `.heap_alloc` (libc
malloc) when `Context` wasn't registered — i.e. when the program
didn't import std.sx. That was a silent escape hatch: a program could
appear to allocate fine without a `Context`, sidestepping protocol
dispatch entirely. Same shape as the matchContextAllocCall trap we
removed, just in a different code path.
Now: every site that needs `Context` emits a clear diagnostic when
the type isn't in scope, pointing the user at the required import.
- `allocViaContext`: the three fallback branches (no implicit_ctx, no
Context type, malformed Context struct) all call the new
`diagnoseMissingContext("heap allocation")` and return a
placeholder. Codegen no longer emits libc malloc as the silent
no-import path.
- `lowerPush`: the no-Context branches used to silently drop the
push and just lower the body. Now diagnose first, then lower
(keeping the body's other diagnostics flowing).
- `lowerIdentifier` for "context": used to silently fall through to
`global_names.get("context")` (which would emit an unresolved
identifier with no actionable hint). Now diagnose with the
required-import message.
With every consumer gone, the `.heap_alloc` and `.heap_free` IR ops
are deleted entirely:
- `inst.zig`: drop the Op variants.
- `interp.zig`: drop the execInst arms.
- `emit_llvm.zig`: drop the arms (the `getOrDeclareMalloc/Free`
helpers stay — they're still used by the foreign-decl path for
user-level `malloc`/`free` foreign bindings).
- `print.zig`: drop the printers + the isVoidOp arm.
- `emit_llvm.test.zig`: drop the unit test (op no longer exists).
155/155 example tests pass. Unit tests green. Chess green on macOS /
iOS sim / Android. A program that doesn't import std.sx and tries to
use `context.allocator.alloc` or `push Context.{}` or the `context`
identifier now gets a real error:
error: heap allocation requires the Context type — add
`#import "modules/std.sx";` (or a module that imports it)
Closes the last silent allocation-protocol escape.
This commit is contained in:
@@ -243,36 +243,6 @@ test "emit: comparison and branch" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "br i1") != null);
|
||||
}
|
||||
|
||||
test "emit: heap_alloc and heap_free" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
var b = Builder.init(&module);
|
||||
|
||||
// func f() -> void { p = malloc(64); free(p); }
|
||||
_ = b.beginFunction(str(&module, "heapfn"), &.{}, .void);
|
||||
const entry = b.appendBlock(str(&module, "entry"), &.{});
|
||||
b.switchToBlock(entry);
|
||||
|
||||
const size = b.constInt(64, .s64);
|
||||
const ptr_ty = module.types.ptrTo(.void);
|
||||
const ptr = b.emit(.{ .heap_alloc = .{ .operand = size } }, ptr_ty);
|
||||
b.emit(.{ .heap_free = .{ .operand = ptr } }, .void);
|
||||
b.retVoid();
|
||||
b.finalize();
|
||||
|
||||
var emitter = LLVMEmitter.init(alloc, &module, "test_heap", .{});
|
||||
defer emitter.deinit();
|
||||
emitter.emit();
|
||||
|
||||
try std.testing.expect(emitter.verify());
|
||||
|
||||
const ir_str = emitter.dumpToString();
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "malloc") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "free") != null);
|
||||
}
|
||||
|
||||
test "emit: function call" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
|
||||
Reference in New Issue
Block a user