# Agent Solutions Guide: Zig 0.15 Issues This document catalogs common issues encountered when working with Zig 0.15 and their solutions. Written for AI coding assistants to avoid repeating mistakes. ## Critical: ArrayList API Changed in Zig 0.15 ### Problem `std.ArrayList` is now an alias to `std.ArrayListUnmanaged` in Zig 0.15. The managed version has been removed. ### Old (Pre-0.15) Code - DOES NOT WORK ```zig var list = std.ArrayList(u8).init(allocator); defer list.deinit(); try list.append(item); ``` ### New (0.15+) Code - CORRECT ```zig // Empty initialization var list = std.ArrayList(u8){}; defer list.deinit(allocator); try list.append(allocator, item); // Or with capacity var list = try std.ArrayList(u8).initCapacity(allocator, 100); defer list.deinit(allocator); try list.append(allocator, item); ``` ### Key Changes 1. **Initialization**: Use `{}` or `initCapacity()`, not `init()` 2. **All methods take allocator**: `append(allocator, item)` not `append(item)` 3. **Deinit takes allocator**: `deinit(allocator)` not `deinit()` ## AST Rendering API Changed ### Problem The `ast.render()` function signature changed in Zig 0.15. ### Old Code - DOES NOT WORK ```zig var ast = try std.zig.Ast.parse(allocator, source, .zig); const output = try ast.render(allocator); ``` ### New Code - CORRECT ```zig var ast = try std.zig.Ast.parse(allocator, source, .zig); const output = try ast.renderAlloc(allocator); defer allocator.free(output); ``` ### The API - `renderAlloc(allocator)` - Returns allocated string - `render(tree, gpa, writer, fixups)` - Low-level version for custom output ## Type Conversion: SDL Types to Zig ### Pointer Types | C Type | Zig Type | Notes | |--------|----------|-------| | `const char *` | `[*c]const u8` | C string | | `void *` | `?*anyopaque` | Nullable any pointer | | `const void *` | `?*const anyopaque` | Const version | | `SDL_Type *` | `?*Type` | Nullable pointer to opaque/struct | | `const SDL_Type *` | `*const Type` | Non-null const pointer | | `SDL_Type **` | `?*?*Type` | Output parameter (double pointer) | | `SDL_Type *const *` | `[*c]*const Type` | Array of const pointers | | `Uint32 *` | `*u32` | Output parameter (primitive) | ### Key Principles 1. **Non-nullable by default** for const pointers to structs 2. **Nullable (`?*`)** for pointers that can be NULL 3. **Use `*` not `[*c]`** when you know it's not a C-style array 4. **Double pointers**: `?*?*Type` for output parameters ## Function Signature Formatting ### Trailing Commas Only use trailing commas for functions with **more than 3 parameters**. This triggers multi-line formatting. ```zig // 1-3 parameters: single line, no trailing comma pub fn foo(a: i32, b: i32, c: i32) void {} // 4+ parameters: multi-line with trailing comma pub fn bar( a: i32, b: i32, c: i32, d: i32, ) void {} ``` ### Why? - Trailing comma with no parameters: `(,)` is **syntax error** - Trailing comma with 1-3 params: unnecessary, wastes vertical space - Trailing comma with 4+ params: makes diffs cleaner, easier to read ## Method Organization ### Place Methods Inside Opaque Types Functions where the first parameter is a pointer to an opaque type should be methods: ```zig // Good - method syntax pub const GPUDevice = opaque { pub fn destroy(device: *GPUDevice) void { c.SDL_DestroyGPUDevice(device); } }; // Usage: device.destroy() // Bad - standalone function pub fn destroyGPUDevice(device: ?*GPUDevice) void { c.SDL_DestroyGPUDevice(device); } // Usage: destroyGPUDevice(device) ``` ### Benefits 1. Cleaner API: `device.create()` vs `createGPUDevice(device)` 2. IDE autocomplete works better 3. Namespacing prevents naming conflicts 4. More idiomatic Zig ## Casting Guidelines ### When to Cast | Scenario | Cast | Example | |----------|------|---------| | Opaque pointer | `@ptrCast` | `@ptrCast(device)` | | Flags (packed struct) | `@bitCast` | `@bitCast(flags)` | | Enum to int | `@intFromEnum` | `@intFromEnum(enum_val)` | | Struct passed by value | None | Just pass it | | Const pointer to struct | `@ptrCast` | `@ptrCast(info)` | ### Don't Over-Cast ```zig // Bad - unnecessary cast for value type fn setColor(color: FColor) void { c.SDL_SetColor(@bitCast(color)); // Wrong! } // Good - no cast needed fn setColor(color: FColor) void { c.SDL_SetColor(color); // Correct } ``` ## StringHashMap Usage ### Correct Pattern ```zig var map = std.StringHashMap(ValueType).init(allocator); defer map.deinit(); // No allocator needed for deinit try map.put("key", value); const val = map.get("key"); ``` ### Iteration ```zig var it = map.keyIterator(); while (it.next()) |key| { // Use key.* } var it = map.valueIterator(); while (it.next()) |value| { // Use value.* if needed } ``` ## Common Pitfalls ### 1. Forgetting Allocator in Unmanaged Collections ```zig // Wrong list.append(item); // Right list.append(allocator, item); ``` ### 2. Using .init() on ArrayList ```zig // Wrong var list = std.ArrayList(u8).init(allocator); // Right var list = std.ArrayList(u8){}; // or var list = try std.ArrayList(u8).initCapacity(allocator, size); ``` ### 3. Not Checking AST Errors Before Rendering ```zig // Wrong - will panic if there are errors const output = try ast.renderAlloc(allocator); // Right - check first if (ast.errors.len > 0) { // Handle errors return error.ParseError; } const output = try ast.renderAlloc(allocator); ``` ### 4. Incorrect Double Pointer Types ```zig // Wrong - C-style for output params texture: [*c]*GPUTexture // Right - Zig optional pointers texture: ?*?*GPUTexture ``` ## Testing Patterns ### Simple Test ```zig test "description" { const result = try someFunction(); try std.testing.expectEqual(expected, result); } ``` ### Test with Allocator ```zig test "with allocator" { const allocator = std.testing.allocator; const result = try allocateAndDoSomething(allocator); defer allocator.free(result); try std.testing.expectEqualStrings("expected", result); } ``` ## Build System Integration ### Adding Parser to Dependencies ```zig // build.zig.zon .dependencies = .{ .sdl3_parser = .{ .path = "parser/" }, }, // build.zig const parser_dep = b.dependency("sdl3_parser", .{ .target = target, .optimize = optimize, }); const parser_exe = parser_dep.artifact("sdl-parser"); ``` ### Run Step ```zig const run_parser = b.addRunArtifact(parser_exe); run_parser.addFileArg(b.path("input.h")); run_parser.addArg("--output=output.zig"); const step = b.step("generate", "Generate bindings"); step.dependOn(&run_parser.step); ``` ## Quick Reference Card ```zig // Collections var list = std.ArrayList(T){}; defer list.deinit(allocator); try list.append(allocator, item); var map = std.StringHashMap(V).init(allocator); defer map.deinit(); try map.put("key", value); // AST var ast = try std.zig.Ast.parse(allocator, source, .zig); defer ast.deinit(allocator); const formatted = try ast.renderAlloc(allocator); defer allocator.free(formatted); // Type Patterns ?*Type // Nullable pointer *const Type // Non-null const pointer ?*?*Type // Output parameter [*c]*const Type // C array of const pointers // Casts @ptrCast(ptr) // Pointers @bitCast(value) // Packed structs, flags @intFromEnum(e) // Enum to int // No cast for value types! ``` ## Version Info - **Zig Version**: 0.15.2 - **Date**: 2025-01-22 - **SDL Version**: 3.2.0 ## Issues Encountered During Mock Testing Implementation ### Issue 1: Build.addStaticLibrary Removed **Problem**: Zig 0.15 removed `b.addStaticLibrary()` method. **Error**: ``` error: no field or member function named 'addStaticLibrary' in 'Build' ``` **Solution**: Use `b.addLibrary()` with `.linkage = .static`: ```zig // OLD - Does not work const lib = b.addStaticLibrary(.{ .name = "mylib", .target = target, .optimize = optimize, }); // NEW - Correct for Zig 0.15 const lib = b.addLibrary(.{ .name = "mylib", .linkage = .static, .root_module = b.createModule(.{ .target = target, .optimize = optimize, }), }); ``` **Key change**: Must create `root_module` explicitly with target/optimize. ### Issue 2: C Mock Type Definitions **Problem**: Generated C mocks referenced SDL types like `Uint32`, `SDL_Window`, `FColor` that weren't defined when using only stdint.h/stdbool.h. **Error**: ``` error: unknown type name 'Uint32' error: unknown type name 'SDL_GPUColorTargetInfo' ``` **Solution**: Include actual SDL headers in generated mocks: ```c // OLD - Missing types #include #include #include // NEW - Proper type definitions #include #include ``` Then add SDL include path to C compilation: ```zig mock_lib.addIncludePath(b.path("SDL/include")); ``` **Key insight**: Mocks should compile like real SDL implementation files, with full access to SDL type definitions. ### Issue 3: Testing Strategy **Problem**: Initial testing with tiny `test_small.h` (3 declarations) didn't reveal real-world issues. **Solution**: Test with full production header (SDL_gpu.h with 169 declarations) to: - Verify parser handles large inputs - Catch type definition issues - Validate all declaration types work together - Ensure build system scales **Lesson**: Always test with realistic, production-sized inputs, not toy examples. ## References - Zig 0.15 Release Notes: https://ziglang.org/download/0.15.0/release-notes.html - Zig Standard Library Docs: https://ziglang.org/documentation/master/std/