✓
Passing This code compiles and runs correctly.
Code
// Test: Cross-module type with nullable
// PRIORITY: Nullable prefix ?* should be separate from module path
//
// The subject is the DECLARATION parsing: `?*std/interpreter:HandlePool` must
// read as nullable-pointer-to-a-cross-module-type, not as a mangled module path.
// There is no flow here, so the program prints nothing — and the expectation is
// empty on purpose. It used to claim `done`, a line this test could never have
// produced, sitting in an `expected_output.txt` the harness never opened.
import std/interpreter
import std/io
pub tor test-nullable {
pool: ?*std/interpreter:HandlePool,
}
Actual
Expected output
✓ Zig✓ C#Emitted Zig source
// Access compiler flags via the per-user compiler_env module
const CompilerEnv = @import("compiler_env").CompilerEnv;
pub const panic = if (@import("builtin").mode == .Debug)
@import("std").debug.FullPanic(@import("std").debug.defaultPanic)
else
@import("std").debug.simple_panic;
const __koru_bare = struct {
extern fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;
extern fn free(ptr: ?*anyopaque) void;
fn bareAlloc(_: *anyopaque, len: usize, alignment: @import("std").mem.Alignment, _: usize) ?[*]u8 {
var p: ?*anyopaque = null;
const a = @max(alignment.toByteUnits(), @sizeOf(usize));
if (posix_memalign(&p, a, len) != 0) return null;
return @ptrCast(p);
}
fn bareResize(_: *anyopaque, _: []u8, _: @import("std").mem.Alignment, _: usize, _: usize) bool { return false; }
fn bareRemap(_: *anyopaque, _: []u8, _: @import("std").mem.Alignment, _: usize, _: usize) ?[*]u8 { return null; }
fn bareFree(_: *anyopaque, memory: []u8, _: @import("std").mem.Alignment, _: usize) void { free(@ptrCast(memory.ptr)); }
const vtable = @import("std").mem.Allocator.VTable{ .alloc = bareAlloc, .resize = bareResize, .remap = bareRemap, .free = bareFree };
const allocator = @import("std").mem.Allocator{ .ptr = undefined, .vtable = &vtable };
};
const __koru_backing = if (@import("builtin").link_libc) @import("std").heap.c_allocator else if (@import("builtin").os.tag == .freestanding and !@import("builtin").cpu.arch.isWasm()) __koru_bare.allocator else @import("std").heap.page_allocator;
var __koru_leak_count: @import("std").atomic.Value(usize) = .init(0);
var __koru_par_tasks: @import("std").atomic.Value(usize) = .init(0);
const __koru_par_CAP: usize = 128;
threadlocal var __koru_par_depth: usize = 0;
const __koru_par_DEPTH_CAP: usize = 7;
var __koru_par_pool: @import("std").Thread.Pool = undefined;
var __koru_par_pool_ready: bool = false;
var __koru_par_pool_mtx: @import("std").Thread.Mutex = .{};
fn __koru_par_pool_ensure() *@import("std").Thread.Pool {
__koru_par_pool_mtx.lock();
defer __koru_par_pool_mtx.unlock();
if (!__koru_par_pool_ready) {
@import("std").Thread.Pool.init(&__koru_par_pool, .{ .allocator = @import("std").heap.smp_allocator }) catch @panic("koru: thread pool init failed");
__koru_par_pool_ready = true;
}
return &__koru_par_pool;
}
fn __koru_alloc(ctx: *anyopaque, len: usize, alignment: @import("std").mem.Alignment, ret_addr: usize) ?[*]u8 {
_ = ctx;
const r = __koru_backing.rawAlloc(len, alignment, ret_addr);
if (comptime @import("builtin").mode == .Debug) {
if (r != null) _ = __koru_leak_count.fetchAdd(1, .monotonic);
}
return r;
}
fn __koru_resize(ctx: *anyopaque, memory: []u8, alignment: @import("std").mem.Alignment, new_len: usize, ret_addr: usize) bool {
_ = ctx;
return __koru_backing.rawResize(memory, alignment, new_len, ret_addr);
}
fn __koru_remap(ctx: *anyopaque, memory: []u8, alignment: @import("std").mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
_ = ctx;
return __koru_backing.rawRemap(memory, alignment, new_len, ret_addr);
}
fn __koru_free(ctx: *anyopaque, memory: []u8, alignment: @import("std").mem.Alignment, ret_addr: usize) void {
_ = ctx;
__koru_backing.rawFree(memory, alignment, ret_addr);
if (comptime @import("builtin").mode == .Debug) {
_ = __koru_leak_count.fetchSub(1, .monotonic);
}
}
const __koru_vtable = @import("std").mem.Allocator.VTable{ .alloc = __koru_alloc, .resize = __koru_resize, .remap = __koru_remap, .free = __koru_free };
pub fn koru_allocator() @import("std").mem.Allocator {
return .{ .ptr = undefined, .vtable = &__koru_vtable };
}
pub inline fn __koru_intcast(comptime T: type, x: anytype) T {
if (comptime (@import("builtin").mode == .Debug or @import("builtin").mode == .ReleaseSafe))
return @as(T, @intCast(x));
const dst = @typeInfo(T);
const src = @typeInfo(@TypeOf(x));
if (comptime (dst == .int and src == .int and dst.int.bits == src.int.bits and dst.int.signedness != src.int.signedness))
return @as(T, @bitCast(x));
return @as(T, @intCast(x));
}
fn __KoruPadI(comptime T: type) type {
const V = switch (@typeInfo(T)) { .comptime_int => i64, .comptime_float => f64, else => T };
return struct {
v: V,
width: usize,
pub fn format(self: @This(), w: *@import("std").Io.Writer) @import("std").Io.Writer.Error!void {
const rendered = @import("std").fmt.count("{d}", .{self.v});
const pad = self.width -| rendered;
for (0..pad) |_| try w.writeByte(' ');
try w.print("{d}", .{self.v});
}
};
}
pub fn __koru_pad_i(width: usize, value: anytype) __KoruPadI(@TypeOf(value)) {
return .{ .v = value, .width = width };
}
pub fn koru_leak_check() void {
if (comptime @import("builtin").mode != .Debug) return;
if (__koru_leak_count.load(.acquire) == 0) return;
if (comptime @import("builtin").target.os.tag == .freestanding) {
if (comptime @import("builtin").cpu.arch == .wasm32 or @import("builtin").cpu.arch == .wasm64) {
@panic("KORU LEAK CHECK FAILED: the produced program leaked");
} else {
const __klc = struct { extern var stdout: ?*anyopaque; extern fn fputs(__s: [*:0]const u8, __st: ?*anyopaque) c_int; };
var __lb: [128]u8 = undefined;
const __lm = "KORU LEAK CHECK FAILED: allocations still outstanding at end of run: ";
@memcpy(__lb[0..__lm.len], __lm);
var __ln: usize = __lm.len;
var __lv = __koru_leak_count.load(.acquire);
var __ld: [20]u8 = undefined;
var __lk: usize = 0;
while (__lv > 0) : (__lk += 1) { __ld[__lk] = @intCast('0' + __lv % 10); __lv /= 10; }
for (0..__lk) |__li| { __lb[__ln] = __ld[__lk - 1 - __li]; __ln += 1; }
__lb[__ln] = '\n'; __ln += 1; __lb[__ln] = 0;
_ = __klc.fputs(@as([*:0]const u8, @ptrCast(&__lb)), __klc.stdout);
@trap();
}
} else {
@import("std").debug.print("KORU LEAK CHECK FAILED: the produced program leaked (trace above)\n", .{});
@import("std").process.exit(1);
}
}
pub const main_module = struct {
pub fn koru_start_flow() void {
const result_0 = koru_koru.start_event.handler(.{ });
const result_0_done = result_0.done;
_ = &result_0_done;
}
pub fn koru_end_flow() void {
const result_0 = koru_koru.end_event.handler(.{ });
const result_0_done = result_0.done;
_ = &result_0_done;
}
};
pub const koru_std = struct {
pub const koru_interpreter = struct {
const std = @import("std");
const ast = @import("ast");
const koru_parser = @import("koru_parser");
const flow_parser = @import("flow_parser");
pub const FieldValue = union(enum) {
string_val: []const u8,
int_val: i64,
float_val: f64,
bool_val: bool,
string_array: []const []const u8,
};
pub const NamedField = struct {
name: []const u8,
value: FieldValue,
};
pub const Value = struct {
branch: []const u8, // Which branch was taken/constructed
fields: []const NamedField, // Structured fields (typed from dispatcher)
pub fn empty(branch_name: []const u8) Value {
return .{
.branch = branch_name,
.fields = &[_]NamedField{},
};
}
pub fn fromDispatch(branch_name: []const u8, fields: []const NamedField) Value {
return .{
.branch = branch_name,
.fields = fields,
};
}
pub fn toJsonAlloc(self: Value, allocator: std.mem.Allocator) []const u8 {
var out = std.ArrayListUnmanaged(u8){};
self.writeJson(out.writer(allocator)) catch @panic("koru: OOM serializing value");
return out.toOwnedSlice(allocator) catch @panic("koru: OOM serializing value");
}
pub fn toJsonBuf(self: Value) []const u8 {
return self.toJsonAlloc(std.heap.page_allocator);
}
fn writeJson(self: Value, w: anytype) !void {
try w.writeAll("{\"branch\":\"");
try w.writeAll(self.branch);
try w.writeByte('"');
if (self.fields.len == 1 and std.mem.eql(u8, self.fields[0].name, "__type_ref")) {
try w.writeAll(",\"value\":");
try writeFieldValue(w, self.fields[0].value);
} else if (self.fields.len > 0) {
try w.writeAll(",\"fields\":{");
for (self.fields, 0..) |f, i| {
if (i > 0) try w.writeByte(',');
try w.writeByte('"');
try w.writeAll(f.name);
try w.writeAll("\":");
try writeFieldValue(w, f.value);
}
try w.writeByte('}');
}
try w.writeByte('}');
}
fn writeFieldValue(w: anytype, fv: FieldValue) !void {
switch (fv) {
.int_val => |v| try std.fmt.format(w, "{d}", .{v}),
.float_val => |v| try std.fmt.format(w, "{d}", .{v}),
.bool_val => |v| try w.writeAll(if (v) "true" else "false"),
.string_val => |v| try writeJsonString(w, v),
.string_array => |arr| {
try w.writeByte('[');
for (arr, 0..) |v, i| {
if (i > 0) try w.writeByte(',');
try writeJsonString(w, v);
}
try w.writeByte(']');
},
}
}
pub fn clonePersistent(self: Value) Value {
const S = struct {
threadlocal var inited: bool = false;
threadlocal var arena: std.heap.ArenaAllocator = undefined;
};
if (!S.inited) {
S.arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
S.inited = true;
}
_ = S.arena.reset(.retain_capacity);
const allocator = S.arena.allocator();
const branch_copy = allocator.dupe(u8, self.branch) catch @panic("koru: OOM cloning value");
const field_copy = allocator.alloc(NamedField, self.fields.len) catch @panic("koru: OOM cloning value");
for (self.fields, 0..) |f, i| {
const name_copy = allocator.dupe(u8, f.name) catch @panic("koru: OOM cloning value");
const value_copy = switch (f.value) {
.string_val => |s| FieldValue{ .string_val = allocator.dupe(u8, s) catch @panic("koru: OOM cloning value") },
.string_array => |arr| FieldValue{ .string_array = dupeStrs(allocator, arr) },
.bool_val => |b| FieldValue{ .bool_val = b },
.int_val => |n| FieldValue{ .int_val = n },
.float_val => |fl| FieldValue{ .float_val = fl },
};
field_copy[i] = .{ .name = name_copy, .value = value_copy };
}
return .{ .branch = branch_copy, .fields = field_copy };
}
};
pub const BudgetState = struct {
limit: ?u64,
used: u64,
last_event: []const u8,
pub fn init(limit: ?u64) BudgetState {
return .{
.limit = limit,
.used = 0,
.last_event = "",
};
}
pub fn consume(self: *BudgetState, cost: u64, event_name: []const u8) bool {
if (self.limit) |max| {
if (self.used + cost > max) {
return false; // Would exceed budget
}
}
self.used += cost;
self.last_event = event_name;
return true;
}
pub fn remaining(self: *const BudgetState) ?u64 {
if (self.limit) |max| {
return max - self.used;
}
return null; // Unlimited
}
};
pub const DispatchResult = struct {
branch: []const u8,
fields: []const NamedField, // Direct typed field values
};
pub const InterpreterContext = struct {
env: *Environment,
expr_bindings: *ExprBindings, // For expression evaluation in ~if
allocator: std.mem.Allocator,
dispatcher: ?*const fn(*const ast.Invocation, *DispatchResult) anyerror!void,
cost_fn: ?*const fn([]const u8) u64,
creates_obligations_fn: ?ObligationsArrayFn, // tor -> []obligations it creates
discharges_obligations_fn: ?ObligationsArrayFn, // tor -> []obligations it discharges
discharge_event_fn: ?DischargeEventFn, // obligation -> tor that discharges it
creates_spec_fn: ?CreatesSpecFn, // tor -> []CreateSpec
discharges_spec_fn: ?DischargesSpecFn, // tor -> []DischargeSpec
budget: ?*BudgetState,
handle_pool: ?*HandlePool,
scope_name: []const u8,
defined: ?*DefinedFlows,
defined_depth: u32 = 0,
input_fn: ?InputLookupFn = null,
host_dispatch: ?HostDispatchFn = null,
session_env: ?*Environment = null,
turn_env: ?*Environment = null,
unhandled_outcome: ?Value = null,
error_detail: ?[]const u8 = null,
failing_event_name: ?[]const u8 = null,
trace: std.ArrayListUnmanaged(TraceEntry) = .{},
session_bind_fn: ?SessionBindFn = null,
session_bind_env: ?*Environment = null,
session_bind_counter: ?*u64 = null,
mounted: ?[]const @import("root").koru_std.koru_runtime.ScopeRuntime = null,
};
pub const InterpreterThreadState = struct {
threadlocal var inited: bool = false;
threadlocal var arena: std.heap.ArenaAllocator = undefined;
threadlocal var env: Environment = undefined;
threadlocal var expr_bindings: ExprBindings = undefined;
pub fn prepare() std.mem.Allocator {
if (!inited) {
arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
env = Environment.init(std.heap.page_allocator);
expr_bindings = ExprBindings.init(std.heap.page_allocator);
inited = true;
}
_ = arena.reset(.retain_capacity);
if (env.bindings.count() != 0) env.clear();
if (expr_bindings.values.count() != 0) expr_bindings.clear();
__dispatch_detail = null;
return arena.allocator();
}
};
pub const HostTilde = struct {
line: u32,
column: u32,
};
pub fn findHostTilde(source: []const u8) ?HostTilde {
var line: u32 = 1;
var column: u32 = 1;
var in_string = false;
var escaped = false;
for (source) |c| {
if (escaped) {
escaped = false;
} else if (in_string and c == '\\') {
escaped = true;
} else if (c == '"') {
in_string = !in_string;
} else if (c == '~' and !in_string) {
return .{ .line = line, .column = column };
}
if (c == '\n') {
line += 1;
column = 1;
} else {
column += 1;
}
}
return null;
}
fn unboundRefName(raw: []const u8, bound: *const std.StringHashMap(void), allocator: std.mem.Allocator) ?[]const u8 {
const t = std.mem.trim(u8, raw, " \t");
if (t.len == 0 or t[0] == '"') return null;
const base = if (std.mem.indexOfScalar(u8, t, '.')) |dot| t[0..dot] else t;
if (isWireIdent(base)) {
if (std.mem.eql(u8, base, "true") or std.mem.eql(u8, base, "false") or std.mem.eql(u8, base, "null")) return null;
if (!bound.contains(base)) return base;
}
return wireExprRefName(t, bound, allocator);
}
fn isWireIdent(t: []const u8) bool {
if (t.len == 0) return false;
if (!std.ascii.isAlphabetic(t[0]) and t[0] != '_') return false;
for (t[1..]) |c| {
if (!std.ascii.isAlphanumeric(c) and c != '_' and c != '-') return false;
}
return true;
}
fn exprArithmeticOp(left: ExprValue, right: ExprValue, op: ExprArithOp) ExprEvalError!ExprValue {
return switch (left) {
.int_val => |l| switch (right) {
.int_val => |r| exprIntOp(l, r, op),
.float_val => |r| exprFloatOp(@as(f64, @floatFromInt(l)), r, op),
else => ExprEvalError.TypeMismatch,
},
.float_val => |l| switch (right) {
.int_val => |r| exprFloatOp(l, @as(f64, @floatFromInt(r)), op),
.float_val => |r| exprFloatOp(l, r, op),
else => ExprEvalError.TypeMismatch,
},
else => ExprEvalError.TypeMismatch,
};
}
fn exprIntOp(l: i64, r: i64, op: ExprArithOp) ExprEvalError!ExprValue {
return .{ .int_val = switch (op) {
.add => l + r,
.sub => l - r,
.mul => l * r,
.div => if (r == 0) return ExprEvalError.DivisionByZero else @divTrunc(l, r),
.mod => if (r == 0) return ExprEvalError.DivisionByZero else @mod(l, r),
} };
}
fn exprFloatOp(l: f64, r: f64, op: ExprArithOp) ExprEvalError!ExprValue {
return .{ .float_val = switch (op) {
.add => l + r,
.sub => l - r,
.mul => l * r,
.div => if (r == 0.0) return ExprEvalError.DivisionByZero else l / r,
.mod => @mod(l, r),
} };
}
pub fn checkShadowedArms(
conts: []const ast.Continuation,
allocator: std.mem.Allocator,
) ?ValidationError {
for (conts, 0..) |*arm, i| {
if (arm.is_transformed_subtree) continue;
if (armShadowable(arm)) {
for (conts[0..i]) |*prior| {
if (!armShadowable(prior)) continue;
if (!namesEql(prior.branch, arm.branch)) continue;
return .{
.message = if (arm.branch.len == 0)
"unreachable '|>' step — an earlier unguarded unnamed step at this level always fires first; consecutive '|>' lines continue one chain only when nothing sits between them"
else
std.fmt.allocPrint(allocator, "unreachable '{s}' arm — an earlier unguarded '{s}' sibling answers that outcome and first match wins; indent the arm under the step it follows to chain deeper, or 'when'-guard all but one same-bran
... [truncated - 187KB total]Emitted C# source
static class main_module {
public static void __koru_stdout_write(object s) => global::System.Console.Out.Write(s);
public static void __koru_stderr_write(object s) => global::System.Console.Error.Write(s);
// The mutable handle carrier — `*String`-style resources are
// `new __KoruBox { data = … }` because C# anonymous types are
// read-only: `s.data = …` (std/string append/clear, handle
// mutation generally) needs a settable member. `object`, not
// `dynamic`: the field is storage — every `x.data` read reaches
// it through a dynamic-typed handle (`*T` Input fields, the
// `dynamic` borrow locals below), so the member itself only
// needs to hold the value, and `object` keeps the DLR out of
// the store/load path.
public class __KoruBox { public object data; }
// Textification for `{{ … }}` operands: C# bool ToStrings as
// `True` where Koru prints `true`, and the operand's static type
// is unknown at this boundary — a `(x) is bool` inline test would
// be a compile error on statically-typed operands instead. Generic
// on purpose: `dynamic` boxed every value-type operand — measured
// ~1s/10M elements on 012_threat_scanner — while T specializes to
// the operand's own ToString() with no box.
public static string __koru_str<T>(T v) => v is bool b ? (b ? "true" : "false") : v?.ToString();
public static class koru_start_event {
public struct Input {
}
public struct Output {
public string tag;
public object done;
}
public static Output handler(Input __koru_input) => new Output { tag = "done" };
}
public static class koru_end_event {
public struct Input {
}
public struct Output {
public string tag;
public object done;
}
public static Output handler(Input __koru_input) => new Output { tag = "done" };
}
public static void flow0() {
main_module.koru_start_event.handler(new koru_start_event.Input { });
}
public static void flow1() {
main_module.koru_end_event.handler(new koru_end_event.Input { });
}
}
static class Program {
static void Main() {
main_module.flow0();
main_module.flow1();
}
}
Test Configuration
MUST_RUN LANGUAGES: zig cs