ziglang / zig

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

Self-referential tagged union segfaults the compiler #20218

Open paoda opened 4 months ago

paoda commented 4 months ago

Zig Version

0.13.0

Steps to Reproduce and Observed Behavior

In example.zig:

const std = @import("std");

const ImSelfReferential = union(enum) { foo: ImSelfReferential };

pub fn main() !void {
    const thing: ImSelfReferential = undefined;
    _ = thing;
}

run zig build-exe example.zig

Expected Behavior

A compiler error of some sort

sno2 commented 4 months ago

Reproduces on 0.14.0-dev.2+0884a4341. Also noteworthy, this compiles fine (EDIT: not doing what I thought it was, see nektro's comment)

//! zig build-exe foo.zig

const std = @import("std");

const ImSelfReferential = union(enum) { foo: ImSelfReferential };

pub fn main() !void {
    _ = ImSelfReferential;
}

And this gives us a good error message:

//! zig build-exe foo.zig

const std = @import("std");

pub fn main() !void {
    _ = foo;
}

const ImSelfReferential = union(enum) { foo: ImSelfReferential };
fn foo(a: ImSelfReferential) ImSelfReferential {
    _ = a; // autofix
}
foo.zig:7:27: error: union 'foo.ImSelfReferential' depends on itself                                                               
const ImSelfReferential = union(enum) { foo: ImSelfReferential };                                                                  
                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                                   
foo.zig:7:41: note: while checking this field
const ImSelfReferential = union(enum) { foo: ImSelfReferential };
                                        ^~~~~~~~~~~~~~~~~~~~~~
nektro commented 4 months ago

_ = T; when T is a type doesn't count as a reference of the fields, only the namespace which in this case is empty

der-teufel-programming commented 4 months ago

While this breaks the compiler

const ImSelfReferential = union(enum) { foo: ImSelfReferential };

pub fn main() !void {
    const thing: ImSelfReferential = undefined;
    _ = thing;
}

a very similar piece of code

const ImSelfReferential = union(enum) { foo: ImSelfReferential };

pub fn main() !void {
    const thing: ImSelfReferential = undefined;
    _ = &thing;
}

gives the correct error message:

selfref-taggedunion-break.zig:1:27: error: union 'selfref-taggedunion-break.ImSelfReferential' depends on itself
const ImSelfReferential = union(enum) { foo: ImSelfReferential };
                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
selfref-taggedunion-break.zig:1:41: note: while checking this field
const ImSelfReferential = union(enum) { foo: ImSelfReferential };
                                        ^~~~~~~~~~~~~~~~~~~~~~