Return value optimization (RVO) for self-referential structs?

Context

We know that in Zig RVO is guaranteed when you construct a value directly in the return statement:

return .{ .my_data = 0 };

For self-referential structs, though, we might need extra setup. For now, I’m using this pattern:

/// Self-referential struct.
const DeviceEvaluator = struct {
    physical_device: vk.PhysicalDevice,
    dynamic_rendering_feats: vk.PhysicalDeviceDynamicRenderingFeaturesKHR,
    synchronization2_feats: vk.PhysicalDeviceSynchronization2FeaturesKHR,
    maintenance4_feats: vk.PhysicalDeviceMaintenance4FeaturesKHR,
    maintenance5_feats: vk.PhysicalDeviceMaintenance5FeaturesKHR,
    feats_12: vk.PhysicalDeviceVulkan12Features,
    feats: vk.PhysicalDeviceFeatures2,
    extensions: []vk.ExtensionProperties,
    queue_family_properties: []vk.QueueFamilyProperties,
    queue_family_index: u32,

    pub fn init(self: *@This()) void {
        self.dynamic_rendering_feats = .{};
        self.synchronization2_feats = .{ .p_next = &self.dynamic_rendering_feats };
        self.maintenance4_feats = .{ .p_next = &self.synchronization2_feats };
        self.maintenance5_feats = .{ .p_next = &self.maintenance4_feats };
        self.feats_12 = .{ .p_next = &self.maintenance5_feats };
        self.feats = .{ .p_next = &self.feats_12, .features = .{} };
        self.extensions = .{};
        self.queue_family_properties = .{};
    }
};

fn usageDemo(scratch: std.mem.Allocator) !void {
    var evaluator: DeviceEvaluator = undefined;
    evaluator.init();
    defer evaluator.drop(scratch);
}

Suggestion

This could be improved if Zig supports “get address of return value”.

I know we already have @returnAddress but it’s pointing to the next instruction, not the data returned.

This function returns the address of the next machine code instruction that will be executed when the current function returns.

Thread to Validity

Function inlining, call conventions… These make the exact location of a return value quite versatile.

While we can say “OK when you say inside the function that you want to grab the return address we’ll force the caller to expect return values on the stack”, it seems against the mantra of Zig being explicit with the least surprise, and being a systems programming language means that the location of a return value could be important for a caller.

An opponent could argue that the status quo has already been nondeterministic about where the return value is, so why not utilize it and make our life even better?

My personal take is that the feature is kind of nice-to-have but low priority. Keen to learn about anything I’m missing, though.

2765

1 Like