authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-10-17 00:30:53-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-21 10:04:01-07:00
logbaed9626d075628887b7d5400fb03efcb9910050
tree4336a378943bb93ba71157cb2c3bece03378cd21
parent07ea1c97ee891ea259020fe28fba317c20c3b03a

Sema: check slice load for uninstantiable element type


10 files changed, 176 insertions(+), 47 deletions(-)

src/Sema.zig+19-1
...@@ -27920,7 +27920,25 @@ fn elemValSlice(...@@ -27920,7 +27920,25 @@ fn elemValSlice(
27920 }27920 }
27921 }27921 }
2792227922
27923 if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);27923 switch (elem_ty.classify(zcu)) {
27924 .no_possible_value => switch (elem_ty.zigTypeTag(zcu)) {
27925 .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{elem_ty.fmt(zcu)}),
27926 else => {
27927 // Loading an uninstantiable type always invokes Illegal Behavior.
27928 if (block.isComptime()) {
27929 return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{elem_ty.fmt(zcu)});
27930 } else if (block.wantSafety()) {
27931 try sema.safetyPanic(block, src, .load_uninstantiable_type);
27932 return .unreachable_value;
27933 } else {
27934 _ = try block.addNoOp(.unreach);
27935 return .unreachable_value;
27936 }
27937 },
27938 },
27939 .one_possible_value => return .fromValue((try elem_ty.onePossibleValue(pt)).?),
27940 .runtime, .partially_comptime, .fully_comptime => {},
27941 }
2792427942
27925 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_src);27943 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_src);
27926 try sema.validateRuntimeValue(block, slice_src, slice);27944 try sema.validateRuntimeValue(block, slice_src, slice);
test/behavior/slice.zig+18
...@@ -1209,3 +1209,21 @@ test "modify slice through coerced array pointer" {...@@ -1209,3 +1209,21 @@ test "modify slice through coerced array pointer" {
1209 assert(slice[2] == 0);1209 assert(slice[2] == 0);
1210 }1210 }
1211}1211}
1212
1213test "iterate empty slice of uninstantiable types" {
1214 const E = enum {};
1215 var slice_enum: []const E = undefined;
1216 slice_enum = &.{};
1217 for (slice_enum) |elem| {
1218 _ = elem;
1219 return error.ReachedUnreachable;
1220 }
1221
1222 const U = union { foo: struct { a: u8, b: noreturn }, bar: E };
1223 var slice_union: []const U = undefined;
1224 slice_union = &.{};
1225 for (slice_union) |elem| {
1226 _ = elem;
1227 return error.ReachedUnreachable;
1228 }
1229}
test/cases/compile_errors/load_uninstantiable_type.zig created+48
...@@ -0,0 +1,48 @@
1const E = enum {};
2const U = union {
3 foo: struct { a: u8, b: noreturn },
4 bar: E,
5};
6export fn loadSliceUninstantiableEnum() void {
7 const bytes: [32]u8 = @splat(0);
8 const ptr: *const [1]E = @ptrCast(&bytes);
9 const slice: []const E = ptr;
10 _ = comptime slice[0];
11 comptime unreachable;
12}
13export fn iterateSliceUninstantiableEnum() void {
14 const bytes: [32]u8 = @splat(0);
15 const ptr: *const [1]E = @ptrCast(&bytes);
16 const slice: []const E = ptr;
17 comptime for (slice) |elem| {
18 _ = elem;
19 };
20 comptime unreachable;
21}
22export fn loadSliceUninstantiableUnion() void {
23 const bytes: [32]u8 = @splat(0);
24 const ptr: *const [1]U = @ptrCast(&bytes);
25 const slice: []const U = ptr;
26 _ = comptime slice[0];
27 comptime unreachable;
28}
29export fn iterateSliceUninstantiableUnion() void {
30 const bytes: [32]u8 = @splat(0);
31 const ptr: *const [1]U = @ptrCast(&bytes);
32 const slice: []const U = ptr;
33 comptime for (slice) |elem| {
34 _ = elem;
35 };
36 comptime unreachable;
37}
38
39// error
40//
41// :10:23: error: cannot load uninstantiable type 'tmp.E'
42// :1:11: note: enum declared here
43// :17:19: error: cannot load uninstantiable type 'tmp.E'
44// :1:11: note: enum declared here
45// :26:23: error: cannot load uninstantiable type 'tmp.U'
46// :2:11: note: union declared here
47// :33:19: error: cannot load uninstantiable type 'tmp.U'
48// :2:11: note: union declared here
test/cases/safety/iterate_slice_uninstantiable_enum.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11const E = enum {};
12pub fn main() error{TestFailed}!void {
13 const bytes: [32]u8 = @splat(0);
14 const ptr: *const [1]E = @ptrCast(&bytes);
15 const slice: []const E = ptr;
16 for (slice) |elem| _ = elem;
17 return error.TestFailed;
18}
19// run
20// backend=selfhosted,llvm
21// target=x86_64-linux,aarch64-linux,wasm32-wasi
test/cases/safety/iterate_slice_uninstantiable_union.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11const U = union {
12 foo: struct { a: u8, b: noreturn },
13 bar: enum {},
14};
15pub fn main() error{TestFailed}!void {
16 const bytes: [32]u8 = @splat(0);
17 const ptr: *const [1]U = @ptrCast(&bytes);
18 const slice: []const U = ptr;
19 for (slice) |elem| _ = elem;
20 return error.TestFailed;
21}
22// run
23// backend=selfhosted,llvm
24// target=x86_64-linux,aarch64-linux,wasm32-wasi
test/cases/safety/load_slice_uninstantiable_enum.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11const E = enum {};
12pub fn main() error{TestFailed}!void {
13 const bytes: [32]u8 = @splat(0);
14 const ptr: *const [1]E = @ptrCast(&bytes);
15 const slice: []const E = ptr;
16 _ = slice[0];
17 return error.TestFailed;
18}
19// run
20// backend=selfhosted,llvm
21// target=x86_64-linux,aarch64-linux,wasm32-wasi
test/cases/safety/load_slice_uninstantiable_union.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11const U = union {
12 foo: struct { a: u8, b: noreturn },
13 bar: enum {},
14};
15pub fn main() error{TestFailed}!void {
16 const bytes: [32]u8 = @splat(0);
17 const ptr: *const [1]U = @ptrCast(&bytes);
18 const slice: []const U = ptr;
19 _ = slice[0];
20 return error.TestFailed;
21}
22// run
23// backend=selfhosted,llvm
24// target=x86_64-linux,aarch64-linux,wasm32-wasi
test/cases/safety/load_uninstantiable_enum_from_slice.zig deleted-21
...@@ -1,21 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11const E = enum {};
12pub fn main() error{TestFailed}!void {
13 const bytes: [32]u8 = @splat(0);
14 const ptr: *const [1]E = @ptrCast(&bytes);
15 const slice: []const E = ptr;
16 _ = slice[0];
17 return error.TestFailed;
18}
19// run
20// backend=selfhosted,llvm
21// target=x86_64-linux,aarch64-linux,wasm32-wasi
test/cases/safety/load_uninstantiable_union.zig+1-1
...@@ -9,7 +9,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usi...@@ -9,7 +9,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usi
9}9}
1010
11const U = union {11const U = union {
12 foo: struct { a: u8, b: noreturn, },12 foo: struct { a: u8, b: noreturn },
13 bar: enum {},13 bar: enum {},
14};14};
15pub fn main() error{TestFailed}!void {15pub fn main() error{TestFailed}!void {
test/cases/safety/load_uninstantiable_union_from_slice.zig deleted-24
...@@ -1,24 +0,0 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11const U = union {
12 foo: struct { a: u8, b: noreturn, },
13 bar: enum {},
14};
15pub fn main() error{TestFailed}!void {
16 const bytes: [32]u8 = @splat(0);
17 const ptr: *const [1]U = @ptrCast(&bytes);
18 const slice: []const U = ptr;
19 _ = slice[0];
20 return error.TestFailed;
21}
22// run
23// backend=selfhosted,llvm
24// target=x86_64-linux,aarch64-linux,wasm32-wasi