From b7b6d66675dc1eb20862b6a18ac9745287bc7e32 Mon Sep 17 00:00:00 2001 From: Kendall Condon Date: Mon, 18 May 2026 18:59:50 -0400 Subject: [PATCH 1/3] AstGen: properly advance cursor for decl qualifiers Reorders the parsing code so the expressions are parsed in the same order as in source and removes unnecessary cursor restores when parsing function qualifiers. --- lib/std/zig/AstGen.zig | 151 ++++++++++++++------------- test/cases/decl_qualifier_cursor.zig | 45 ++++++++ 2 files changed, 121 insertions(+), 75 deletions(-) create mode 100644 test/cases/decl_qualifier_cursor.zig diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 0b98fcf2b92d6b3f80e187f54f8060343fd8e24e..6a6bd0ed217906f84cebe87c1d063fbf4f2d2db8 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -3135,6 +3135,11 @@ fn varDecl( return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw}); } + const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node.unwrap()) |type_node| + try typeExpr(gz, scope, type_node) + else + .none; + const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node.unwrap()) |align_node| try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align") else @@ -3155,8 +3160,8 @@ fn varDecl( if (align_inst == .none and !astgen.nodes_need_rl.contains(node)) { - const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| .{ - .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, + const result_info: ResultInfo = if (type_inst != .none) .{ + .rl = .{ .ty = type_inst }, .ctx = .const_init, } else .{ .rl = .none, .ctx = .const_init }; const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, result_info, init_node, .dbg_var) orelse @@ -3185,8 +3190,7 @@ fn varDecl( const is_comptime = gz.is_comptime or tree.nodeTag(init_node) == .@"comptime"; - const init_rl: ResultInfo.Loc = if (var_decl.ast.type_node.unwrap()) |type_node| init_rl: { - const type_inst = try typeExpr(gz, scope, type_node); + const init_rl: ResultInfo.Loc = if (type_inst != .none) init_rl: { if (align_inst == .none) { break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; } else { @@ -3254,8 +3258,7 @@ fn varDecl( if (var_decl.comptime_token != null and gz.is_comptime) return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{}); const is_comptime = var_decl.comptime_token != null or gz.is_comptime; - const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| a: { - const type_inst = try typeExpr(gz, scope, type_node); + const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (type_inst != .none) a: { const alloc = alloc: { if (align_inst == .none) { const tag: Zir.Inst.Tag = if (is_comptime) @@ -3482,14 +3485,18 @@ fn assignDestructureMaybeDecls( // We also mark `const`s as comptime if the RHS is definitely comptime-known. const this_variable_comptime = is_comptime or (is_const and value_is_comptime); + const type_inst = if (full_var_decl.ast.type_node.unwrap()) |type_node| + try typeExpr(gz, scope, type_node) + else + .none; + const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node.unwrap()) |align_node| try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align") else .none; - if (full_var_decl.ast.type_node.unwrap()) |type_node| { + if (type_inst != .none) { // Typed alloc - const type_inst = try typeExpr(gz, scope, type_node); const ptr = if (align_inst == .none) ptr: { const tag: Zir.Inst.Tag = if (this_variable_comptime) .alloc_comptime_mut @@ -4061,43 +4068,41 @@ fn fnDecl( // We include a function *type*, not a value. const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true); _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node); + astgen.restoreSourceCursor(saved_cursor); } var align_gz = type_gz.makeSubBlock(scope); defer align_gz.unstack(); if (fn_proto.ast.align_expr.unwrap()) |align_expr| { - astgen.restoreSourceCursor(saved_cursor); const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_expr); _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); } - var linksection_gz = align_gz.makeSubBlock(scope); - defer linksection_gz.unstack(); - - if (fn_proto.ast.section_expr.unwrap()) |section_expr| { - astgen.restoreSourceCursor(saved_cursor); - const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr); - _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); - } - - var addrspace_gz = linksection_gz.makeSubBlock(scope); + var addrspace_gz = align_gz.makeSubBlock(scope); defer addrspace_gz.unstack(); if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| { - astgen.restoreSourceCursor(saved_cursor); const addrspace_ty = try addrspace_gz.addStdLangValue(addrspace_expr, .address_space); const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_expr); _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); } - var value_gz = addrspace_gz.makeSubBlock(scope); + var linksection_gz = addrspace_gz.makeSubBlock(scope); + defer linksection_gz.unstack(); + + if (fn_proto.ast.section_expr.unwrap()) |section_expr| { + const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr); + _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); + } + + var value_gz = linksection_gz.makeSubBlock(scope); defer value_gz.unstack(); if (!is_extern) { // We include a function *value*, not a type. astgen.restoreSourceCursor(saved_cursor); - try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto); + try astgen.fnDeclInner(&value_gz, &value_gz.base, decl_inst, decl_node, body_node.unwrap().?, fn_proto); } // *Now* we can incorporate the full source code into the hasher. @@ -4119,8 +4124,8 @@ fn fnDecl( .type_gz = &type_gz, .align_gz = &align_gz, - .linksection_gz = &linksection_gz, .addrspace_gz = &addrspace_gz, + .linksection_gz = &linksection_gz, .value_gz = &value_gz, }); } @@ -4129,7 +4134,6 @@ fn fnDeclInner( astgen: *AstGen, decl_gz: *GenZir, scope: *Scope, - saved_cursor: SourceCursor, decl_inst: Zir.Inst.Index, decl_node: Ast.Node.Index, body_node: Ast.Node.Index, @@ -4266,6 +4270,31 @@ fn fnDeclInner( // instructions inside the expression blocks for cc and ret_ty to use the function // instruction as the body to break from. + var cc_gz = decl_gz.makeSubBlock(scope); + defer cc_gz.unstack(); + const cc_ref: Zir.Inst.Ref = blk: { + if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| { + const inst = try expr( + &cc_gz, + scope, + .{ .rl = .{ .coerced_ty = try cc_gz.addStdLangValue(callconv_expr, .calling_convention) } }, + callconv_expr, + ); + if (cc_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :blk inst; + } + _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); + break :blk inst; + } else if (has_inline_keyword) { + const inst = try cc_gz.addStdLangValue(decl_node, .calling_convention_inline); + _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); + break :blk inst; + } else { + break :blk .none; + } + }; + var ret_gz = decl_gz.makeSubBlock(params_scope); defer ret_gz.unstack(); any_param_used = false; // we will check this later @@ -4284,34 +4313,6 @@ fn fnDeclInner( const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items); const ret_ty_is_generic = any_param_used; - // We're jumping back in source, so restore the cursor. - astgen.restoreSourceCursor(saved_cursor); - - var cc_gz = decl_gz.makeSubBlock(scope); - defer cc_gz.unstack(); - const cc_ref: Zir.Inst.Ref = blk: { - if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| { - const inst = try expr( - &cc_gz, - scope, - .{ .rl = .{ .coerced_ty = try cc_gz.addStdLangValue(callconv_expr, .calling_convention) } }, - callconv_expr, - ); - if (cc_gz.instructionsSlice().len == 0) { - // In this case we will send a len=0 body which can be encoded more efficiently. - break :blk inst; - } - _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); - break :blk inst; - } else if (has_inline_keyword) { - const inst = try cc_gz.addStdLangValue(decl_node, .calling_convention_inline); - _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); - break :blk inst; - } else { - break :blk .none; - } - }; - var body_gz: GenZir = .{ .is_comptime = false, .src_baseline = fn_proto.ast.proto_node, @@ -4490,14 +4491,6 @@ fn globalVarDecl( _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node); } - var linksection_gz = type_gz.makeSubBlock(scope); - defer linksection_gz.unstack(); - - if (var_decl.ast.section_node.unwrap()) |section_node| { - const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node); - _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); - } - var addrspace_gz = type_gz.makeSubBlock(scope); defer addrspace_gz.unstack(); @@ -4507,6 +4500,14 @@ fn globalVarDecl( _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); } + var linksection_gz = type_gz.makeSubBlock(scope); + defer linksection_gz.unstack(); + + if (var_decl.ast.section_node.unwrap()) |section_node| { + const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node); + _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); + } + var init_gz = type_gz.makeSubBlock(scope); defer init_gz.unstack(); @@ -4536,8 +4537,8 @@ fn globalVarDecl( .type_gz = &type_gz, .align_gz = &align_gz, - .linksection_gz = &linksection_gz, .addrspace_gz = &addrspace_gz, + .linksection_gz = &linksection_gz, .value_gz = &init_gz, }); } @@ -4599,8 +4600,8 @@ fn comptimeDecl( .linkage = .normal, .type_gz = &dummy_gz, .align_gz = &dummy_gz, - .linksection_gz = &dummy_gz, .addrspace_gz = &dummy_gz, + .linksection_gz = &dummy_gz, .value_gz = &comptime_gz, }); } @@ -11527,8 +11528,8 @@ const GenZir = struct { /// Must be called with the following stack set up: /// * gz (bottom) - /// * ret_gz /// * cc_gz + /// * ret_gz /// * body_gz (top) /// Unstacks all of those except for `gz`. fn addFunc( @@ -11575,16 +11576,16 @@ const GenZir = struct { stacked_gz = body_gz; break :body body; } else &.{}; - const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: { - const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz); - stacked_gz = cc_gz; - break :body cc_body; - } else &.{}; const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: { const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz); stacked_gz = ret_gz; break :body ret_body; } else &.{}; + const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: { + const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz); + stacked_gz = cc_gz; + break :body cc_body; + } else &.{}; break :bodies .{ body, cc_body, ret_body }; }; @@ -11725,8 +11726,8 @@ const GenZir = struct { // Order is important when unstacking. if (args.body_gz) |body_gz| body_gz.unstack(); - if (args.cc_gz) |cc_gz| cc_gz.unstack(); if (args.ret_gz) |ret_gz| ret_gz.unstack(); + if (args.cc_gz) |cc_gz| cc_gz.unstack(); astgen.instructions.appendAssumeCapacity(.{ .tag = tag, @@ -13502,10 +13503,10 @@ fn setDeclaration( /// Must be stacked on `type_gz`. align_gz: *GenZir, /// Must be stacked on `align_gz`. - linksection_gz: *GenZir, - /// Must be stacked on `linksection_gz`. addrspace_gz: *GenZir, - /// Must be stacked on `addrspace_gz` and have nothing stacked on top of it. + /// Must be stacked on `addrspace_gz`. + linksection_gz: *GenZir, + /// Must be stacked on `linksection_gz` and have nothing stacked on top of it. value_gz: *GenZir, }, ) !void { @@ -13513,9 +13514,9 @@ fn setDeclaration( const gpa = astgen.gpa; const type_body = args.type_gz.instructionsSliceUpto(args.align_gz); - const align_body = args.align_gz.instructionsSliceUpto(args.linksection_gz); - const linksection_body = args.linksection_gz.instructionsSliceUpto(args.addrspace_gz); - const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.value_gz); + const align_body = args.align_gz.instructionsSliceUpto(args.addrspace_gz); + const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.linksection_gz); + const linksection_body = args.linksection_gz.instructionsSliceUpto(args.value_gz); const value_body = args.value_gz.instructionsSlice(); const has_name = args.name != .empty; diff --git a/test/cases/decl_qualifier_cursor.zig b/test/cases/decl_qualifier_cursor.zig new file mode 100644 index 0000000000000000000000000000000000000000..c4add0b6f11aad4253f89d49253bb9187542d3ca --- /dev/null +++ b/test/cases/decl_qualifier_cursor.zig @@ -0,0 +1,45 @@ +const lang = @import("std").lang; + +fn al() comptime_int { + return 16; +} + +fn as() lang.AddressSpace { + return .generic; +} + +fn ls() []const u8 { + return "text"; +} + +fn cc() lang.CallingConvention { + return .c; +} + +fn rv() type { + return void; +} + +extern var a: u64 align(al()) addrspace(as()) linksection(ls()); +extern fn b() align(al()) addrspace(as()) linksection(ls()) callconv(cc()) rv(); +// Intentionally not `export` so it is never analyzed since the qualifiers are +// not valid for all targets. The main point of this test is checking that Zir +// generation is done correctly for these, so the above is not an issue. +fn c() align(al()) addrspace(as()) linksection(ls()) callconv(cc()) rv() { + const v: struct { + x: u32, + y: u32, + } align(al()) = undefined; + const ta: u64, const tb: struct { + x: u32, + y: u32, + } align(al()) = .{ 0, .{ .x = 10, .y = 20 } }; + _ = v; + _ = a; + _ = b; + _ = ta; + _ = tb; +} + +// compile +// output_mode=Obj -- 2.54.0 From 9195b36a6c55c2f2084d8a2edfd9a98c2e077e8c Mon Sep 17 00:00:00 2001 From: Kendall Condon Date: Tue, 19 May 2026 19:18:43 -0400 Subject: [PATCH 2/3] AstGen: move error capture asserts outside loop --- lib/std/zig/AstGen.zig | 8 ++-- .../error_set_cannot_capture_by_reference.zig | 13 ------- .../switch_capture_error_by_ref.zig | 25 ------------- .../switch_on_error_bad_captures_astgen.zig | 37 +++++++++++++++++++ .../switch_on_error_bad_captures_sema.zig | 21 +++++++++++ ...tch_on_error_with_capture_by_reference.zig | 22 ----------- 6 files changed, 62 insertions(+), 64 deletions(-) delete mode 100644 test/cases/compile_errors/error_set_cannot_capture_by_reference.zig delete mode 100644 test/cases/compile_errors/switch_capture_error_by_ref.zig create mode 100644 test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig create mode 100644 test/cases/compile_errors/switch_on_error_bad_captures_sema.zig delete mode 100644 test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 6a6bd0ed217906f84cebe87c1d063fbf4f2d2db8..d0e5a6b8a2325f48e00cb2ecc7da74cd0df5b401 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -7735,10 +7735,6 @@ fn switchExpr( }; if (capture != .none) assert(any_has_payload_capture); - if (is_err_switch) { - assert(!any_payload_is_ref); // should have failed by now - assert(!any_has_tag_capture); // should have failed by now - } prong_body: { scratch_scope.instructions_top = parent_gz.instructions.items.len; @@ -7845,6 +7841,10 @@ fn switchExpr( } } } + if (is_err_switch) { + assert(!any_payload_is_ref); // should have failed by now + assert(!any_has_tag_capture); // should have failed by now + } assert(scalar_case_index + multi_case_index + @intFromBool(has_else) == case_nodes.len); assert(multi_items_infos_start + multi_item_offset == bodies_start); diff --git a/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig b/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig deleted file mode 100644 index d13f85d066983081dd6c2b02f48f1164c2162bde..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig +++ /dev/null @@ -1,13 +0,0 @@ -export fn entry() void { - const err: error{Foo} = error.Foo; - - switch (err) { - error.Foo => |*foo| { - foo catch {}; - }, - } -} - -// error -// -// :5:23: error: error set cannot be captured by reference diff --git a/test/cases/compile_errors/switch_capture_error_by_ref.zig b/test/cases/compile_errors/switch_capture_error_by_ref.zig deleted file mode 100644 index e432159d2c7fbf9f919c0539c6ee75fdd7f08cb5..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/switch_capture_error_by_ref.zig +++ /dev/null @@ -1,25 +0,0 @@ -export fn entry1() void { - switch (@as(anyerror, error.MyError)) { - error.MyError, error.MyOtherError => |*err| _ = err, - else => {}, - } -} - -export fn entry2() void { - switch (@as(anyerror, error.MyError)) { - inline error.MyError, error.MyOtherError => |*err| _ = err, - else => {}, - } -} - -export fn entry3() void { - switch (@as(anyerror, error.MyError)) { - else => |*err| _ = err, - } -} - -// error -// -// :3:47: error: error set cannot be captured by reference -// :10:54: error: error set cannot be captured by reference -// :17:18: error: error set cannot be captured by reference diff --git a/test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig b/test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig new file mode 100644 index 0000000000000000000000000000000000000000..97abfcd55fec648f62ccce83c932a461c78c97eb --- /dev/null +++ b/test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig @@ -0,0 +1,37 @@ +const Error = error {OutOfMemory, ReadFailed}; +const result: Error!u64 = error.OutOfMemory; + +test { + _ = result catch |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |*val| 1, + }; +} + +test { + _ = result catch |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |_, tag| 1, + }; +} + +test { + _ = if (result) |n| n else |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |*val| 1, + }; +} + +test { + _ = if (result) |n| n else |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |_, tag| 1, + }; +} + +// error +// +// :7:32: error: error set cannot be captured by reference +// :14:34: error: cannot capture tag of error union +// :21:32: error: error set cannot be captured by reference +// :28:34: error: cannot capture tag of error union diff --git a/test/cases/compile_errors/switch_on_error_bad_captures_sema.zig b/test/cases/compile_errors/switch_on_error_bad_captures_sema.zig new file mode 100644 index 0000000000000000000000000000000000000000..6a5ff43818a2905d4ed50ac1b0e2eefdc4c8248d --- /dev/null +++ b/test/cases/compile_errors/switch_on_error_bad_captures_sema.zig @@ -0,0 +1,21 @@ +const Error = error{ OutOfMemory, ReadFailed }; +const err: Error = error.OutOfMemory; + +export fn a() void { + _ = switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |*val| @intFromError(val.*), + }; +} + +export fn b() void { + _ = switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |_, tag| @intFromEnum(tag), + }; +} + +// error +// +// :7:31: error: error set cannot be captured by reference +// :14:34: error: cannot capture tag of non-union type 'error{OutOfMemory,ReadFailed}' diff --git a/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig b/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig deleted file mode 100644 index d3206d02fe12f024513fcd5d19261251ae32c4f2..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig +++ /dev/null @@ -1,22 +0,0 @@ -comptime { - const e: error{Foo}!u32 = error.Foo; - e catch |err| switch (err) { - error.Foo => |*foo| { - foo catch {}; - }, - }; -} - -comptime { - const e: error{Foo}!u32 = error.Foo; - if (e) {} else |err| switch (err) { - error.Foo => |*foo| { - foo catch {}; - }, - } -} - -// error -// -// :4:24: error: error set cannot be captured by reference -// :13:24: error: error set cannot be captured by reference -- 2.54.0 From 7287181ff52660615a4f05e4b45f9ade0fed267d Mon Sep 17 00:00:00 2001 From: Kendall Condon Date: Tue, 26 May 2026 17:11:33 -0400 Subject: [PATCH 3/3] AstGen: add missing cursor restores --- lib/std/zig/AstGen.zig | 8 ++++++++ test/cases/array_sentinel_cursor.zig | 14 ++++++++++++++ test/cases/tuple_assign_cursor.zig | 13 +++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 test/cases/array_sentinel_cursor.zig create mode 100644 test/cases/tuple_assign_cursor.zig diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index d0e5a6b8a2325f48e00cb2ecc7da74cd0df5b401..bb3cda94038be396c53bde1984d8449a7544ee38 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -1460,6 +1460,7 @@ fn arrayInitExpr( mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_")) { const len_inst = try gz.addInt(array_init.ast.elements.len); + const saved_cursor = astgen.saveSourceCursor(); const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); if (array_type.ast.sentinel == .none) { const array_type_inst = try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{ @@ -1468,6 +1469,7 @@ fn arrayInitExpr( }); break :inst .{ array_type_inst, elem_type }; } else { + astgen.restoreSourceCursor(saved_cursor); const sentinel_node = array_type.ast.sentinel.unwrap().?; const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel); const array_type_inst = try gz.addPlNode( @@ -1729,6 +1731,7 @@ fn structInitExpr( mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_"); if (struct_init.ast.fields.len == 0) { if (is_inferred_array_len) { + const saved_cursor = astgen.saveSourceCursor(); const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); const array_type_inst = if (array_type.ast.sentinel == .none) blk: { break :blk try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{ @@ -1736,6 +1739,7 @@ fn structInitExpr( .rhs = elem_type, }); } else blk: { + astgen.restoreSourceCursor(saved_cursor); const sentinel_node = array_type.ast.sentinel.unwrap().?; const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel); break :blk try gz.addPlNode( @@ -3436,6 +3440,7 @@ fn assignDestructureMaybeDecls( const is_comptime = full.comptime_token != null or gz.is_comptime; const value_is_comptime = tree.nodeTag(full.ast.value_expr) == .@"comptime"; + const saved_cursor = astgen.saveSourceCursor(); // When declaring consts via a destructure, we always use a result pointer. // This avoids the need to create tuple types, and is also likely easier to // optimize, since it's a bit tricky for the optimizer to "split up" the @@ -3563,6 +3568,7 @@ fn assignDestructureMaybeDecls( if (any_lvalue_expr) { // At least one variable was an lvalue expr. Iterate again in order to // evaluate the lvalues from within the possible block_comptime. + astgen.restoreSourceCursor(saved_cursor); for (rl_components, full.ast.variables) |*variable_rl, variable_node| { if (variable_rl.* != .typed_ptr) continue; switch (tree.nodeTag(variable_node)) { @@ -3896,7 +3902,9 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node. return astgen.failNode(len_node, "unable to infer array size", .{}); } const len = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node, .array_length); + const saved_cursor = astgen.saveSourceCursor(); const elem_type = try typeExpr(gz, scope, extra.elem_type); + astgen.restoreSourceCursor(saved_cursor); const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, .array_sentinel); const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{ diff --git a/test/cases/array_sentinel_cursor.zig b/test/cases/array_sentinel_cursor.zig new file mode 100644 index 0000000000000000000000000000000000000000..7b20dd48a1e8029c740645a06a079016c842080b --- /dev/null +++ b/test/cases/array_sentinel_cursor.zig @@ -0,0 +1,14 @@ +fn s() usize { + return 64; +} + +fn T() type { + return u64; +} + +const Array = [64:s()]T(); +const empty = [_:s()]T(){}; +const items = [_:s()]T(){100}; + +// compile +// output_mode=Obj diff --git a/test/cases/tuple_assign_cursor.zig b/test/cases/tuple_assign_cursor.zig new file mode 100644 index 0000000000000000000000000000000000000000..a0318f263596dccceef036215f1c8653cc9057d5 --- /dev/null +++ b/test/cases/tuple_assign_cursor.zig @@ -0,0 +1,13 @@ +fn T() type { + return u64; +} + +test { + struct { + var a: u64 = 0; + }.a, const b: T() = .{ 1, 2 }; + _ = b; +} + +// compile +// output_mode=Obj -- 2.54.0