authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-05-18 18:59:50-04:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-09 10:09:42+02:00
logb7b6d66675dc1eb20862b6a18ac9745287bc7e32
treea42d3e4d362ee056b81f8db8fc3d8e70e497c514
parent18d9b2850ecba8fca14b11bd18111c17f0415aa9

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.

2 files changed, 114 insertions(+), 68 deletions(-)

lib/std/zig/AstGen.zig+69-68
......@@ -3135,6 +3135,11 @@ fn varDecl(
31353135 return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw});
31363136 }
31373137
3138 const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node.unwrap()) |type_node|
3139 try typeExpr(gz, scope, type_node)
3140 else
3141 .none;
3142
31383143 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node.unwrap()) |align_node|
31393144 try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align")
31403145 else
......@@ -3155,8 +3160,8 @@ fn varDecl(
31553160 if (align_inst == .none and
31563161 !astgen.nodes_need_rl.contains(node))
31573162 {
3158 const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| .{
3159 .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
3163 const result_info: ResultInfo = if (type_inst != .none) .{
3164 .rl = .{ .ty = type_inst },
31603165 .ctx = .const_init,
31613166 } else .{ .rl = .none, .ctx = .const_init };
31623167 const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, result_info, init_node, .dbg_var) orelse
......@@ -3185,8 +3190,7 @@ fn varDecl(
31853190 const is_comptime = gz.is_comptime or
31863191 tree.nodeTag(init_node) == .@"comptime";
31873192
3188 const init_rl: ResultInfo.Loc = if (var_decl.ast.type_node.unwrap()) |type_node| init_rl: {
3189 const type_inst = try typeExpr(gz, scope, type_node);
3193 const init_rl: ResultInfo.Loc = if (type_inst != .none) init_rl: {
31903194 if (align_inst == .none) {
31913195 break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
31923196 } else {
......@@ -3254,8 +3258,7 @@ fn varDecl(
32543258 if (var_decl.comptime_token != null and gz.is_comptime)
32553259 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
32563260 const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
3257 const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| a: {
3258 const type_inst = try typeExpr(gz, scope, type_node);
3261 const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (type_inst != .none) a: {
32593262 const alloc = alloc: {
32603263 if (align_inst == .none) {
32613264 const tag: Zir.Inst.Tag = if (is_comptime)
......@@ -3482,14 +3485,18 @@ fn assignDestructureMaybeDecls(
34823485 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
34833486 const this_variable_comptime = is_comptime or (is_const and value_is_comptime);
34843487
3488 const type_inst = if (full_var_decl.ast.type_node.unwrap()) |type_node|
3489 try typeExpr(gz, scope, type_node)
3490 else
3491 .none;
3492
34853493 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node.unwrap()) |align_node|
34863494 try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align")
34873495 else
34883496 .none;
34893497
3490 if (full_var_decl.ast.type_node.unwrap()) |type_node| {
3498 if (type_inst != .none) {
34913499 // Typed alloc
3492 const type_inst = try typeExpr(gz, scope, type_node);
34933500 const ptr = if (align_inst == .none) ptr: {
34943501 const tag: Zir.Inst.Tag = if (this_variable_comptime)
34953502 .alloc_comptime_mut
......@@ -4061,43 +4068,41 @@ fn fnDecl(
40614068 // We include a function *type*, not a value.
40624069 const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true);
40634070 _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node);
4071 astgen.restoreSourceCursor(saved_cursor);
40644072 }
40654073
40664074 var align_gz = type_gz.makeSubBlock(scope);
40674075 defer align_gz.unstack();
40684076
40694077 if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
4070 astgen.restoreSourceCursor(saved_cursor);
40714078 const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_expr);
40724079 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
40734080 }
40744081
4075 var linksection_gz = align_gz.makeSubBlock(scope);
4076 defer linksection_gz.unstack();
4077
4078 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
4079 astgen.restoreSourceCursor(saved_cursor);
4080 const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr);
4081 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
4082 }
4083
4084 var addrspace_gz = linksection_gz.makeSubBlock(scope);
4082 var addrspace_gz = align_gz.makeSubBlock(scope);
40854083 defer addrspace_gz.unstack();
40864084
40874085 if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
4088 astgen.restoreSourceCursor(saved_cursor);
40894086 const addrspace_ty = try addrspace_gz.addStdLangValue(addrspace_expr, .address_space);
40904087 const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_expr);
40914088 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
40924089 }
40934090
4094 var value_gz = addrspace_gz.makeSubBlock(scope);
4091 var linksection_gz = addrspace_gz.makeSubBlock(scope);
4092 defer linksection_gz.unstack();
4093
4094 if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
4095 const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr);
4096 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
4097 }
4098
4099 var value_gz = linksection_gz.makeSubBlock(scope);
40954100 defer value_gz.unstack();
40964101
40974102 if (!is_extern) {
40984103 // We include a function *value*, not a type.
40994104 astgen.restoreSourceCursor(saved_cursor);
4100 try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto);
4105 try astgen.fnDeclInner(&value_gz, &value_gz.base, decl_inst, decl_node, body_node.unwrap().?, fn_proto);
41014106 }
41024107
41034108 // *Now* we can incorporate the full source code into the hasher.
......@@ -4119,8 +4124,8 @@ fn fnDecl(
41194124
41204125 .type_gz = &type_gz,
41214126 .align_gz = &align_gz,
4122 .linksection_gz = &linksection_gz,
41234127 .addrspace_gz = &addrspace_gz,
4128 .linksection_gz = &linksection_gz,
41244129 .value_gz = &value_gz,
41254130 });
41264131}
......@@ -4129,7 +4134,6 @@ fn fnDeclInner(
41294134 astgen: *AstGen,
41304135 decl_gz: *GenZir,
41314136 scope: *Scope,
4132 saved_cursor: SourceCursor,
41334137 decl_inst: Zir.Inst.Index,
41344138 decl_node: Ast.Node.Index,
41354139 body_node: Ast.Node.Index,
......@@ -4266,27 +4270,6 @@ fn fnDeclInner(
42664270 // instructions inside the expression blocks for cc and ret_ty to use the function
42674271 // instruction as the body to break from.
42684272
4269 var ret_gz = decl_gz.makeSubBlock(params_scope);
4270 defer ret_gz.unstack();
4271 any_param_used = false; // we will check this later
4272 const ret_ref: Zir.Inst.Ref = inst: {
4273 // Parameters are in scope for the return type, so we use `params_scope` here.
4274 // The calling convention will not have parameters in scope, so we'll just use `scope`.
4275 // See #22263 for a proposal to solve the inconsistency here.
4276 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type.unwrap().?, .normal);
4277 if (ret_gz.instructionsSlice().len == 0) {
4278 // In this case we will send a len=0 body which can be encoded more efficiently.
4279 break :inst inst;
4280 }
4281 _ = try ret_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst);
4282 break :inst inst;
4283 };
4284 const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items);
4285 const ret_ty_is_generic = any_param_used;
4286
4287 // We're jumping back in source, so restore the cursor.
4288 astgen.restoreSourceCursor(saved_cursor);
4289
42904273 var cc_gz = decl_gz.makeSubBlock(scope);
42914274 defer cc_gz.unstack();
42924275 const cc_ref: Zir.Inst.Ref = blk: {
......@@ -4312,6 +4295,24 @@ fn fnDeclInner(
43124295 }
43134296 };
43144297
4298 var ret_gz = decl_gz.makeSubBlock(params_scope);
4299 defer ret_gz.unstack();
4300 any_param_used = false; // we will check this later
4301 const ret_ref: Zir.Inst.Ref = inst: {
4302 // Parameters are in scope for the return type, so we use `params_scope` here.
4303 // The calling convention will not have parameters in scope, so we'll just use `scope`.
4304 // See #22263 for a proposal to solve the inconsistency here.
4305 const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type.unwrap().?, .normal);
4306 if (ret_gz.instructionsSlice().len == 0) {
4307 // In this case we will send a len=0 body which can be encoded more efficiently.
4308 break :inst inst;
4309 }
4310 _ = try ret_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst);
4311 break :inst inst;
4312 };
4313 const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items);
4314 const ret_ty_is_generic = any_param_used;
4315
43154316 var body_gz: GenZir = .{
43164317 .is_comptime = false,
43174318 .src_baseline = fn_proto.ast.proto_node,
......@@ -4490,14 +4491,6 @@ fn globalVarDecl(
44904491 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
44914492 }
44924493
4493 var linksection_gz = type_gz.makeSubBlock(scope);
4494 defer linksection_gz.unstack();
4495
4496 if (var_decl.ast.section_node.unwrap()) |section_node| {
4497 const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node);
4498 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
4499 }
4500
45014494 var addrspace_gz = type_gz.makeSubBlock(scope);
45024495 defer addrspace_gz.unstack();
45034496
......@@ -4507,6 +4500,14 @@ fn globalVarDecl(
45074500 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
45084501 }
45094502
4503 var linksection_gz = type_gz.makeSubBlock(scope);
4504 defer linksection_gz.unstack();
4505
4506 if (var_decl.ast.section_node.unwrap()) |section_node| {
4507 const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node);
4508 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
4509 }
4510
45104511 var init_gz = type_gz.makeSubBlock(scope);
45114512 defer init_gz.unstack();
45124513
......@@ -4536,8 +4537,8 @@ fn globalVarDecl(
45364537
45374538 .type_gz = &type_gz,
45384539 .align_gz = &align_gz,
4539 .linksection_gz = &linksection_gz,
45404540 .addrspace_gz = &addrspace_gz,
4541 .linksection_gz = &linksection_gz,
45414542 .value_gz = &init_gz,
45424543 });
45434544}
......@@ -4599,8 +4600,8 @@ fn comptimeDecl(
45994600 .linkage = .normal,
46004601 .type_gz = &dummy_gz,
46014602 .align_gz = &dummy_gz,
4602 .linksection_gz = &dummy_gz,
46034603 .addrspace_gz = &dummy_gz,
4604 .linksection_gz = &dummy_gz,
46044605 .value_gz = &comptime_gz,
46054606 });
46064607}
......@@ -11527,8 +11528,8 @@ const GenZir = struct {
1152711528
1152811529 /// Must be called with the following stack set up:
1152911530 /// * gz (bottom)
11530 /// * ret_gz
1153111531 /// * cc_gz
11532 /// * ret_gz
1153211533 /// * body_gz (top)
1153311534 /// Unstacks all of those except for `gz`.
1153411535 fn addFunc(
......@@ -11575,16 +11576,16 @@ const GenZir = struct {
1157511576 stacked_gz = body_gz;
1157611577 break :body body;
1157711578 } else &.{};
11578 const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
11579 const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
11580 stacked_gz = cc_gz;
11581 break :body cc_body;
11582 } else &.{};
1158311579 const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: {
1158411580 const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz);
1158511581 stacked_gz = ret_gz;
1158611582 break :body ret_body;
1158711583 } else &.{};
11584 const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
11585 const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
11586 stacked_gz = cc_gz;
11587 break :body cc_body;
11588 } else &.{};
1158811589 break :bodies .{ body, cc_body, ret_body };
1158911590 };
1159011591
......@@ -11725,8 +11726,8 @@ const GenZir = struct {
1172511726
1172611727 // Order is important when unstacking.
1172711728 if (args.body_gz) |body_gz| body_gz.unstack();
11728 if (args.cc_gz) |cc_gz| cc_gz.unstack();
1172911729 if (args.ret_gz) |ret_gz| ret_gz.unstack();
11730 if (args.cc_gz) |cc_gz| cc_gz.unstack();
1173011731
1173111732 astgen.instructions.appendAssumeCapacity(.{
1173211733 .tag = tag,
......@@ -13502,10 +13503,10 @@ fn setDeclaration(
1350213503 /// Must be stacked on `type_gz`.
1350313504 align_gz: *GenZir,
1350413505 /// Must be stacked on `align_gz`.
13505 linksection_gz: *GenZir,
13506 /// Must be stacked on `linksection_gz`.
1350713506 addrspace_gz: *GenZir,
13508 /// Must be stacked on `addrspace_gz` and have nothing stacked on top of it.
13507 /// Must be stacked on `addrspace_gz`.
13508 linksection_gz: *GenZir,
13509 /// Must be stacked on `linksection_gz` and have nothing stacked on top of it.
1350913510 value_gz: *GenZir,
1351013511 },
1351113512) !void {
......@@ -13513,9 +13514,9 @@ fn setDeclaration(
1351313514 const gpa = astgen.gpa;
1351413515
1351513516 const type_body = args.type_gz.instructionsSliceUpto(args.align_gz);
13516 const align_body = args.align_gz.instructionsSliceUpto(args.linksection_gz);
13517 const linksection_body = args.linksection_gz.instructionsSliceUpto(args.addrspace_gz);
13518 const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.value_gz);
13517 const align_body = args.align_gz.instructionsSliceUpto(args.addrspace_gz);
13518 const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.linksection_gz);
13519 const linksection_body = args.linksection_gz.instructionsSliceUpto(args.value_gz);
1351913520 const value_body = args.value_gz.instructionsSlice();
1352013521
1352113522 const has_name = args.name != .empty;
test/cases/decl_qualifier_cursor.zig created+45
......@@ -0,0 +1,45 @@
1const lang = @import("std").lang;
2
3fn al() comptime_int {
4 return 16;
5}
6
7fn as() lang.AddressSpace {
8 return .generic;
9}
10
11fn ls() []const u8 {
12 return "text";
13}
14
15fn cc() lang.CallingConvention {
16 return .c;
17}
18
19fn rv() type {
20 return void;
21}
22
23extern var a: u64 align(al()) addrspace(as()) linksection(ls());
24extern fn b() align(al()) addrspace(as()) linksection(ls()) callconv(cc()) rv();
25// Intentionally not `export` so it is never analyzed since the qualifiers are
26// not valid for all targets. The main point of this test is checking that Zir
27// generation is done correctly for these, so the above is not an issue.
28fn c() align(al()) addrspace(as()) linksection(ls()) callconv(cc()) rv() {
29 const v: struct {
30 x: u32,
31 y: u32,
32 } align(al()) = undefined;
33 const ta: u64, const tb: struct {
34 x: u32,
35 y: u32,
36 } align(al()) = .{ 0, .{ .x = 10, .y = 20 } };
37 _ = v;
38 _ = a;
39 _ = b;
40 _ = ta;
41 _ = tb;
42}
43
44// compile
45// output_mode=Obj