Skip to content

eliminate hidden pass-by-reference footguns #5973

@ghost

Description

Accepted Proposal


Zig 0.6.0 (not master). This is related to, actually maybe a subset of, #4021 / #3696 (this issue doesn't involve result copy elision).

I understand that this was intended to be a feature of zig: args passed as values "may" be silently translated to pass-by-reference by the compiler. I think the intent was to stop the user from passing const pointers as an "optimization". But it's also a footgun, sort of like #2915. The problem occurs when you have another non-const pointer aliasing the same memory as the argument value.

const std = @import("std");

const Thing = struct {
    value: u32,
};

const State = struct {
    thing: Thing,
};

fn inner(state: *State, thing: Thing) void {
    std.debug.warn("before: {}\n", .{thing.value}); // prints 10

    state.thing.value = 0;

    std.debug.warn("after: {}\n", .{thing.value}); // prints 0
}

pub fn main() void {
    var state: State = .{
        .thing = .{ .value = 10 },
    };
    inner(&state, state.thing);
}

The behavior here depends on the compiler implementation. It seems that right now, if thing is a struct value, it's passed by reference. But if it's a bare u32, it's passed by value. I don't know if it will always be this simple (I assume there are plans to pass "small" structs by value.)

The workaround for this situation is to make an explicit copy using a redundant-seeming optimization, probably accompanied by a comment explaining what's going on. Or else to restructure the code at a higher level, but then this footgun will still be lurking in the shadows.

I think that any optimistic "assume no aliases" optimization ought to be opt-in rather than opt-out. That would mean, either go back to the C way of things, or add a new syntax (some symbol that means "compiler can choose between value and const pointer"). Either way, a plain argument should always be passed by value. What do others think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedThis proposal is planned.proposalThis issue suggests modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions