From b7b6d66675dc1eb20862b6a18ac9745287bc7e32 Mon Sep 17 00:00:00 2001 From: Kendall Condon Date: Mon, 18 May 2026 18:59:50 -0400 Subject: [PATCH] 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