ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
34.63k stars 2.53k forks source link

Proposal: Taking address of temporary value is disallowed. #4355

Open JesseRMeyer opened 4 years ago

JesseRMeyer commented 4 years ago

Both code examples compile and satisfy the type system but have subtly different semantics.

var d12_device: *ID3D12Device = undefined;
var result = D3D12CreateDevice(null, D3D_FEATURE_LEVEL_12_1, &IID_ID3D12Device, @ptrCast(**c_void, &d12_device));
var d12_device: *ID3D12Device = undefined;
var result = D3D12CreateDevice(null, D3D_FEATURE_LEVEL_12_1, &IID_ID3D12Device, &@ptrCast(*c_void, d12_device));

@ptrCast returns a temporary (unnamed) variable, so its address is likewise transient. I cannot think of a valuable use-case for taking its address, and have felt this as an (educational) footgun about how Zig works.

Is there a high value use-case for taking the address of a temporary variable?

If not, I propose this should be considered an error.

ikskuh commented 4 years ago

They main question that comes up is:

Is the following code undefined behaviour or does Zig guarantee that the expression value of try footgunnery(1) is alive until "end of sope"

const T = struct { val: i32 };

fn footgunnery(v: i32) !T {
    … // here be magic implementation
}

fn callme() !i32 {
    const ptr1 = &(try footgunnery(1)).val;
    const ptr2 = &(try footgunnery(2)).val;
    return ptr1.* + ptr2.*;
}

For function parameters there should be at least the rule that taking a mutable reference of a temporary is forbidden as i cannot imagine when it is not a bug.

Allowing a const reference to a temporary (even a literal) can sometimes come in handy when interfacing generic C apis:

extern fn setValue(type: MyTypeStruct, value: *const c_void) void;

fn foo() void {
    setValue(.i32, &as(i32, 42));
}
JesseRMeyer commented 4 years ago

Good point. The real danger comes from taking the address of temporary mutable references.

ghost commented 4 years ago

Footgun:

var opt: ?u32 = 4;

var ptr = &(opt orelse return);

ptr.* = 2;

std.debug.warn("{}\n", .{ opt }); // prints 4

Whereas var ptr = &opt.?; works. I'm not sure why.

ghost commented 3 years ago

Linking #7663 since it's at least somewhat related (taking address of struct literals).