Why can't I resolve this comptime value?


const std = @import("std");

const TreeConfig = struct {
    name: []const u8,
    branches: []const TreeConfig = &.{},
};

pub fn ParsedTree(comptime tree: TreeConfig) type {
    const BranchTaggedUnion = blk: {
        var field_types: [tree.branches.len]type = undefined;
        var field_names: [tree.branches.len][]const u8 = undefined;
        inline for (&field_types, &field_names, tree.branches) |*field_type, *field_name, branch| {
            field_type.* = ParsedTree(branch);
            field_name.* = branch.name;
        }
        const field_attrs: [tree.branches.len]std.builtin.Type.UnionField.Attributes = @splat(.{});
        const bits = if (field_names.len != 0) std.math.log2_int_ceil(usize, field_names.len) else 0;
        const TagInt = @Int(.unsigned, bits);
        comptime var field_values: [field_names.len]TagInt = undefined;
        comptime for (0..field_names.len) |id| {
            field_values[id] = @intCast(id);
        };
        const E = @Enum(TagInt, .exhaustive, &field_names, &field_values);
        break :blk @Union(
            .auto,
            E,
            &field_names,
            &field_types,
            &field_attrs,
        );
    };

    return struct {
        branch: ?BranchTaggedUnion,
    };
}

inline fn descentPathRecursive(comptime accumulator: []const []const u8, comptime tree: TreeConfig, parsed: ParsedTree(tree)) []const []const u8 {
    const result = accumulator ++ [_][]const u8{tree.name};
    if (parsed.branch) |branch| {
        switch (branch) {
            inline else => |value, tag| {
                inline for (tree.branches) |branch_config| {
                    if (comptime std.mem.eql(u8, branch_config.name, @tagName(tag))) {
                        return descentPathRecursive(result, branch_config, value);
                    }
                }
            },
        }
    }
    return result;
}

test {
    const tree: TreeConfig = .{
        .name = "root",
        .branches = &.{
            .{ .name = "branch1" },
            .{ .name = "branch2" },
        },
    };
    var parsed: ParsedTree(tree) = .{ .branch = .{ .branch1 = .{ .branch = null } } };
    _ = &parsed;
    try std.testing.expectEqual(null, parsed.branch.?.branch1.branch);

    const descent_path = comptime descentPathRecursive(&.{}, tree, parsed);
    try std.testing.expectEqualSlices(
        []const u8,
        &.{ "root", "branch1" },
        descent_path,
    );
}
test7.zig:66:68: error: unable to resolve comptime value
    const descent_path = comptime descentPathRecursive(&.{}, tree, parsed);
                                                                   ^~~~~~
test7.zig:66:26: note: 'comptime' keyword forces comptime evaluation
    const descent_path = comptime descentPathRecursive(&.{}, tree, parsed);
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If I remove comptime from the test, the test passes. But if I have comptime in there it says I cannot resolve it. The return value of descentPathRecursive is only constructed from comptime known values, and it breaks the laws of physics (returns reference to local). Whats going on here? I think I should be generating all possible values for descentPath at comptime from my inline fors.

parsed is a runtime var

ok yeah its obvious to me now, I had a misunderstanding of what comptime means.

The table of all posible values from descentPathRecursive is comptime-known but which value is selected is not comptime-known and depends on runtime variable parsed.

so then its useless for descentPathRecursive to be inline because it doesn’t need to forward comptime-ness to the return value?