-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Copy elision causes side effects when modifying a struct in place. #4021
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Same issue as #3696 (although your code example is a better reduction than mine) |
I would say to support a solution to aliasing, force copies. Except I only see 2 possibilities, both with their own drawbacks. Maybe we need a way to mark param as "always-copy" ? drawback: forces callsites to pass addrpub fn mul(z1: *const Complex, z2: *const Complex) Complex {
const tmp_z1 = z1.*;
const tmp_z2 = z2.*;
return Complex {
.x = tmp_z1.x*tmp_z2.x - tmp_z1.y*tmp_z2.y,
.y = tmp_z1.x*tmp_z2.y + tmp_z1.y*tmp_z2.x,
};
} drawback: if zig chooses to pass-by-value it is a redundant copypub fn mul(z1: Complex, z2: Complex) Complex {
const tmp_z1 = z1;
const tmp_z2 = z2;
return Complex{
.x = tmp_z1.x * tmp_z2.x - tmp_z1.y * tmp_z2.y,
.y = tmp_z1.x * tmp_z2.y + tmp_z1.y * tmp_z2.x,
};
} |
Here are some examples that illustrate this problem: // 1. without a function
const Vec2 = struct { x: f32, y: f32 };
var v = Vec2{ .x = 4, .y = 6 };
v = .{ .x = -v.y, .y = v.x }; // sets v to (-6, -6)
// 2. with a function
fn rotate(v: Vec2) Vec2 {
return .{ .x = -v.y, .y = v.x };
}
var v = Vec2{ .x = 4, .y = 6 };
v = rotate(v); // sets v to (-6, -6)
// 3. with a union
const U = union(enum) { a: u32, b: u32 };
var u = U{ .a = 4 };
u = .{ .b = u.a }; // debug panic or UB, accesses bad field The problem is not limited to function calls, result location semantics plus struct initializers are enough to break things. |
We can solve this without modifying function code by making the copy at the callsite. |
Just got bitten by this as well, here's another example: const std = @import("std");
const Vector3 = struct {
x: f32,
y: f32,
z: f32,
};
fn vecFunction( vec: *Vector3 ) Vector3 {
return Vector3{
.x = vec.x * vec.y * vec.z,
.y = vec.x * vec.y * vec.z,
.z = vec.x * vec.y * vec.z,
};
}
pub fn main() void {
var vec = Vector3{ .x = 2.0, .y = 2.0, .z = 2.0 };
vec = vecFunction( &vec );
std.debug.print("{d}\n", .{vec});
} Expected to be |
This code attempts to square a complex number
i² -> -1
. When computingz = z.mul(z)
, the compiler decides to pass both valuesz1
andz2
by reference, however copy elision seems to modifyz
in place causing the incorrect value to be computed.Output:
Related issues: #287, #2765, #3804
The text was updated successfully, but these errors were encountered: