zig
zig copied to clipboard
Mutable tuples coerce un-checked into mutable slices
Zig Version
0.10.0-dev.3981+60678f5ba
Steps to Reproduce
const std = @import("std");
pub fn main() void {
// add explicit type annotation, like
// `std.meta.Tuple(&.{ u7, u7, u7, u7 })`,
// to avoid the segfault triggered by the commented line below.
var a = .{ 1, 2, 3, 4 };
var b: []u8 = &a;
// b[0] = 7; // <- un-comment to observe a segfault.
std.debug.print(
\\a: (@{x}){any}
\\b: (@{x}){any}
\\
, .{ @ptrToInt(&a), a, @ptrToInt(b.ptr), b });
}
Expected Behavior
I would expect only comptime tuples to coerce to arrays this way, or at most, that the slice would alias the tuple memory when the tuple contains fields with a homogeneous field type, also equal to the type of the slice, instead of creating an equivalent temporary.
Actual Behavior
Running with zig run
:
Output with var a: std.meta.Tuple(&[_]type{u8} ** 4) = .{ 1, 2, 3, 4 };
:
a: (@7ffe6db49f18){ 1, 2, 3, 4 }
b: (@7ffe6db49f34){ 1, 2, 3, 4 }
Output with var a: std.meta.Tuple(&[_]type{u7} ** 4) = .{ 1, 2, 3, 4 };
:
a: (@7fff80bacae8){ 1, 2, 3, 4 }
b: (@7fff80bacb04){ 1, 2, 3, 4 }
Output with var a: std.meta.Tuple(&[_]type{u8} ** 4) = .{ 1, 2, 3, 4 };
& b[0] = 7;
:
a: (@7ffc9c319158){ 1, 2, 3, 4 }
b: (@7ffc9c319174){ 7, 2, 3, 4 }
Output with var a = .{ 1, 2, 3, 4 };
:
a: (@aaaaaaaaaaaaaaaa){ 1, 2, 3, 4 }
b: (@20bb28){ 1, 2, 3, 4 }
Output with var b: []u32 = &a;
:
a: (@aaaaaaaaaaaaaaaa){ 1, 2, 3, 4 }
b: (@208690){ 1, 2, 3, 4 }
Output with var a: std.meta.Tuple(&[_]type{u7} ** 4) = .{ 1, 2, 3, 4 };
, var b: []u32 = &a;
& b[0] = 7;
:
a: (@7fff8cbcd1d8){ 1, 2, 3, 4 }
b: (@7fff8cbcd200){ 7, 2, 3, 4 }
Output with var a = .{ 1, 2, 3, 4 };
& b[0] = 7;
:
Segmentation fault at address 0x20bb28
src/main.zig:6:5: 0x20db72 in main (main)
b[0] = 7;
^
{zig install}/files/lib/std/start.zig:568:22: 0x20d41d in posixCallMainAndExit (main)
root.main();
^
{zig install}/files/lib/std/start.zig:340:5: 0x20ce22 in _start (main)
@call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
^
Aborted (core dumped)
Extra observation: when a
's fields aren't comprised of comptime_int
(has an explicit tuple type), and thus is placed on the stack, the distance between the memory address of a
, and the address pointed to by b
acts like this:
-
a.len == 0
: 0 -
a.len == 1
: 31 -
a.len == 2
: 30 -
a.len == 3 or a.len == 4
: 28 -
a.len >= 5 and a.len <= 8
: 32 -
a.len >= 9 and a.len <= 16
: 48 -
a.len >= 17 and a.len <= 24
: 64 -
a.len >= 25 and a.len <= 32
: 80
and then seemingly continues like that, the difference incrementing by 16 for every 7 additional elements in a
.
This seems to indicate that there is indeed a temporary array being created for b
to point to in this case, but for some reason not when a
is comprised of comptime_int
s.
Another observation: stage1 also allows coercion from a mutable tuple to a mutable slice, apparently.