diff --git a/src/Sema.zig b/src/Sema.zig index 4904149cf0f1c47e59d0ee7e4d4c4e11307acf43..ca4ccd20c9652e741cfa7badfeafad4b8324338b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -27920,7 +27920,25 @@ fn elemValSlice( } } - if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); + switch (elem_ty.classify(zcu)) { + .no_possible_value => switch (elem_ty.zigTypeTag(zcu)) { + .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{elem_ty.fmt(zcu)}), + else => { + // Loading an uninstantiable type always invokes Illegal Behavior. + if (block.isComptime()) { + return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{elem_ty.fmt(zcu)}); + } else if (block.wantSafety()) { + try sema.safetyPanic(block, src, .load_uninstantiable_type); + return .unreachable_value; + } else { + _ = try block.addNoOp(.unreach); + return .unreachable_value; + } + }, + }, + .one_possible_value => return .fromValue((try elem_ty.onePossibleValue(pt)).?), + .runtime, .partially_comptime, .fully_comptime => {}, + } try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_src); try sema.validateRuntimeValue(block, slice_src, slice); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index b1feb5433000f68a3d54c4e4555bb07cfa75f5dd..6f521a724349c176e5ff693c521394d0fca9d73b 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -1209,3 +1209,21 @@ test "modify slice through coerced array pointer" { assert(slice[2] == 0); } } + +test "iterate empty slice of uninstantiable types" { + const E = enum {}; + var slice_enum: []const E = undefined; + slice_enum = &.{}; + for (slice_enum) |elem| { + _ = elem; + return error.ReachedUnreachable; + } + + const U = union { foo: struct { a: u8, b: noreturn }, bar: E }; + var slice_union: []const U = undefined; + slice_union = &.{}; + for (slice_union) |elem| { + _ = elem; + return error.ReachedUnreachable; + } +} diff --git a/test/cases/compile_errors/load_uninstantiable_type.zig b/test/cases/compile_errors/load_uninstantiable_type.zig new file mode 100644 index 0000000000000000000000000000000000000000..7b68d2330c91356a1640a115ecfe53aad68a6762 --- /dev/null +++ b/test/cases/compile_errors/load_uninstantiable_type.zig @@ -0,0 +1,48 @@ +const E = enum {}; +const U = union { + foo: struct { a: u8, b: noreturn }, + bar: E, +}; +export fn loadSliceUninstantiableEnum() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + _ = comptime slice[0]; + comptime unreachable; +} +export fn iterateSliceUninstantiableEnum() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + comptime for (slice) |elem| { + _ = elem; + }; + comptime unreachable; +} +export fn loadSliceUninstantiableUnion() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + _ = comptime slice[0]; + comptime unreachable; +} +export fn iterateSliceUninstantiableUnion() void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + comptime for (slice) |elem| { + _ = elem; + }; + comptime unreachable; +} + +// error +// +// :10:23: error: cannot load uninstantiable type 'tmp.E' +// :1:11: note: enum declared here +// :17:19: error: cannot load uninstantiable type 'tmp.E' +// :1:11: note: enum declared here +// :26:23: error: cannot load uninstantiable type 'tmp.U' +// :2:11: note: union declared here +// :33:19: error: cannot load uninstantiable type 'tmp.U' +// :2:11: note: union declared here diff --git a/test/cases/safety/iterate_slice_uninstantiable_enum.zig b/test/cases/safety/iterate_slice_uninstantiable_enum.zig new file mode 100644 index 0000000000000000000000000000000000000000..09093930e66113bfe3b51ab34633d59fcae74457 --- /dev/null +++ b/test/cases/safety/iterate_slice_uninstantiable_enum.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const E = enum {}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + for (slice) |elem| _ = elem; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/iterate_slice_uninstantiable_union.zig b/test/cases/safety/iterate_slice_uninstantiable_union.zig new file mode 100644 index 0000000000000000000000000000000000000000..b6841dbc7005fbd15e73deb183228ad6850e8f4f --- /dev/null +++ b/test/cases/safety/iterate_slice_uninstantiable_union.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const U = union { + foo: struct { a: u8, b: noreturn }, + bar: enum {}, +}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + for (slice) |elem| _ = elem; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_slice_uninstantiable_enum.zig b/test/cases/safety/load_slice_uninstantiable_enum.zig new file mode 100644 index 0000000000000000000000000000000000000000..57c308f8f6b15ca4f61eb36a3da0c246b6831dd8 --- /dev/null +++ b/test/cases/safety/load_slice_uninstantiable_enum.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const E = enum {}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]E = @ptrCast(&bytes); + const slice: []const E = ptr; + _ = slice[0]; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_slice_uninstantiable_union.zig b/test/cases/safety/load_slice_uninstantiable_union.zig new file mode 100644 index 0000000000000000000000000000000000000000..1f7264526ed4518198a3c797110515971ac2dbc1 --- /dev/null +++ b/test/cases/safety/load_slice_uninstantiable_union.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { + std.process.exit(0); + } + std.process.exit(1); +} + +const U = union { + foo: struct { a: u8, b: noreturn }, + bar: enum {}, +}; +pub fn main() error{TestFailed}!void { + const bytes: [32]u8 = @splat(0); + const ptr: *const [1]U = @ptrCast(&bytes); + const slice: []const U = ptr; + _ = slice[0]; + return error.TestFailed; +} +// run +// backend=selfhosted,llvm +// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_uninstantiable_enum_from_slice.zig b/test/cases/safety/load_uninstantiable_enum_from_slice.zig deleted file mode 100644 index 57c308f8f6b15ca4f61eb36a3da0c246b6831dd8..0000000000000000000000000000000000000000 --- a/test/cases/safety/load_uninstantiable_enum_from_slice.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { - std.process.exit(0); - } - std.process.exit(1); -} - -const E = enum {}; -pub fn main() error{TestFailed}!void { - const bytes: [32]u8 = @splat(0); - const ptr: *const [1]E = @ptrCast(&bytes); - const slice: []const E = ptr; - _ = slice[0]; - return error.TestFailed; -} -// run -// backend=selfhosted,llvm -// target=x86_64-linux,aarch64-linux,wasm32-wasi diff --git a/test/cases/safety/load_uninstantiable_union.zig b/test/cases/safety/load_uninstantiable_union.zig index 87f84e07c7f624104d797b94037fc6148491265b..9e673d3958bd8ec11c39033f0d5e3688ddaca79e 100644 --- a/test/cases/safety/load_uninstantiable_union.zig +++ b/test/cases/safety/load_uninstantiable_union.zig @@ -9,7 +9,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usi } const U = union { - foo: struct { a: u8, b: noreturn, }, + foo: struct { a: u8, b: noreturn }, bar: enum {}, }; pub fn main() error{TestFailed}!void { diff --git a/test/cases/safety/load_uninstantiable_union_from_slice.zig b/test/cases/safety/load_uninstantiable_union_from_slice.zig deleted file mode 100644 index 844bfd0b7d766bfb9e5eb0f911061eda7bace0d6..0000000000000000000000000000000000000000 --- a/test/cases/safety/load_uninstantiable_union_from_slice.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = stack_trace; - if (std.mem.eql(u8, message, "attempt to load uninstantiable type")) { - std.process.exit(0); - } - std.process.exit(1); -} - -const U = union { - foo: struct { a: u8, b: noreturn, }, - bar: enum {}, -}; -pub fn main() error{TestFailed}!void { - const bytes: [32]u8 = @splat(0); - const ptr: *const [1]U = @ptrCast(&bytes); - const slice: []const U = ptr; - _ = slice[0]; - return error.TestFailed; -} -// run -// backend=selfhosted,llvm -// target=x86_64-linux,aarch64-linux,wasm32-wasi