005 cross module type nullable

✓ Passing This code compiles and runs correctly.

Code

input.k

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