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:
agra
2026-05-25 12:49:26 +03:00
parent 8e21cc5f73
commit b263704664
6 changed files with 31 additions and 100 deletions

View File

@@ -1185,37 +1185,6 @@ pub const LLVMEmitter = struct {
}
self.advanceRefCounter();
},
.heap_alloc => |un| {
// malloc(size) → *void
const size = self.coerceArg(self.resolveRef(un.operand), self.sizeType());
const malloc_fn = self.getOrDeclareMalloc();
var args = [_]c.LLVMValueRef{size};
const result = c.LLVMBuildCall2(
self.builder,
self.getMallocType(),
malloc_fn,
&args,
1,
"heap",
);
self.mapRef(result);
},
.heap_free => |un| {
// free(ptr)
const ptr = self.resolveRef(un.operand);
const free_fn = self.getOrDeclareFree();
var args = [_]c.LLVMValueRef{ptr};
_ = c.LLVMBuildCall2(
self.builder,
self.getFreeType(),
free_fn,
&args,
1,
"",
);
self.advanceRefCounter();
},
// ── Globals ───────────────────────────────────────────
.global_get => |gid| {
const llvm_global = self.global_map.get(gid.index()) orelse {