aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.zig2
-rw-r--r--doc/langref.html.in130
-rw-r--r--lib/std/array_hash_map.zig15
-rw-r--r--lib/std/child_process.zig2
-rw-r--r--lib/std/math.zig12
-rw-r--r--lib/std/os/wasi.zig5
-rw-r--r--lib/std/os/windows.zig2
-rw-r--r--lib/std/zig/Ast.zig31
-rw-r--r--lib/std/zig/parse.zig17
-rw-r--r--lib/std/zig/parser_test.zig2
-rw-r--r--lib/std/zig/render.zig21
-rw-r--r--src/AstGen.zig170
-rw-r--r--src/Autodoc.zig2
-rw-r--r--src/Compilation.zig24
-rw-r--r--src/Module.zig24
-rw-r--r--src/Sema.zig552
-rw-r--r--src/Zir.zig27
-rw-r--r--src/arch/x86_64/Emit.zig2
-rw-r--r--src/codegen/llvm.zig8
-rw-r--r--src/print_zir.zig15
-rw-r--r--src/stage1/all_types.hpp1
-rw-r--r--src/stage1/astgen.cpp6
-rw-r--r--src/stage1/parser.cpp16
-rw-r--r--test/behavior.zig1
-rw-r--r--test/behavior/inline_switch.zig131
-rw-r--r--test/cases/compile_errors/function_type_coercion.zig21
-rw-r--r--test/cases/compile_errors/inline_underscore_prong.zig15
-rw-r--r--test/cases/compile_errors/invalid_inline_else_type.zig27
-rw-r--r--test/cases/compile_errors/invalid_tag_capture.zig15
-rw-r--r--test/cases/compile_errors/packed_struct_field_alignment_unavailable_for_reify_type.zig11
-rw-r--r--test/cases/compile_errors/tag_capture_on_non_inline_prong.zig14
-rw-r--r--test/standalone.zig2
-rw-r--r--test/standalone/issue_13030/build.zig18
-rw-r--r--test/standalone/issue_13030/main.zig7
34 files changed, 1092 insertions, 256 deletions
diff --git a/build.zig b/build.zig
index 3bb5474ce3..fcdf569e48 100644
--- a/build.zig
+++ b/build.zig
@@ -124,6 +124,7 @@ pub fn build(b: *Builder) !void {
const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse false;
const force_gpa = b.option(bool, "force-gpa", "Force the compiler to use GeneralPurposeAllocator") orelse false;
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse enable_llvm;
+ const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
const strip = b.option(bool, "strip", "Omit debug information") orelse false;
const use_zig0 = b.option(bool, "zig0", "Bootstrap using zig0") orelse false;
const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false;
@@ -143,6 +144,7 @@ pub fn build(b: *Builder) !void {
const exe = b.addExecutable("zig", main_file);
exe.stack_size = stack_size;
exe.strip = strip;
+ exe.sanitize_thread = sanitize_thread;
exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
exe.install();
exe.setBuildMode(mode);
diff --git a/doc/langref.html.in b/doc/langref.html.in
index ef9e8402c1..7cc26d662a 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4255,6 +4255,134 @@ test "enum literals with switch" {
}
{#code_end#}
{#header_close#}
+
+ {#header_open|Inline switch#}
+ <p>
+ Switch prongs can be marked as {#syntax#}inline{#endsyntax#} to generate
+ the prong's body for each possible value it could have:
+ </p>
+ {#code_begin|test|test_inline_switch#}
+const std = @import("std");
+const expect = std.testing.expect;
+const expectError = std.testing.expectError;
+
+fn isFieldOptional(comptime T: type, field_index: usize) !bool {
+ const fields = @typeInfo(T).Struct.fields;
+ return switch (field_index) {
+ // This prong is analyzed `fields.len - 1` times with `idx` being an
+ // unique comptime known value each time.
+ inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].field_type) == .Optional,
+ else => return error.IndexOutOfBounds,
+ };
+}
+
+const Struct1 = struct { a: u32, b: ?u32 };
+
+test "using @typeInfo with runtime values" {
+ var index: usize = 0;
+ try expect(!try isFieldOptional(Struct1, index));
+ index += 1;
+ try expect(try isFieldOptional(Struct1, index));
+ index += 1;
+ try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
+}
+
+// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
+// of this function:
+fn isFieldOptionalUnrolled(field_index: usize) !bool {
+ return switch (field_index) {
+ 0 => false,
+ 1 => true,
+ else => return error.IndexOutOfBounds,
+ };
+}
+ {#code_end#}
+ <p>
+ {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe
+ alternative to {#syntax#}inline for{#endsyntax#} loops:
+ </p>
+ {#code_begin|test|test_inline_else#}
+const std = @import("std");
+const expect = std.testing.expect;
+
+const SliceTypeA = extern struct {
+ len: usize,
+ ptr: [*]u32,
+};
+const SliceTypeB = extern struct {
+ ptr: [*]SliceTypeA,
+ len: usize,
+};
+const AnySlice = union(enum) {
+ a: SliceTypeA,
+ b: SliceTypeB,
+ c: []const u8,
+ d: []AnySlice,
+};
+
+fn withFor(any: AnySlice) usize {
+ const Tag = @typeInfo(AnySlice).Union.tag_type.?;
+ inline for (@typeInfo(Tag).Enum.fields) |field| {
+ // With `inline for` the function gets generated as
+ // a series of `if` statements relying on the optimizer
+ // to convert it to a switch.
+ if (field.value == @enumToInt(any)) {
+ return @field(any, field.name).len;
+ }
+ }
+ // When using `inline for` the compiler doesn't know that every
+ // possible case has been handled requiring an explicit `unreachable`.
+ unreachable;
+}
+
+fn withSwitch(any: AnySlice) usize {
+ return switch (any) {
+ // With `inline else` the function is explicitly generated
+ // as the desired switch and the compiler can check that
+ // every possible case is handled.
+ inline else => |slice| slice.len,
+ };
+}
+
+test "inline for and inline else similarity" {
+ var any = AnySlice{ .c = "hello" };
+ try expect(withFor(any) == 5);
+ try expect(withSwitch(any) == 5);
+}
+ {#code_end#}
+ <p>
+ When using an inline prong switching on an union an additional
+ capture can be used to obtain the union's enum tag value.
+ </p>
+ {#code_begin|test|test_inline_switch_union_tag#}
+const std = @import("std");
+const expect = std.testing.expect;
+
+const U = union(enum) {
+ a: u32,
+ b: f32,
+};
+
+fn getNum(u: U) u32 {
+ switch (u) {
+ // Here `num` is a runtime known value that is either
+ // `u.a` or `u.b` and `tag` is `u`'s comptime known tag value.
+ inline else => |num, tag| {
+ if (tag == .b) {
+ return @floatToInt(u32, num);
+ }
+ return num;
+ }
+ }
+}
+
+test "test" {
+ var u = U{ .b = 42 };
+ try expect(getNum(u) == 42);
+}
+ {#code_end#}
+ {#see_also|inline while|inline for#}
+ {#header_close#}
{#header_close#}
{#header_open|while#}
@@ -10768,7 +10896,7 @@ test "string literal to constant slice" {
</p>
{#code_begin|syntax#}
const builtin = @import("builtin");
-const separator = if (builtin.os.tag == builtin.Os.windows) '\\' else '/';
+const separator = if (builtin.os.tag == .windows) '\\' else '/';
{#code_end#}
<p>
Example of what is imported with {#syntax#}@import("builtin"){#endsyntax#}:
diff --git a/lib/std/array_hash_map.zig b/lib/std/array_hash_map.zig
index 031a9fab5d..a502c5fa3f 100644
--- a/lib/std/array_hash_map.zig
+++ b/lib/std/array_hash_map.zig
@@ -773,9 +773,9 @@ pub fn ArrayHashMapUnmanaged(
}
}
+ try self.entries.ensureTotalCapacity(allocator, new_capacity);
const new_bit_index = try IndexHeader.findBitIndex(new_capacity);
const new_header = try IndexHeader.alloc(allocator, new_bit_index);
- try self.entries.ensureTotalCapacity(allocator, new_capacity);
if (self.index_header) |old_header| old_header.free(allocator);
self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
@@ -2042,6 +2042,19 @@ test "ensure capacity" {
try testing.expect(initial_capacity == map.capacity());
}
+test "ensure capacity leak" {
+ try testing.checkAllAllocationFailures(std.testing.allocator, struct {
+ pub fn f(allocator: Allocator) !void {
+ var map = AutoArrayHashMap(i32, i32).init(allocator);
+ defer map.deinit();
+
+ var i: i32 = 0;
+ // put more than `linear_scan_max` in so index_header gets allocated.
+ while (i <= 20) : (i += 1) try map.put(i, i);
+ }
+ }.f, .{});
+}
+
test "big map" {
var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
defer map.deinit();
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index f1604bb86c..322c74b11b 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -122,7 +122,7 @@ pub const ChildProcess = struct {
}
pub fn setUserName(self: *ChildProcess, name: []const u8) !void {
- const user_info = try os.getUserInfo(name);
+ const user_info = try std.process.getUserInfo(name);
self.uid = user_info.uid;
self.gid = user_info.gid;
}
diff --git a/lib/std/math.zig b/lib/std/math.zig
index a69a6f428c..18c8f555d4 100644
--- a/lib/std/math.zig
+++ b/lib/std/math.zig
@@ -285,10 +285,10 @@ pub inline fn tan(value: anytype) @TypeOf(value) {
return @tan(value);
}
-// Convert an angle in radians to degrees. T must be a float type.
+/// Converts an angle in radians to degrees. T must be a float type.
pub fn radiansToDegrees(comptime T: type, angle_in_radians: T) T {
- if (@typeInfo(T) != .Float)
- @compileError("T must be a float type.");
+ if (@typeInfo(T) != .Float and @typeInfo(T) != .ComptimeFloat)
+ @compileError("T must be a float type");
return angle_in_radians * 180.0 / pi;
}
@@ -300,10 +300,10 @@ test "radiansToDegrees" {
try std.testing.expectApproxEqAbs(@as(f32, 360), radiansToDegrees(f32, 2.0 * pi), 1e-6);
}
-// Convert an angle in degrees to radians. T must be a float type.
+/// Converts an angle in degrees to radians. T must be a float type.
pub fn degreesToRadians(comptime T: type, angle_in_degrees: T) T {
- if (@typeInfo(T) != .Float)
- @compileError("T must be a float type.");
+ if (@typeInfo(T) != .Float and @typeInfo(T) != .ComptimeFloat)
+ @compileError("T must be a float type");
return angle_in_degrees * pi / 180.0;
}
diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig
index ec2c577de1..db6b4c930c 100644
--- a/lib/std/os/wasi.zig
+++ b/lib/std/os/wasi.zig
@@ -76,6 +76,7 @@ pub extern "wasi_snapshot_preview1" fn random_get(buf: [*]u8, buf_len: usize) er
pub extern "wasi_snapshot_preview1" fn sched_yield() errno_t;
+pub extern "wasi_snapshot_preview1" fn sock_accept(sock: fd_t, flags: fdflags_t, result_fd: *fd_t) errno_t;
pub extern "wasi_snapshot_preview1" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t;
pub extern "wasi_snapshot_preview1" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t;
pub extern "wasi_snapshot_preview1" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t;
@@ -434,6 +435,7 @@ pub const RIGHT = struct {
pub const PATH_UNLINK_FILE: rights_t = 0x0000000004000000;
pub const POLL_FD_READWRITE: rights_t = 0x0000000008000000;
pub const SOCK_SHUTDOWN: rights_t = 0x0000000010000000;
+ pub const SOCK_ACCEPT: rights_t = 0x0000000020000000;
pub const ALL: rights_t = FD_DATASYNC |
FD_READ |
FD_SEEK |
@@ -462,7 +464,8 @@ pub const RIGHT = struct {
PATH_REMOVE_DIRECTORY |
PATH_UNLINK_FILE |
POLL_FD_READWRITE |
- SOCK_SHUTDOWN;
+ SOCK_SHUTDOWN |
+ SOCK_ACCEPT;
};
pub const sdflags_t = u8;
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index c20457943b..f6d148a317 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -3695,4 +3695,4 @@ pub const CTRL_CLOSE_EVENT: DWORD = 2;
pub const CTRL_LOGOFF_EVENT: DWORD = 5;
pub const CTRL_SHUTDOWN_EVENT: DWORD = 6;
-pub const HANDLER_ROUTINE = std.meta.FnPtr(fn (dwCtrlType: DWORD) callconv(.C) BOOL);
+pub const HANDLER_ROUTINE = std.meta.FnPtr(fn (dwCtrlType: DWORD) callconv(WINAPI) BOOL);
diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig
index bfa73dc9ac..62a567387f 100644
--- a/lib/std/zig/Ast.zig
+++ b/lib/std/zig/Ast.zig
@@ -643,11 +643,23 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
n = datas[n].lhs;
}
},
+ .switch_case_inline_one => {
+ if (datas[n].lhs == 0) {
+ return main_tokens[n] - 2 - end_offset; // else token
+ } else {
+ return firstToken(tree, datas[n].lhs) - 1;
+ }
+ },
.switch_case => {
const extra = tree.extraData(datas[n].lhs, Node.SubRange);
assert(extra.end - extra.start > 0);
n = tree.extra_data[extra.start];
},
+ .switch_case_inline => {
+ const extra = tree.extraData(datas[n].lhs, Node.SubRange);
+ assert(extra.end - extra.start > 0);
+ return firstToken(tree, tree.extra_data[extra.start]) - 1;
+ },
.asm_output, .asm_input => {
assert(token_tags[main_tokens[n] - 1] == .l_bracket);
@@ -763,7 +775,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
.ptr_type_bit_range,
.array_type,
.switch_case_one,
+ .switch_case_inline_one,
.switch_case,
+ .switch_case_inline,
.switch_range,
=> n = datas[n].rhs,
@@ -1755,7 +1769,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
.values = if (data.lhs == 0) values[0..0] else values[0..1],
.arrow_token = tree.nodes.items(.main_token)[node],
.target_expr = data.rhs,
- });
+ }, node);
}
pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
@@ -1765,7 +1779,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
.values = tree.extra_data[extra.start..extra.end],
.arrow_token = tree.nodes.items(.main_token)[node],
.target_expr = data.rhs,
- });
+ }, node);
}
pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
@@ -2038,15 +2052,21 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai
return result;
}
-fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components) full.SwitchCase {
+fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
const token_tags = tree.tokens.items(.tag);
+ const node_tags = tree.nodes.items(.tag);
var result: full.SwitchCase = .{
.ast = info,
.payload_token = null,
+ .inline_token = null,
};
if (token_tags[info.arrow_token + 1] == .pipe) {
result.payload_token = info.arrow_token + 2;
}
+ switch (node_tags[node]) {
+ .switch_case_inline, .switch_case_inline_one => result.inline_token = firstToken(tree, node),
+ else => {},
+ }
return result;
}
@@ -2454,6 +2474,7 @@ pub const full = struct {
};
pub const SwitchCase = struct {
+ inline_token: ?TokenIndex,
/// Points to the first token after the `|`. Will either be an identifier or
/// a `*` (with an identifier immediately after it).
payload_token: ?TokenIndex,
@@ -2847,9 +2868,13 @@ pub const Node = struct {
/// `lhs => rhs`. If lhs is omitted it means `else`.
/// main_token is the `=>`
switch_case_one,
+ /// Same ast `switch_case_one` but the case is inline
+ switch_case_inline_one,
/// `a, b, c => rhs`. `SubRange[lhs]`.
/// main_token is the `=>`
switch_case,
+ /// Same ast `switch_case` but the case is inline
+ switch_case_inline,
/// `lhs...rhs`.
switch_range,
/// `while (lhs) rhs`.
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index 43b6eda8e0..db56cef21e 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -3100,7 +3100,7 @@ const Parser = struct {
return identifier;
}
- /// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
+ /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
/// SwitchCase
/// <- SwitchItem (COMMA SwitchItem)* COMMA?
/// / KEYWORD_else
@@ -3108,6 +3108,8 @@ const Parser = struct {
const scratch_top = p.scratch.items.len;
defer p.scratch.shrinkRetainingCapacity(scratch_top);
+ const is_inline = p.eatToken(.keyword_inline) != null;
+
if (p.eatToken(.keyword_else) == null) {
while (true) {
const item = try p.parseSwitchItem();
@@ -3115,15 +3117,18 @@ const Parser = struct {
try p.scratch.append(p.gpa, item);
if (p.eatToken(.comma) == null) break;
}
- if (scratch_top == p.scratch.items.len) return null_node;
+ if (scratch_top == p.scratch.items.len) {
+ if (is_inline) p.tok_i -= 1;
+ return null_node;
+ }
}
const arrow_token = try p.expectToken(.equal_angle_bracket_right);
- _ = try p.parsePtrPayload();
+ _ = try p.parsePtrIndexPayload();
const items = p.scratch.items[scratch_top..];
switch (items.len) {
0 => return p.addNode(.{
- .tag = .switch_case_one,
+ .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
.main_token = arrow_token,
.data = .{
.lhs = 0,
@@ -3131,7 +3136,7 @@ const Parser = struct {
},
}),
1 => return p.addNode(.{
- .tag = .switch_case_one,
+ .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
.main_token = arrow_token,
.data = .{
.lhs = items[0],
@@ -3139,7 +3144,7 @@ const Parser = struct {
},
}),
else => return p.addNode(.{
- .tag = .switch_case,
+ .tag = if (is_inline) .switch_case_inline else .switch_case,
.main_token = arrow_token,
.data = .{
.lhs = try p.addExtra(try p.listToSpan(items)),
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index 0e1817ffab..4e155df6d8 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -3276,6 +3276,8 @@ test "zig fmt: switch" {
\\ switch (u) {
\\ Union.Int => |int| {},
\\ Union.Float => |*float| unreachable,
+ \\ 1 => |a, b| unreachable,
+ \\ 2 => |*a, b| unreachable,
\\ }
\\}
\\
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index 6ef0cfcd6f..ab009f8390 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -685,8 +685,8 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
},
- .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
- .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
+ .switch_case_one, .switch_case_inline_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
+ .switch_case, .switch_case_inline => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
.while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
.while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
@@ -1509,6 +1509,11 @@ fn renderSwitchCase(
break :blk hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token);
};
+ // render inline keyword
+ if (switch_case.inline_token) |some| {
+ try renderToken(ais, tree, some, .space);
+ }
+
// Render everything before the arrow
if (switch_case.ast.values.len == 0) {
try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
@@ -1536,13 +1541,17 @@ fn renderSwitchCase(
if (switch_case.payload_token) |payload_token| {
try renderToken(ais, tree, payload_token - 1, .none); // pipe
+ const ident = payload_token + @boolToInt(token_tags[payload_token] == .asterisk);
if (token_tags[payload_token] == .asterisk) {
try renderToken(ais, tree, payload_token, .none); // asterisk
- try renderToken(ais, tree, payload_token + 1, .none); // identifier
- try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
+ }
+ try renderToken(ais, tree, ident, .none); // identifier
+ if (token_tags[ident + 1] == .comma) {
+ try renderToken(ais, tree, ident + 1, .space); // ,
+ try renderToken(ais, tree, ident + 2, .none); // identifier
+ try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
} else {
- try renderToken(ais, tree, payload_token, .none); // identifier
- try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
+ try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
}
}
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 422827c673..c12778929c 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -386,7 +386,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
.simple_var_decl => unreachable,
.aligned_var_decl => unreachable,
.switch_case => unreachable,
+ .switch_case_inline => unreachable,
.switch_case_one => unreachable,
+ .switch_case_inline_one => unreachable,
.container_field_init => unreachable,
.container_field_align => unreachable,
.container_field => unreachable,
@@ -600,7 +602,9 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
.@"errdefer" => unreachable, // Handled in `blockExpr`.
.switch_case => unreachable, // Handled in `switchExpr`.
+ .switch_case_inline => unreachable, // Handled in `switchExpr`.
.switch_case_one => unreachable, // Handled in `switchExpr`.
+ .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
.switch_range => unreachable, // Handled in `switchExpr`.
.asm_output => unreachable, // Handled in `asmExpr`.
@@ -2369,6 +2373,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
+ .switch_capture_tag,
.struct_init_empty,
.struct_init,
.struct_init_ref,
@@ -6213,14 +6218,15 @@ fn switchExpr(
var any_payload_is_ref = false;
var scalar_cases_len: u32 = 0;
var multi_cases_len: u32 = 0;
+ var inline_cases_len: u32 = 0;
var special_prong: Zir.SpecialProng = .none;
var special_node: Ast.Node.Index = 0;
var else_src: ?Ast.TokenIndex = null;
var underscore_src: ?Ast.TokenIndex = null;
for (case_nodes) |case_node| {
const case = switch (node_tags[case_node]) {
- .switch_case_one => tree.switchCaseOne(case_node),
- .switch_case => tree.switchCase(case_node),
+ .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+ .switch_case, .switch_case_inline => tree.switchCase(case_node),
else => unreachable,
};
if (case.payload_token) |payload_token| {
@@ -6304,6 +6310,9 @@ fn switchExpr(
},
);
}
+ if (case.inline_token != null) {
+ return astgen.failTok(case_src, "cannot inline '_' prong", .{});
+ }
special_node = case_node;
special_prong = .under;
underscore_src = case_src;
@@ -6315,6 +6324,9 @@ fn switchExpr(
} else {
multi_cases_len += 1;
}
+ if (case.inline_token != null) {
+ inline_cases_len += 1;
+ }
}
const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
@@ -6354,8 +6366,8 @@ fn switchExpr(
var scalar_case_index: u32 = 0;
for (case_nodes) |case_node| {
const case = switch (node_tags[case_node]) {
- .switch_case_one => tree.switchCaseOne(case_node),
- .switch_case => tree.switchCase(case_node),
+ .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+ .switch_case, .switch_case_inline => tree.switchCase(case_node),
else => unreachable,
};
@@ -6364,8 +6376,12 @@ fn switchExpr(
var dbg_var_name: ?u32 = null;
var dbg_var_inst: Zir.Inst.Ref = undefined;
+ var dbg_var_tag_name: ?u32 = null;
+ var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
var capture_inst: Zir.Inst.Index = 0;
+ var tag_inst: Zir.Inst.Index = 0;
var capture_val_scope: Scope.LocalVal = undefined;
+ var tag_scope: Scope.LocalVal = undefined;
const sub_scope = blk: {
const payload_token = case.payload_token orelse break :blk &case_scope.base;
const ident = if (token_tags[payload_token] == .asterisk)
@@ -6373,59 +6389,96 @@ fn switchExpr(
else
payload_token;
const is_ptr = ident != payload_token;
- if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
+ const ident_slice = tree.tokenSlice(ident);
+ var payload_sub_scope: *Scope = undefined;
+ if (mem.eql(u8, ident_slice, "_")) {
if (is_ptr) {
return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
}
- break :blk &case_scope.base;
- }
- if (case_node == special_node) {
- const capture_tag: Zir.Inst.Tag = if (is_ptr)
- .switch_capture_ref
- else
- .switch_capture;
- capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
- try astgen.instructions.append(gpa, .{
- .tag = capture_tag,
- .data = .{
- .switch_capture = .{
- .switch_inst = switch_block,
- // Max int communicates that this is the else/underscore prong.
- .prong_index = std.math.maxInt(u32),
- },
- },
- });
+ payload_sub_scope = &case_scope.base;
} else {
- const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
- const is_ptr_bits: u2 = @boolToInt(is_ptr);
- const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
- 0b00 => .switch_capture,
- 0b01 => .switch_capture_ref,
- 0b10 => .switch_capture_multi,
- 0b11 => .switch_capture_multi_ref,
+ if (case_node == special_node) {
+ const capture_tag: Zir.Inst.Tag = if (is_ptr)
+ .switch_capture_ref
+ else
+ .switch_capture;
+ capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
+ try astgen.instructions.append(gpa, .{
+ .tag = capture_tag,
+ .data = .{
+ .switch_capture = .{
+ .switch_inst = switch_block,
+ // Max int communicates that this is the else/underscore prong.
+ .prong_index = std.math.maxInt(u32),
+ },
+ },
+ });
+ } else {
+ const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
+ const is_ptr_bits: u2 = @boolToInt(is_ptr);
+ const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
+ 0b00 => .switch_capture,
+ 0b01 => .switch_capture_ref,
+ 0b10 => .switch_capture_multi,
+ 0b11 => .switch_capture_multi_ref,
+ };
+ const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
+ capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
+ try astgen.instructions.append(gpa, .{
+ .tag = capture_tag,
+ .data = .{ .switch_capture = .{
+ .switch_inst = switch_block,
+ .prong_index = capture_index,
+ } },
+ });
+ }
+ const capture_name = try astgen.identAsString(ident);
+ try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice);
+ capture_val_scope = .{
+ .parent = &case_scope.base,
+ .gen_zir = &case_scope,
+ .name = capture_name,
+ .inst = indexToRef(capture_inst),
+ .token_src = payload_token,
+ .id_cat = .@"capture",
};
- const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
- capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
- try astgen.instructions.append(gpa, .{
- .tag = capture_tag,
- .data = .{ .switch_capture = .{
- .switch_inst = switch_block,
- .prong_index = capture_index,
- } },
- });
+ dbg_var_name = capture_name;
+ dbg_var_inst = indexToRef(capture_inst);
+ payload_sub_scope = &capture_val_scope.base;
+ }
+
+ const tag_token = if (token_tags[ident + 1] == .comma)
+ ident + 2
+ else
+ break :blk payload_sub_scope;
+ const tag_slice = tree.tokenSlice(tag_token);
+ if (mem.eql(u8, tag_slice, "_")) {
+ return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
+ } else if (case.inline_token == null) {
+ return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
}
- const capture_name = try astgen.identAsString(ident);
- capture_val_scope = .{
- .parent = &case_scope.base,
+ const tag_name = try astgen.identAsString(tag_token);
+ try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice);
+ tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
+ try astgen.instructions.append(gpa, .{
+ .tag = .switch_capture_tag,
+ .data = .{ .un_tok = .{
+ .operand = cond,
+ .src_tok = case_scope.tokenIndexToRelative(tag_token),
+ } },
+ });
+
+ tag_scope = .{
+ .parent = payload_sub_scope,
.gen_zir = &case_scope,
- .name = capture_name,
- .inst = indexToRef(capture_inst),
- .token_src = payload_token,
- .id_cat = .@"capture",
+ .name = tag_name,
+ .inst = indexToRef(tag_inst),
+ .token_src = tag_token,
+ .id_cat = .@"switch tag capture",
};
- dbg_var_name = capture_name;
- dbg_var_inst = indexToRef(capture_inst);
- break :blk &capture_val_scope.base;
+ dbg_var_tag_name = tag_name;
+ dbg_var_tag_inst = indexToRef(tag_inst);
+ break :blk &tag_scope.base;
};
const header_index = @intCast(u32, payloads.items.len);
@@ -6480,10 +6533,14 @@ fn switchExpr(
defer case_scope.unstack();
if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
+ if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
try case_scope.addDbgBlockBegin();
if (dbg_var_name) |some| {
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
}
+ if (dbg_var_tag_name) |some| {
+ try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_tag_inst);
+ }
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
try checkUsed(parent_gz, &case_scope.base, sub_scope);
try case_scope.addDbgBlockEnd();
@@ -6495,7 +6552,8 @@ fn switchExpr(
const case_slice = case_scope.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(case_slice);
try payloads.ensureUnusedCapacity(gpa, body_len);
- payloads.items[body_len_index] = body_len;
+ const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
+ payloads.items[body_len_index] = body_len | inline_bit;
appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
}
}
@@ -6509,7 +6567,6 @@ fn switchExpr(
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
.operand = cond,
.bits = Zir.Inst.SwitchBlock.Bits{
- .is_ref = any_payload_is_ref,
.has_multi_cases = multi_cases_len != 0,
.has_else = special_prong == .@"else",
.has_under = special_prong == .under,
@@ -6543,7 +6600,7 @@ fn switchExpr(
end_index += 3 + items_len + 2 * ranges_len;
}
- const body_len = payloads.items[body_len_index];
+ const body_len = @truncate(u31, payloads.items[body_len_index]);
end_index += body_len;
switch (strat.tag) {
@@ -8433,7 +8490,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
.@"usingnamespace",
.test_decl,
.switch_case,
+ .switch_case_inline,
.switch_case_one,
+ .switch_case_inline_one,
.container_field_init,
.container_field_align,
.container_field,
@@ -8665,7 +8724,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
.@"usingnamespace",
.test_decl,
.switch_case,
+ .switch_case_inline,
.switch_case_one,
+ .switch_case_inline_one,
.container_field_init,
.container_field_align,
.container_field,
@@ -8876,7 +8937,9 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
.@"usingnamespace",
.test_decl,
.switch_case,
+ .switch_case_inline,
.switch_case_one,
+ .switch_case_inline_one,
.container_field_init,
.container_field_align,
.container_field,
@@ -9118,7 +9181,9 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
.@"usingnamespace",
.test_decl,
.switch_case,
+ .switch_case_inline,
.switch_case_one,
+ .switch_case_inline_one,
.container_field_init,
.container_field_align,
.container_field,
@@ -10051,6 +10116,7 @@ const Scope = struct {
@"local constant",
@"local variable",
@"loop index capture",
+ @"switch tag capture",
@"capture",
};
diff --git a/src/Autodoc.zig b/src/Autodoc.zig
index 9fe56a8ec2..496e5afa61 100644
--- a/src/Autodoc.zig
+++ b/src/Autodoc.zig
@@ -853,7 +853,7 @@ fn walkInstruction(
var path = str_tok.get(file.zir);
const maybe_other_package: ?*Package = blk: {
- if (self.module.main_pkg_in_std and std.mem.eql(u8, path, "std")) {
+ if (self.module.main_pkg_is_std and std.mem.eql(u8, path, "std")) {
path = "root";
break :blk self.module.main_pkg;
} else {
diff --git a/src/Compilation.zig b/src/Compilation.zig
index f0d100cb9a..7c4c369a6b 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -1588,10 +1588,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
try main_pkg.add(gpa, "root", root_pkg);
try main_pkg.addAndAdopt(gpa, "std", std_pkg);
- const main_pkg_in_std = m: {
+ const main_pkg_is_std = m: {
const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
std_pkg.root_src_directory.path orelse ".",
- std.fs.path.dirname(std_pkg.root_src_path) orelse ".",
+ std_pkg.root_src_path,
});
defer arena.free(std_path);
const main_path = try std.fs.path.resolve(arena, &[_][]const u8{
@@ -1599,7 +1599,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
main_pkg.root_src_path,
});
defer arena.free(main_path);
- break :m mem.startsWith(u8, main_path, std_path);
+ break :m mem.eql(u8, main_path, std_path);
};
// Pre-open the directory handles for cached ZIR code so that it does not need
@@ -1638,7 +1638,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.gpa = gpa,
.comp = comp,
.main_pkg = main_pkg,
- .main_pkg_in_std = main_pkg_in_std,
+ .main_pkg_is_std = main_pkg_is_std,
.root_pkg = root_pkg,
.zig_cache_artifact_directory = zig_cache_artifact_directory,
.global_zir_cache = global_zir_cache,
@@ -4207,14 +4207,6 @@ pub fn addCCArgs(
else => {},
}
- if (!comp.bin_file.options.strip) {
- switch (target.ofmt) {
- .coff => try argv.append("-gcodeview"),
- .elf, .macho => try argv.append("-gdwarf-4"),
- else => try argv.append("-g"),
- }
- }
-
if (target.cpu.arch.isThumb()) {
try argv.append("-mthumb");
}
@@ -4356,6 +4348,14 @@ pub fn addCCArgs(
},
}
+ if (!comp.bin_file.options.strip) {
+ switch (target.ofmt) {
+ .coff => try argv.append("-gcodeview"),
+ .elf, .macho => try argv.append("-gdwarf-4"),
+ else => try argv.append("-g"),
+ }
+ }
+
if (target_util.llvmMachineAbi(target)) |mabi| {
try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi}));
}
diff --git a/src/Module.zig b/src/Module.zig
index 6056c385e3..81a9ec220b 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -142,7 +142,7 @@ job_queued_update_builtin_zig: bool = true,
/// This makes it so that we can run `zig test` on the standard library.
/// Otherwise, the logic for scanning test decls skips all of them because
/// `main_pkg != std_pkg`.
-main_pkg_in_std: bool,
+main_pkg_is_std: bool,
compile_log_text: ArrayListUnmanaged(u8) = .{},
@@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {
const case_nodes = tree.extra_data[extra.start..extra.end];
for (case_nodes) |case_node| {
const case = switch (node_tags[case_node]) {
- .switch_case_one => tree.switchCaseOne(case_node),
- .switch_case => tree.switchCase(case_node),
+ .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+ .switch_case, .switch_case_inline => tree.switchCase(case_node),
else => unreachable,
};
const is_special = (case.ast.values.len == 0) or
@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {
const case_nodes = tree.extra_data[extra.start..extra.end];
for (case_nodes) |case_node| {
const case = switch (node_tags[case_node]) {
- .switch_case_one => tree.switchCaseOne(case_node),
- .switch_case => tree.switchCase(case_node),
+ .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+ .switch_case, .switch_case_inline => tree.switchCase(case_node),
else => unreachable,
};
const is_special = (case.ast.values.len == 0) or
@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {
const case_node = src_loc.declRelativeToNodeIndex(node_off);
const node_tags = tree.nodes.items(.tag);
const case = switch (node_tags[case_node]) {
- .switch_case_one => tree.switchCaseOne(case_node),
- .switch_case => tree.switchCase(case_node),
+ .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+ .switch_case, .switch_case_inline => tree.switchCase(case_node),
else => unreachable,
};
const start_tok = case.payload_token.?;
@@ -4430,6 +4430,8 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
new_decl.has_linksection_or_addrspace = false;
new_decl.ty = ty_ty;
new_decl.val = struct_val;
+ new_decl.@"align" = 0;
+ new_decl.@"linksection" = null;
new_decl.has_tv = true;
new_decl.owns_tv = true;
new_decl.alive = true; // This Decl corresponds to a File and is therefore always alive.
@@ -5172,7 +5174,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
// the test name filter.
if (!comp.bin_file.options.is_test) break :blk false;
if (decl_pkg != mod.main_pkg) {
- if (!mod.main_pkg_in_std) break :blk false;
+ if (!mod.main_pkg_is_std) break :blk false;
const std_pkg = mod.main_pkg.table.get("std").?;
if (std_pkg != decl_pkg) break :blk false;
}
@@ -5183,7 +5185,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
if (!is_named_test) break :blk false;
if (!comp.bin_file.options.is_test) break :blk false;
if (decl_pkg != mod.main_pkg) {
- if (!mod.main_pkg_in_std) break :blk false;
+ if (!mod.main_pkg_is_std) break :blk false;
const std_pkg = mod.main_pkg.table.get("std").?;
if (std_pkg != decl_pkg) break :blk false;
}
@@ -5938,8 +5940,8 @@ pub const SwitchProngSrc = union(enum) {
var scalar_i: u32 = 0;
for (case_nodes) |case_node| {
const case = switch (node_tags[case_node]) {
- .switch_case_one => tree.switchCaseOne(case_node),
- .switch_case => tree.switchCase(case_node),
+ .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
+ .switch_case, .switch_case_inline => tree.switchCase(case_node),
else => unreachable,
};
if (case.ast.values.len == 0)
diff --git a/src/Sema.zig b/src/Sema.zig
index aed09d6201..c185752dc0 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -162,6 +162,9 @@ pub const Block = struct {
/// type of `err` in `else => |err|`
switch_else_err_ty: ?Type = null,
+ /// Value for switch_capture in an inline case
+ inline_case_capture: Air.Inst.Ref = .none,
+
const Param = struct {
/// `noreturn` means `anytype`.
ty: Type,
@@ -603,6 +606,21 @@ fn resolveBody(
return try sema.resolveInst(break_data.operand);
}
+fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
+ _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
+ error.ComptimeBreak => {
+ const zir_datas = sema.code.instructions.items(.data);
+ const break_data = zir_datas[sema.comptime_break_inst].@"break";
+ try sema.addRuntimeBreak(block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
+}
+
pub fn analyzeBody(
sema: *Sema,
block: *Block,
@@ -796,6 +814,7 @@ fn analyzeBodyInner(
.switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
.switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
.switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
+ .switch_capture_tag => try sema.zirSwitchCaptureTag(block, inst),
.type_info => try sema.zirTypeInfo(block, inst),
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),
@@ -8107,6 +8126,13 @@ fn funcCommon(
for (comptime_params) |ct| is_generic = is_generic or ct;
is_generic = is_generic or ret_ty_requires_comptime;
+ if (!is_generic and sema.wantErrorReturnTracing(return_type)) {
+ // Make sure that StackTrace's fields are resolved so that the backend can
+ // lower this fn type.
+ const unresolved_stack_trace_ty = try sema.getBuiltinType(block, ret_ty_src, "StackTrace");
+ _ = try sema.resolveTypeFields(block, ret_ty_src, unresolved_stack_trace_ty);
+ }
+
break :fn_ty try Type.Tag.function.create(sema.arena, .{
.param_types = param_types,
.comptime_params = comptime_params.ptr,
@@ -9023,13 +9049,38 @@ fn zirSwitchCapture(
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
- const operand_is_ref = switch_extra.data.bits.is_ref;
const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
- const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
+ const cond_info = zir_datas[cond_inst].un_node;
+ const cond_tag = sema.code.instructions.items(.tag)[cond_inst];
+ const operand_is_ref = cond_tag == .switch_cond_ref;
const operand_ptr = try sema.resolveInst(cond_info.operand);
const operand_ptr_ty = sema.typeOf(operand_ptr);
const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
+ if (block.inline_case_capture != .none) {
+ const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable;
+ if (operand_ty.zigTypeTag() == .Union) {
+ const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?);
+ const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
+ const field_ty = union_obj.fields.values()[field_index].ty;
+ if (is_ref) {
+ const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = field_ty,
+ .mutable = operand_ptr_ty.ptrIsMutable(),
+ .@"volatile" = operand_ptr_ty.isVolatilePtr(),
+ .@"addrspace" = operand_ptr_ty.ptrAddressSpace(),
+ });
+ return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
+ } else {
+ return block.addStructFieldVal(operand_ptr, field_index, field_ty);
+ }
+ } else if (is_ref) {
+ return sema.addConstantMaybeRef(block, operand_src, operand_ty, item_val, true);
+ } else {
+ return block.inline_case_capture;
+ }
+ }
+
const operand = if (operand_is_ref)
try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
else
@@ -9038,7 +9089,6 @@ fn zirSwitchCapture(
if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
// It is the else/`_` prong.
if (is_ref) {
- assert(operand_is_ref);
return operand_ptr;
}
@@ -9098,8 +9148,6 @@ fn zirSwitchCapture(
}
if (is_ref) {
- assert(operand_is_ref);
-
const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = first_field.ty,
.@"addrspace" = .generic,
@@ -9160,7 +9208,6 @@ fn zirSwitchCapture(
// In this case the capture value is just the passed-through value of the
// switch condition.
if (is_ref) {
- assert(operand_is_ref);
return operand_ptr;
} else {
return operand;
@@ -9169,6 +9216,33 @@ fn zirSwitchCapture(
}
}
+fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const zir_datas = sema.code.instructions.items(.data);
+ const inst_data = zir_datas[inst].un_tok;
+ const src = inst_data.src();
+
+ const switch_tag = sema.code.instructions.items(.tag)[Zir.refToIndex(inst_data.operand).?];
+ const is_ref = switch_tag == .switch_cond_ref;
+ const cond_data = zir_datas[Zir.refToIndex(inst_data.operand).?].un_node;
+ const operand_ptr = try sema.resolveInst(cond_data.operand);
+ const operand_ptr_ty = sema.typeOf(operand_ptr);
+ const operand_ty = if (is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
+
+ if (operand_ty.zigTypeTag() != .Union) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, operand_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+
+ return block.inline_case_capture;
+}
+
fn zirSwitchCond(
sema: *Sema,
block: *Block,
@@ -9266,14 +9340,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
} else 0;
const special_prong = extra.data.bits.specialProng();
- const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
- .none => .{ .body = &.{}, .end = header_extra_index },
+ const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
+ .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
.under, .@"else" => blk: {
- const body_len = sema.code.extra[header_extra_index];
+ const body_len = @truncate(u31, sema.code.extra[header_extra_index]);
const extra_body_start = header_extra_index + 1;
break :blk .{
.body = sema.code.extra[extra_body_start..][0..body_len],
.end = extra_body_start + body_len,
+ .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
};
},
};
@@ -9285,8 +9360,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
break :blk sema.typeOf(raw_operand);
};
const union_originally = maybe_union_ty.zigTypeTag() == .Union;
- var seen_union_fields: []?Module.SwitchProngSrc = &.{};
- defer gpa.free(seen_union_fields);
+
+ // Duplicate checking variables later also used for `inline else`.
+ var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
+ var seen_errors = SwitchErrorSet.init(gpa);
+ var range_set = RangeSet.init(gpa, sema.mod);
+ var true_count: u8 = 0;
+ var false_count: u8 = 0;
+
+ defer {
+ range_set.deinit();
+ gpa.free(seen_enum_fields);
+ seen_errors.deinit();
+ }
var empty_enum = false;
@@ -9323,15 +9409,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
switch (operand_ty.zigTypeTag()) {
.Union => unreachable, // handled in zirSwitchCond
.Enum => {
- var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
- empty_enum = seen_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
- defer if (!union_originally) gpa.free(seen_fields);
- if (union_originally) seen_union_fields = seen_fields;
- mem.set(?Module.SwitchProngSrc, seen_fields, null);
-
- // This is used for non-exhaustive enum values that do not correspond to any tags.
- var range_set = RangeSet.init(gpa, sema.mod);
- defer range_set.deinit();
+ seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
+ empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
+ mem.set(?Module.SwitchProngSrc, seen_enum_fields, null);
+ // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
var extra_index: usize = special.end;
{
@@ -9339,13 +9420,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
try sema.validateSwitchItemEnum(
block,
- seen_fields,
+ seen_enum_fields,
&range_set,
item_ref,
src_node_offset,
@@ -9360,7 +9441,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9368,7 +9449,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
for (items) |item_ref, item_i| {
try sema.validateSwitchItemEnum(
block,
- seen_fields,
+ seen_enum_fields,
&range_set,
item_ref,
src_node_offset,
@@ -9379,7 +9460,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
}
}
- const all_tags_handled = for (seen_fields) |seen_src| {
+ const all_tags_handled = for (seen_enum_fields) |seen_src| {
if (seen_src == null) break false;
} else true;
@@ -9399,7 +9480,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.{},
);
errdefer msg.destroy(sema.gpa);
- for (seen_fields) |seen_src, i| {
+ for (seen_enum_fields) |seen_src, i| {
if (seen_src != null) continue;
const field_name = operand_ty.enumFieldName(i);
@@ -9430,16 +9511,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
},
.ErrorSet => {
- var seen_errors = SwitchErrorSet.init(gpa);
- defer seen_errors.deinit();
-
var extra_index: usize = special.end;
{
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9459,7 +9537,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9572,16 +9650,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
},
.Int, .ComptimeInt => {
- var range_set = RangeSet.init(gpa, sema.mod);
- defer range_set.deinit();
-
var extra_index: usize = special.end;
{
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9602,7 +9677,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
@@ -9670,16 +9745,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
},
.Bool => {
- var true_count: u8 = 0;
- var false_count: u8 = 0;
-
var extra_index: usize = special.end;
{
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9700,7 +9772,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9764,7 +9836,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9784,7 +9856,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9864,7 +9936,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
@@ -9885,7 +9957,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
@@ -9926,7 +9998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
}
- if (scalar_cases_len + multi_cases_len == 0) {
+ if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
if (empty_enum) {
return Air.Inst.Ref.void_value;
}
@@ -9958,7 +10030,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const is_inline = sema.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
@@ -9968,8 +10041,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
+ case_block.inline_case_capture = .none;
const item = try sema.resolveInst(item_ref);
+ if (is_inline) case_block.inline_case_capture = item;
// `item` is already guaranteed to be constant known.
const analyze_body = if (union_originally) blk: {
@@ -9981,18 +10056,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else if (analyze_body) {
- _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
- error.ComptimeBreak => {
- const zir_datas = sema.code.instructions.items(.data);
- const break_data = zir_datas[sema.comptime_break_inst].@"break";
- try sema.addRuntimeBreak(&case_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -10014,19 +10078,115 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
defer gpa.free(prev_then_body);
var cases_len = scalar_cases_len;
- var multi_i: usize = 0;
+ var multi_i: u32 = 0;
while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = sema.code.extra[extra_index];
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const is_inline = sema.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
+ case_block.inline_case_capture = .none;
+
+ // Generate all possible cases as scalar prongs.
+ if (is_inline) {
+ const body_start = extra_index + 2 * ranges_len;
+ const body = sema.code.extra[body_start..][0..body_len];
+ var emit_bb = false;
+
+ var range_i: u32 = 0;
+ while (range_i < ranges_len) : (range_i += 1) {
+ const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+
+ const item_first_ref = try sema.resolveInst(first_ref);
+ var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
+ const item_last_ref = try sema.resolveInst(last_ref);
+ const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
+
+ while (item.compare(.lte, item_last, operand_ty, sema.mod)) : ({
+ // Previous validation has resolved any possible lazy values.
+ item = try sema.intAddScalar(block, .unneeded, item, Value.one);
+ }) {
+ cases_len += 1;
+
+ const item_ref = try sema.addConstant(operand_ty, item);
+ case_block.inline_case_capture = item_ref;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } };
+ const decl = sema.mod.declPtr(case_block.src_decl);
+ try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
+ return error.AnalysisFail;
+ },
+ else => return err,
+ };
+ emit_bb = true;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ }
+
+ for (items) |item_ref, item_i| {
+ cases_len += 1;
+
+ const item = try sema.resolveInst(item_ref);
+ case_block.inline_case_capture = item;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ const analyze_body = if (union_originally) blk: {
+ const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
+ const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+ break :blk field_ty.zigTypeTag() != .NoReturn;
+ } else true;
+
+ if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } };
+ const decl = sema.mod.declPtr(case_block.src_decl);
+ try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
+ return error.AnalysisFail;
+ },
+ else => return err,
+ };
+ emit_bb = true;
+
+ if (analyze_body) {
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ } else {
+ _ = try case_block.addNoOp(.unreach);
+ }
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+
+ extra_index += body_len;
+ continue;
+ }
var any_ok: Air.Inst.Ref = .none;
@@ -10051,18 +10211,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else if (analyze_body) {
- _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
- error.ComptimeBreak => {
- const zir_datas = sema.code.instructions.items(.data);
- const break_data = zir_datas[sema.comptime_break_inst].@"break";
- try sema.addRuntimeBreak(&case_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -10143,18 +10292,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else {
- _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
- error.ComptimeBreak => {
- const zir_datas = sema.code.instructions.items(.data);
- const break_data = zir_datas[sema.comptime_break_inst].@"break";
- try sema.addRuntimeBreak(&case_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
}
try wip_captures.finalize();
@@ -10185,14 +10323,150 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var final_else_body: []const Air.Inst.Index = &.{};
if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
+ var emit_bb = false;
+ if (special.is_inline) switch (operand_ty.zigTypeTag()) {
+ .Enum => {
+ if (operand_ty.isNonexhaustiveEnum() and !union_originally) {
+ return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ }
+ for (seen_enum_fields) |f, i| {
+ if (f != null) continue;
+ cases_len += 1;
+
+ const item_val = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
+ const item_ref = try sema.addConstant(operand_ty, item_val);
+ case_block.inline_case_capture = item_ref;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ const analyze_body = if (union_originally) blk: {
+ const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+ break :blk field_ty.zigTypeTag() != .NoReturn;
+ } else true;
+
+ if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+ emit_bb = true;
+
+ if (analyze_body) {
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ } else {
+ _ = try case_block.addNoOp(.unreach);
+ }
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ .ErrorSet => {
+ if (operand_ty.isAnyError()) {
+ return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ }
+ for (operand_ty.errorSetNames()) |error_name| {
+ if (seen_errors.contains(error_name)) continue;
+ cases_len += 1;
+
+ const item_val = try Value.Tag.@"error".create(sema.arena, .{ .name = error_name });
+ const item_ref = try sema.addConstant(operand_ty, item_val);
+ case_block.inline_case_capture = item_ref;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+ emit_bb = true;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ .Int => {
+ var it = try RangeSetUnhandledIterator.init(sema, block, special_prong_src, operand_ty, range_set);
+ while (try it.next()) |cur| {
+ cases_len += 1;
+
+ const item_ref = try sema.addConstant(operand_ty, cur);
+ case_block.inline_case_capture = item_ref;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+ emit_bb = true;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ .Bool => {
+ if (true_count == 0) {
+ cases_len += 1;
+ case_block.inline_case_capture = Air.Inst.Ref.bool_true;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+ emit_bb = true;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ if (false_count == 0) {
+ cases_len += 1;
+ case_block.inline_case_capture = Air.Inst.Ref.bool_false;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+ emit_bb = true;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+ cases_extra.appendAssumeCapacity(1); // items_len
+ cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+ cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
+ operand_ty.fmt(sema.mod),
+ }),
+ };
+
var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
defer wip_captures.deinit();
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
+ case_block.inline_case_capture = .none;
- const analyze_body = if (union_originally)
- for (seen_union_fields) |seen_field, index| {
+ const analyze_body = if (union_originally and !special.is_inline)
+ for (seen_enum_fields) |seen_field, index| {
if (seen_field != null) continue;
const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;
const field_ty = union_obj.fields.values()[index].ty;
@@ -10204,19 +10478,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try sema.maybeErrorUnwrap(&case_block, special.body, operand))
{
// nothing to do here
- } else if (special.body.len != 0 and analyze_body) {
- _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
- error.ComptimeBreak => {
- const zir_datas = sema.code.instructions.items(.data);
- const break_data = zir_datas[sema.comptime_break_inst].@"break";
- try sema.addRuntimeBreak(&case_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
} else {
// We still need a terminator in this block, but we have proven
// that it is unreachable.
@@ -10262,6 +10525,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.analyzeBlockBody(block, src, &child_block, merges);
}
+const RangeSetUnhandledIterator = struct {
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ty: Type,
+ cur: Value,
+ max: Value,
+ ranges: []const RangeSet.Range,
+ range_i: usize = 0,
+ first: bool = true,
+
+ fn init(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
+ const target = sema.mod.getTarget();
+ const min = try ty.minInt(sema.arena, target);
+ const max = try ty.maxInt(sema.arena, target);
+
+ return RangeSetUnhandledIterator{
+ .sema = sema,
+ .block = block,
+ .src = src,
+ .ty = ty,
+ .cur = min,
+ .max = max,
+ .ranges = range_set.ranges.items,
+ };
+ }
+
+ fn next(it: *RangeSetUnhandledIterator) !?Value {
+ while (it.range_i < it.ranges.len) : (it.range_i += 1) {
+ if (!it.first) {
+ it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
+ }
+ it.first = false;
+ if (it.cur.compare(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
+ return it.cur;
+ }
+ it.cur = it.ranges[it.range_i].last;
+ }
+ if (!it.first) {
+ it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
+ }
+ it.first = false;
+ if (it.cur.compare(.lte, it.max, it.ty, it.sema.mod)) {
+ return it.cur;
+ }
+ return null;
+ }
+};
+
fn resolveSwitchItemVal(
sema: *Sema,
block: *Block,
@@ -15344,18 +15656,7 @@ fn zirCondbr(
sub_block.runtime_index.increment();
defer sub_block.instructions.deinit(gpa);
- _ = sema.analyzeBodyInner(&sub_block, then_body) catch |err| switch (err) {
- error.ComptimeBreak => {
- const zir_datas = sema.code.instructions.items(.data);
- const break_data = zir_datas[sema.comptime_break_inst].@"break";
- try sema.addRuntimeBreak(&sub_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
defer gpa.free(true_instructions);
@@ -15374,18 +15675,7 @@ fn zirCondbr(
if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
// nothing to do
} else {
- _ = sema.analyzeBodyInner(&sub_block, else_body) catch |err| switch (err) {
- error.ComptimeBreak => {
- const zir_datas = sema.code.instructions.items(.data);
- const break_data = zir_datas[sema.comptime_break_inst].@"break";
- try sema.addRuntimeBreak(&sub_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
}
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
true_instructions.len + sub_block.instructions.items.len);
@@ -15631,7 +15921,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
return sema.analyzeRet(block, operand, src);
}
- if (sema.wantErrorReturnTracing()) {
+ if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
return retWithErrTracing(sema, block, src, is_non_err, .ret_load, ret_ptr);
}
@@ -15698,11 +15988,11 @@ fn retWithErrTracing(
return always_noreturn;
}
-fn wantErrorReturnTracing(sema: *Sema) bool {
+fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
// TODO implement this feature in all the backends and then delete this check.
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
- return sema.fn_ret_ty.isError() and
+ return fn_ret_ty.isError() and
sema.mod.comp.bin_file.options.error_return_tracing and
backend_supports_error_return_tracing;
}
@@ -15754,7 +16044,7 @@ fn analyzeRet(
try sema.resolveTypeLayout(block, src, sema.fn_ret_ty);
- if (sema.wantErrorReturnTracing()) {
+ if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
// Avoid adding a frame to the error return trace in case the value is comptime-known
// to be not an error.
const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
@@ -17682,6 +17972,10 @@ fn reifyStruct(
}
const abi_align = @intCast(u29, (try alignment_val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?);
+ if (layout == .Packed and abi_align != 0) {
+ return sema.fail(block, src, "alignment in a packed struct field must be set to 0", .{});
+ }
+
const field_name = try name_val.toAllocatedBytes(
Type.initTag(.const_slice_u8),
new_decl_arena_allocator,
@@ -24308,7 +24602,7 @@ fn coerceInMemoryAllowedFns(
if (dest_info.param_types.len != src_info.param_types.len) {
return InMemoryCoercionResult{ .fn_param_count = .{
- .actual = dest_info.param_types.len,
+ .actual = src_info.param_types.len,
.wanted = dest_info.param_types.len,
} };
}
diff --git a/src/Zir.zig b/src/Zir.zig
index 5b1aefea64..add8bad801 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -683,6 +683,9 @@ pub const Inst = struct {
/// Result is a pointer to the value.
/// Uses the `switch_capture` field.
switch_capture_multi_ref,
+ /// Produces the capture value for an inline switch prong tag capture.
+ /// Uses the `un_tok` field.
+ switch_capture_tag,
/// Given a
/// *A returns *A
/// *E!A returns *A
@@ -1128,6 +1131,7 @@ pub const Inst = struct {
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
+ .switch_capture_tag,
.switch_block,
.switch_cond,
.switch_cond_ref,
@@ -1422,6 +1426,7 @@ pub const Inst = struct {
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
+ .switch_capture_tag,
.switch_block,
.switch_cond,
.switch_cond_ref,
@@ -1681,6 +1686,7 @@ pub const Inst = struct {
.switch_capture_ref = .switch_capture,
.switch_capture_multi = .switch_capture,
.switch_capture_multi_ref = .switch_capture,
+ .switch_capture_tag = .un_tok,
.array_base_ptr = .un_node,
.field_base_ptr = .un_node,
.validate_array_init_ty = .pl_node,
@@ -2952,12 +2958,9 @@ pub const Inst = struct {
has_else: bool,
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
has_under: bool,
- /// If true, the `operand` is a pointer to the value being switched on.
- /// TODO this flag is redundant with the tag of operand and can be removed.
- is_ref: bool,
scalar_cases_len: ScalarCasesLen,
- pub const ScalarCasesLen = u28;
+ pub const ScalarCasesLen = u29;
pub fn specialProng(bits: Bits) SpecialProng {
const has_else: u2 = @boolToInt(bits.has_else);
@@ -2993,7 +2996,7 @@ pub const Inst = struct {
}
if (self.bits.specialProng() != .none) {
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
@@ -3003,7 +3006,7 @@ pub const Inst = struct {
while (true) : (scalar_i += 1) {
const item = @intToEnum(Ref, zir.extra[extra_index]);
extra_index += 1;
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
@@ -3032,7 +3035,7 @@ pub const Inst = struct {
var extra_index: usize = extra_end + 1;
if (self.bits.specialProng() != .none) {
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
@@ -3041,7 +3044,7 @@ pub const Inst = struct {
var scalar_i: usize = 0;
while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
extra_index += 1;
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
}
@@ -3049,7 +3052,7 @@ pub const Inst = struct {
while (true) : (multi_i += 1) {
const items_len = zir.extra[extra_index];
extra_index += 2;
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const items = zir.refSlice(extra_index, items_len);
extra_index += items_len;
@@ -3861,7 +3864,7 @@ fn findDeclsSwitch(
const special_prong = extra.data.bits.specialProng();
if (special_prong != .none) {
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body.len;
@@ -3874,7 +3877,7 @@ fn findDeclsSwitch(
var scalar_i: usize = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
extra_index += 1;
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
extra_index += body_len;
@@ -3889,7 +3892,7 @@ fn findDeclsSwitch(
extra_index += 1;
const ranges_len = zir.extra[extra_index];
extra_index += 1;
- const body_len = zir.extra[extra_index];
+ const body_len = @truncate(u31, zir.extra[extra_index]);
extra_index += 1;
const items = zir.refSlice(extra_index, items_len);
extra_index += items_len;
diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
index 0cdc7a4c5f..c1c00d8303 100644
--- a/src/arch/x86_64/Emit.zig
+++ b/src/arch/x86_64/Emit.zig
@@ -2159,7 +2159,7 @@ const RegisterOrMemory = union(enum) {
/// Returns size in bits.
fn size(reg_or_mem: RegisterOrMemory) u64 {
return switch (reg_or_mem) {
- .register => |reg| reg.size(),
+ .register => |register| register.size(),
.memory => |memory| memory.size(),
};
}
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 1b793265da..a24c14c9c6 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -2332,10 +2332,13 @@ pub const Object = struct {
// buffer is only used for int_type, `builtin` is a struct.
const builtin_ty = mod.declPtr(builtin_decl).val.toType(undefined);
const builtin_namespace = builtin_ty.getNamespace().?;
- const stack_trace_decl = builtin_namespace.decls
+ const stack_trace_decl_index = builtin_namespace.decls
.getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?;
+ const stack_trace_decl = mod.declPtr(stack_trace_decl_index);
- return mod.declPtr(stack_trace_decl).val.toType(undefined);
+ // Sema should have ensured that StackTrace was analyzed.
+ assert(stack_trace_decl.has_tv);
+ return stack_trace_decl.val.toType(undefined);
}
};
@@ -10277,6 +10280,7 @@ fn ccAbiPromoteInt(
else => {},
}
const int_info = switch (ty.zigTypeTag()) {
+ .Bool => Type.@"u1".intInfo(target),
.Int, .Enum, .ErrorSet => ty.intInfo(target),
else => return null,
};
diff --git a/src/print_zir.zig b/src/print_zir.zig
index b273365596..d383664c16 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -237,6 +237,7 @@ const Writer = struct {
.ret_tok,
.ensure_err_payload_void,
.closure_capture,
+ .switch_capture_tag,
=> try self.writeUnTok(stream, inst),
.bool_br_and,
@@ -1857,7 +1858,6 @@ const Writer = struct {
} else 0;
try self.writeInstRef(stream, extra.data.operand);
- try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
self.indent += 2;
@@ -1869,14 +1869,15 @@ const Writer = struct {
else => break :else_prong,
};
- const body_len = self.code.extra[extra_index];
+ const body_len = @truncate(u31, self.code.extra[extra_index]);
+ const inline_text = if (self.code.extra[extra_index] >> 31 != 0) "inline " else "";
extra_index += 1;
const body = self.code.extra[extra_index..][0..body_len];
extra_index += body.len;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
- try stream.print("{s} => ", .{prong_name});
+ try stream.print("{s}{s} => ", .{ inline_text, prong_name });
try self.writeBracedBody(stream, body);
}
@@ -1886,13 +1887,15 @@ const Writer = struct {
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
extra_index += 1;
- const body_len = self.code.extra[extra_index];
+ const body_len = @truncate(u31, self.code.extra[extra_index]);
+ const is_inline = self.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const body = self.code.extra[extra_index..][0..body_len];
extra_index += body_len;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
+ if (is_inline) try stream.writeAll("inline ");
try self.writeInstRef(stream, item_ref);
try stream.writeAll(" => ");
try self.writeBracedBody(stream, body);
@@ -1905,13 +1908,15 @@ const Writer = struct {
extra_index += 1;
const ranges_len = self.code.extra[extra_index];
extra_index += 1;
- const body_len = self.code.extra[extra_index];
+ const body_len = @truncate(u31, self.code.extra[extra_index]);
+ const is_inline = self.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const items = self.code.refSlice(extra_index, items_len);
extra_index += items_len;
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
+ if (is_inline) try stream.writeAll("inline ");
for (items) |item_ref, item_i| {
if (item_i != 0) try stream.writeAll(", ");
diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp
index 5f216fe388..d4a2abece9 100644
--- a/src/stage1/all_types.hpp
+++ b/src/stage1/all_types.hpp
@@ -1039,6 +1039,7 @@ struct AstNodeSwitchProng {
AstNode *expr;
bool var_is_ptr;
bool any_items_are_range;
+ bool is_inline;
};
struct AstNodeSwitchRange {
diff --git a/src/stage1/astgen.cpp b/src/stage1/astgen.cpp
index 54d9c969a5..9eea2e650e 100644
--- a/src/stage1/astgen.cpp
+++ b/src/stage1/astgen.cpp
@@ -6987,6 +6987,12 @@ static bool astgen_switch_prong_expr(Stage1AstGen *ag, Scope *scope, AstNode *sw
assert(switch_node->type == NodeTypeSwitchExpr);
assert(prong_node->type == NodeTypeSwitchProng);
+ if (prong_node->data.switch_prong.is_inline) {
+ exec_add_error_node(ag->codegen, ag->exec, prong_node,
+ buf_sprintf("inline switch cases not supported by stage1"));
+ return ag->codegen->invalid_inst_src;
+ }
+
AstNode *expr_node = prong_node->data.switch_prong.expr;
AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
Scope *child_scope;
diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp
index bd778484cb..ec02e6fa8b 100644
--- a/src/stage1/parser.cpp
+++ b/src/stage1/parser.cpp
@@ -2306,17 +2306,17 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
return Optional<PtrIndexPayload>::some(res);
}
-// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
+// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
static AstNode *ast_parse_switch_prong(ParseContext *pc) {
AstNode *res = ast_parse_switch_case(pc);
if (res == nullptr)
return nullptr;
expect_token(pc, TokenIdFatArrow);
- Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
+ Optional<PtrIndexPayload> opt_payload = ast_parse_ptr_index_payload(pc);
AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
- PtrPayload payload;
+ PtrIndexPayload payload;
assert(res->type == NodeTypeSwitchProng);
res->data.switch_prong.expr = expr;
if (opt_payload.unwrap(&payload)) {
@@ -2331,9 +2331,11 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {
// <- SwitchItem (COMMA SwitchItem)* COMMA?
// / KEYWORD_else
static AstNode *ast_parse_switch_case(ParseContext *pc) {
+ bool is_inline = eat_token_if(pc, TokenIdKeywordInline) != 0;
AstNode *first = ast_parse_switch_item(pc);
if (first != nullptr) {
AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first);
+ res->data.switch_prong.is_inline = is_inline;
res->data.switch_prong.items.append(first);
res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;
@@ -2350,9 +2352,13 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
}
TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);
- if (else_token != 0)
- return ast_create_node(pc, NodeTypeSwitchProng, else_token);
+ if (else_token != 0) {
+ AstNode *res = ast_create_node(pc, NodeTypeSwitchProng, else_token);
+ res->data.switch_prong.is_inline = is_inline;
+ return res;
+ }
+ if (is_inline) pc->current_token -= 1;
return nullptr;
}
diff --git a/test/behavior.zig b/test/behavior.zig
index ed7910cfbc..8f4657e634 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -182,6 +182,7 @@ test {
_ = @import("behavior/decltest.zig");
_ = @import("behavior/packed_struct_explicit_backing_int.zig");
_ = @import("behavior/empty_union.zig");
+ _ = @import("behavior/inline_switch.zig");
}
if (builtin.os.tag != .wasi) {
diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig
new file mode 100644
index 0000000000..ecc7bba280
--- /dev/null
+++ b/test/behavior/inline_switch.zig
@@ -0,0 +1,131 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const builtin = @import("builtin");
+
+test "inline scalar prongs" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ var x: usize = 0;
+ switch (x) {
+ 10 => |*item| try expect(@TypeOf(item) == *usize),
+ inline 11 => |*item| {
+ try expect(@TypeOf(item) == *const usize);
+ try expect(item.* == 11);
+ },
+ else => {},
+ }
+}
+
+test "inline prong ranges" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ var x: usize = 0;
+ switch (x) {
+ inline 0...20, 24 => |item| {
+ if (item > 25) @compileError("bad");
+ },
+ else => {},
+ }
+}
+
+const E = enum { a, b, c, d };
+test "inline switch enums" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ var x: E = .a;
+ switch (x) {
+ inline .a, .b => |aorb| if (aorb != .a and aorb != .b) @compileError("bad"),
+ inline .c, .d => |cord| if (cord != .c and cord != .d) @compileError("bad"),
+ }
+}
+
+const U = union(E) { a: void, b: u2, c: u3, d: u4 };
+test "inline switch unions" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+ var x: U = .a;
+ switch (x) {
+ inline .a, .b => |aorb, tag| {
+ if (tag == .a) {
+ try expect(@TypeOf(aorb) == void);
+ } else {
+ try expect(tag == .b);
+ try expect(@TypeOf(aorb) == u2);
+ }
+ },
+ inline .c, .d => |cord, tag| {
+ if (tag == .c) {
+ try expect(@TypeOf(cord) == u3);
+ } else {
+ try expect(tag == .d);
+ try expect(@TypeOf(cord) == u4);
+ }
+ },
+ }
+}
+
+test "inline else bool" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ var a = true;
+ switch (a) {
+ true => {},
+ inline else => |val| if (val != false) @compileError("bad"),
+ }
+}
+
+test "inline else error" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ const Err = error{ a, b, c };
+ var a = Err.a;
+ switch (a) {
+ error.a => {},
+ inline else => |val| comptime if (val == error.a) @compileError("bad"),
+ }
+}
+
+test "inline else enum" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
+ const E2 = enum(u8) { a = 2, b = 3, c = 4, d = 5 };
+ var a: E2 = .a;
+ switch (a) {
+ .a, .b => {},
+ inline else => |val| comptime if (@enumToInt(val) < 4) @compileError("bad"),
+ }
+}
+
+test "inline else int with gaps" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ var a: u8 = 0;
+ switch (a) {
+ 1...125, 128...254 => {},
+ inline else => |val| {
+ if (val != 0 and
+ val != 126 and
+ val != 127 and
+ val != 255)
+ @compileError("bad");
+ },
+ }
+}
+
+test "inline else int all values" {
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ var a: u2 = 0;
+ switch (a) {
+ inline else => |val| {
+ if (val != 0 and
+ val != 1 and
+ val != 2 and
+ val != 3)
+ @compileError("bad");
+ },
+ }
+}
diff --git a/test/cases/compile_errors/function_type_coercion.zig b/test/cases/compile_errors/function_type_coercion.zig
new file mode 100644
index 0000000000..552995304d
--- /dev/null
+++ b/test/cases/compile_errors/function_type_coercion.zig
@@ -0,0 +1,21 @@
+fn f(_: i32) void {}
+export fn wrong_param_count() void {
+ _ = @as(fn () void, f);
+}
+export fn wrong_param_type() void {
+ _ = @as(fn (f32) void, f);
+}
+export fn wrong_return_type() void {
+ _ = @as(fn () i32, f);
+}
+
+// error
+// backend=stage2,llvm
+// target=native
+//
+// :3:25: error: expected type 'fn() void', found 'fn(i32) void'
+// :3:25: note: function with 1 parameters cannot cast into a function with 0 parameters
+// :6:28: error: expected type 'fn(f32) void', found 'fn(i32) void'
+// :6:28: note: parameter 0 'i32' cannot cast into 'f32'
+// :9:24: error: expected type 'fn() i32', found 'fn(i32) void'
+// :9:24: note: return type 'void' cannot cast into return type 'i32'
diff --git a/test/cases/compile_errors/inline_underscore_prong.zig b/test/cases/compile_errors/inline_underscore_prong.zig
new file mode 100644
index 0000000000..12e20e65bc
--- /dev/null
+++ b/test/cases/compile_errors/inline_underscore_prong.zig
@@ -0,0 +1,15 @@
+const E = enum(u8) { a, b, c, d, _ };
+pub export fn entry() void {
+ var x: E = .a;
+ switch (x) {
+ inline .a, .b => |aorb| @compileLog(aorb),
+ .c, .d => |cord| @compileLog(cord),
+ inline _ => {},
+ }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :7:16: error: cannot inline '_' prong
diff --git a/test/cases/compile_errors/invalid_inline_else_type.zig b/test/cases/compile_errors/invalid_inline_else_type.zig
new file mode 100644
index 0000000000..2d52fca43e
--- /dev/null
+++ b/test/cases/compile_errors/invalid_inline_else_type.zig
@@ -0,0 +1,27 @@
+pub export fn entry1() void {
+ var a: anyerror = undefined;
+ switch (a) {
+ inline else => {},
+ }
+}
+const E = enum(u8) { a, _ };
+pub export fn entry2() void {
+ var a: E = undefined;
+ switch (a) {
+ inline else => {},
+ }
+}
+pub export fn entry3() void {
+ var a: *u32 = undefined;
+ switch (a) {
+ inline else => {},
+ }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :4:21: error: cannot enumerate values of type 'anyerror' for 'inline else'
+// :11:21: error: cannot enumerate values of type 'tmp.E' for 'inline else'
+// :17:21: error: cannot enumerate values of type '*u32' for 'inline else'
diff --git a/test/cases/compile_errors/invalid_tag_capture.zig b/test/cases/compile_errors/invalid_tag_capture.zig
new file mode 100644
index 0000000000..2cb9135792
--- /dev/null
+++ b/test/cases/compile_errors/invalid_tag_capture.zig
@@ -0,0 +1,15 @@
+const E = enum { a, b, c, d };
+pub export fn entry() void {
+ var x: E = .a;
+ switch (x) {
+ inline .a, .b => |aorb, d| @compileLog(aorb, d),
+ inline .c, .d => |*cord| @compileLog(cord),
+ }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :5:33: error: cannot capture tag of non-union type 'tmp.E'
+// :1:11: note: enum declared here
diff --git a/test/cases/compile_errors/packed_struct_field_alignment_unavailable_for_reify_type.zig b/test/cases/compile_errors/packed_struct_field_alignment_unavailable_for_reify_type.zig
new file mode 100644
index 0000000000..8c17e9100b
--- /dev/null
+++ b/test/cases/compile_errors/packed_struct_field_alignment_unavailable_for_reify_type.zig
@@ -0,0 +1,11 @@
+export fn entry() void {
+ _ = @Type(.{ .Struct = .{ .layout = .Packed, .fields = &.{
+ .{ .name = "one", .field_type = u4, .default_value = null, .is_comptime = false, .alignment = 2 },
+ }, .decls = &.{}, .is_tuple = false } });
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:9: error: alignment in a packed struct field must be set to 0
diff --git a/test/cases/compile_errors/tag_capture_on_non_inline_prong.zig b/test/cases/compile_errors/tag_capture_on_non_inline_prong.zig
new file mode 100644
index 0000000000..b525aa4db3
--- /dev/null
+++ b/test/cases/compile_errors/tag_capture_on_non_inline_prong.zig
@@ -0,0 +1,14 @@
+const E = enum { a, b, c, d };
+pub export fn entry() void {
+ var x: E = .a;
+ switch (x) {
+ .a, .b => |aorb, d| @compileLog(aorb, d),
+ inline .c, .d => |*cord| @compileLog(cord),
+ }
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :5:26: error: tag capture on non-inline prong
diff --git a/test/standalone.zig b/test/standalone.zig
index aa6c0f0a14..d3dedb59e6 100644
--- a/test/standalone.zig
+++ b/test/standalone.zig
@@ -97,4 +97,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
// Disabled due to tripping LLVM 13 assertion:
// https://github.com/ziglang/zig/issues/12015
//cases.add("tools/update_spirv_features.zig");
+
+ cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });
}
diff --git a/test/standalone/issue_13030/build.zig b/test/standalone/issue_13030/build.zig
new file mode 100644
index 0000000000..8c05e47cf6
--- /dev/null
+++ b/test/standalone/issue_13030/build.zig
@@ -0,0 +1,18 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const Builder = std.build.Builder;
+const CrossTarget = std.zig.CrossTarget;
+
+pub fn build(b: *Builder) void {
+ const mode = b.standardReleaseOptions();
+ const target = b.standardTargetOptions(.{});
+
+ const obj = b.addObject("main", "main.zig");
+ obj.setBuildMode(mode);
+
+ obj.setTarget(target);
+ b.default_step.dependOn(&obj.step);
+
+ const test_step = b.step("test", "Test the program");
+ test_step.dependOn(&obj.step);
+}
diff --git a/test/standalone/issue_13030/main.zig b/test/standalone/issue_13030/main.zig
new file mode 100644
index 0000000000..5e4c976db3
--- /dev/null
+++ b/test/standalone/issue_13030/main.zig
@@ -0,0 +1,7 @@
+fn b(comptime T: type) ?*const fn () error{}!T {
+ return null;
+}
+
+export fn entry() void {
+ _ = b(void);
+}