@IntegratedQuantum to the rescue. He has Schrödinger equation on his github page
Yes, in practice it may be fine. But it is also a perceptional problem. And perception can be as important as reality, especially for a new language fighting for adoption. In Zig (as in C) function arguments have a lot in common with local variables. Both can be on stack or in registers. But local variables have stable addresses and for function arguments (&arg != &arg).
I am trying to be a Zig advocate and evangelist at my company. When talking to experienced C/C++ developers with little or no Zig exposure they like the premise of Zig (especially comptime, build system and embedded C/C++ toolchain). But when I mention a caveat that for function arguments passed by value (&arg != &arg)
the conversation derails. I am not sure how many of them would ever take address of the function parameter in real code (most likely never) but it is a significant perceptional red flag. I think it would be easier to either (A) make fn-parameter address stable or (B) ban taking the address altogether. Current behavior is a bit strange and hurts Zig adoption among those with strong C/C++ heritage.
I’m all for transparency, but you could leave this detail out until they’re hooked in the language.
It does not matter how an argument is passed, as a copy or via a pointer,
this “bug / implementation feature” is still the same:
const std = @import("std");
fn sum(a: *const i32, b: *const i32) i32 {
std.debug.print("&a = {*}\n", .{&a});
std.debug.print("&a = {*}\n", .{&a});
std.debug.print("&b = {*}\n", .{&a});
std.debug.print("&b = {*}\n", .{&a});
return a.* + b.*;
}
pub fn main() void {
const a: i32 = 3;
const b: i32 = 4;
std.debug.print("{}\n", .{sum(&a, &b)});
}
g$ zig run arg_adr.zig
&a = *const i32@7ffc3c30f310
&a = *const i32@7ffc3c30f320
&b = *const i32@7ffc3c30f330
&b = *const i32@7ffc3c30f340
7
Just to clarify something, are you under the impression that &arg != &arg
is an explicit design decision? It’s not. Although, in this topic, we have talked about other things, like Carbon language and stable pointers, this is just us making conversation about parameter passing in general. Returning to the topic at hand, &arg != &arg
is 100% a compiler bug. If anything, it’s inneficient, which is reason enough to fix it. I’m willing to bet the fix will be just one line of code. Someone probably just forgot to write parameterAlreadyOnStack = true
.
If you live in the multiverse where people would ask if Zig has stable pointers to function parameters, just answer:
Yes, it does! (Not in this particular moment, due to a tiny little compiler bug, but it’ll get fixed).
OOPS Beg my pardons for this quick copy-paste in the example:
std.debug.print("&b = {*}\n", .{&a});
std.debug.print("&b = {*}\n", .{&a});
btw, why addresses in question are different between sequential runs?!?!?!
@mono:~/2-coding/zig-lang$ zig run arg_adr.zig
&a = *const i32@7ffd06b5e1d0
&a = *const i32@7ffd06b5e1e0
&b = *const i32@7ffd06b5e1f0
&b = *const i32@7ffd06b5e200
7
@mono:~/2-coding/zig-lang$ zig run arg_adr.zig
&a = *const i32@7fff41e5d400
&a = *const i32@7fff41e5d410
&b = *const i32@7fff41e5d420
&b = *const i32@7fff41e5d430
7
@mono:~/2-coding/zig-lang$ zig run arg_adr.zig
&a = *const i32@7ffd1518e4f0
&a = *const i32@7ffd1518e500
&b = *const i32@7ffd1518e510
&b = *const i32@7ffd1518e520
I just run that several times once and again… WTF is going on?..
same “function arguments’ addresses volatile behavior” also happens when running one and the same ready-to-run-executable:
@mono:~/2-coding/zig-lang$ zig build-exe arg_adr.zig
@mono:~/2-coding/zig-lang$ ./arg_adr
&a = *const i32@7ffcc991a3c0
&a = *const i32@7ffcc991a3d0
&b = *const i32@7ffcc991a3e0
&b = *const i32@7ffcc991a3f0
7
@mono:~/2-coding/zig-lang$ ./arg_adr
&a = *const i32@7fffe40a89a0
&a = *const i32@7fffe40a89b0
&b = *const i32@7fffe40a89c0
&b = *const i32@7fffe40a89d0
7
@mono:~/2-coding/zig-lang$ ./arg_adr
&a = *const i32@7ffdbc4301d0
&a = *const i32@7ffdbc4301e0
&b = *const i32@7ffdbc4301f0
&b = *const i32@7ffdbc430200
7
Can someone explain this?
ASLR
Does that mean that the “Harvard” computer architecture was the right thing to do?..
Yep, I need to work on my salesmanship skills. To give some more context. The discussion was with a HPC guy about low-level optimizations and using registers to load data instead of variables. Obviously, PRO was a part of the discussion, call-by-value and call-by-reference and related stuff.

Returning to the topic at hand,
&arg != &arg
is 100% a compiler bug

t would actually be fine if the language did not require a stable pointer address for references to parameters. If you need this property to hold, you’re doing something wrong.
maybe, it would be better to never pass arguments in registers and let a developer to decide whether an argument is passed as a copy (mutate it as you want, it’s a copy anyway) or as a reference (*const
- do not allow write access to a pointee, *
- allow a callee to modify arg.*)
?..