authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-20 21:48:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-20 21:48:18-07:00
loga1ac2b95bb9d6b98c6ae862bda31c84721b92bf3
treecd9950e6537f8538df8126f6c826309d1d99a5db
parent971f3d95f907fe438b0531df2f0c9f2a5471271d

AstGen: implement union decls


3 files changed, 409 insertions(+), 22 deletions(-)

src/AstGen.zig+260-7
......@@ -1703,6 +1703,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
17031703 .struct_decl_packed,
17041704 .struct_decl_extern,
17051705 .union_decl,
1706 .union_decl_packed,
1707 .union_decl_extern,
17061708 .enum_decl,
17071709 .enum_decl_nonexhaustive,
17081710 .opaque_decl,
......@@ -2897,7 +2899,7 @@ fn structDeclInner(
28972899 if (member.comptime_token) |comptime_token| {
28982900 return astgen.failTok(comptime_token, "TODO implement comptime struct fields", .{});
28992901 }
2900 try fields_data.ensureCapacity(gpa, fields_data.items.len + 4);
2902 try fields_data.ensureUnusedCapacity(gpa, 4);
29012903
29022904 const field_name = try gz.identAsString(member.ast.name_token);
29032905 fields_data.appendAssumeCapacity(field_name);
......@@ -2969,6 +2971,229 @@ fn structDeclInner(
29692971 return gz.indexToRef(decl_inst);
29702972}
29712973
2974fn unionDeclInner(
2975 gz: *GenZir,
2976 scope: *Scope,
2977 node: ast.Node.Index,
2978 members: []const ast.Node.Index,
2979 tag: Zir.Inst.Tag,
2980 arg_inst: Zir.Inst.Ref,
2981 have_auto_enum: bool,
2982) InnerError!Zir.Inst.Ref {
2983 const astgen = gz.astgen;
2984 const gpa = astgen.gpa;
2985 const tree = &astgen.file.tree;
2986 const node_tags = tree.nodes.items(.tag);
2987 const node_datas = tree.nodes.items(.data);
2988
2989 // The union_decl instruction introduces a scope in which the decls of the union
2990 // are in scope, so that field types, alignments, and default value expressions
2991 // can refer to decls within the union itself.
2992 var block_scope: GenZir = .{
2993 .parent = scope,
2994 .decl_node_index = node,
2995 .astgen = astgen,
2996 .force_comptime = true,
2997 .ref_start_index = gz.ref_start_index,
2998 };
2999 defer block_scope.instructions.deinit(gpa);
3000
3001 var wip_decls: WipDecls = .{};
3002 defer wip_decls.deinit(gpa);
3003
3004 // We don't know which members are fields until we iterate, so cannot do
3005 // an accurate ensureCapacity yet.
3006 var fields_data = ArrayListUnmanaged(u32){};
3007 defer fields_data.deinit(gpa);
3008
3009 const bits_per_field = 4;
3010 const fields_per_u32 = 32 / bits_per_field;
3011 // We only need this if there are greater than fields_per_u32 fields.
3012 var bit_bag = ArrayListUnmanaged(u32){};
3013 defer bit_bag.deinit(gpa);
3014
3015 var cur_bit_bag: u32 = 0;
3016 var field_index: usize = 0;
3017 for (members) |member_node| {
3018 const member = switch (node_tags[member_node]) {
3019 .container_field_init => tree.containerFieldInit(member_node),
3020 .container_field_align => tree.containerFieldAlign(member_node),
3021 .container_field => tree.containerField(member_node),
3022
3023 .fn_decl => {
3024 const fn_proto = node_datas[member_node].lhs;
3025 const body = node_datas[member_node].rhs;
3026 switch (node_tags[fn_proto]) {
3027 .fn_proto_simple => {
3028 var params: [1]ast.Node.Index = undefined;
3029 try astgen.fnDecl(gz, &wip_decls, body, tree.fnProtoSimple(&params, fn_proto));
3030 continue;
3031 },
3032 .fn_proto_multi => {
3033 try astgen.fnDecl(gz, &wip_decls, body, tree.fnProtoMulti(fn_proto));
3034 continue;
3035 },
3036 .fn_proto_one => {
3037 var params: [1]ast.Node.Index = undefined;
3038 try astgen.fnDecl(gz, &wip_decls, body, tree.fnProtoOne(&params, fn_proto));
3039 continue;
3040 },
3041 .fn_proto => {
3042 try astgen.fnDecl(gz, &wip_decls, body, tree.fnProto(fn_proto));
3043 continue;
3044 },
3045 else => unreachable,
3046 }
3047 },
3048 .fn_proto_simple => {
3049 var params: [1]ast.Node.Index = undefined;
3050 try astgen.fnDecl(gz, &wip_decls, 0, tree.fnProtoSimple(&params, member_node));
3051 continue;
3052 },
3053 .fn_proto_multi => {
3054 try astgen.fnDecl(gz, &wip_decls, 0, tree.fnProtoMulti(member_node));
3055 continue;
3056 },
3057 .fn_proto_one => {
3058 var params: [1]ast.Node.Index = undefined;
3059 try astgen.fnDecl(gz, &wip_decls, 0, tree.fnProtoOne(&params, member_node));
3060 continue;
3061 },
3062 .fn_proto => {
3063 try astgen.fnDecl(gz, &wip_decls, 0, tree.fnProto(member_node));
3064 continue;
3065 },
3066
3067 .global_var_decl => {
3068 try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.globalVarDecl(member_node));
3069 continue;
3070 },
3071 .local_var_decl => {
3072 try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.localVarDecl(member_node));
3073 continue;
3074 },
3075 .simple_var_decl => {
3076 try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.simpleVarDecl(member_node));
3077 continue;
3078 },
3079 .aligned_var_decl => {
3080 try astgen.globalVarDecl(gz, scope, &wip_decls, member_node, tree.alignedVarDecl(member_node));
3081 continue;
3082 },
3083
3084 .@"comptime" => {
3085 try astgen.comptimeDecl(gz, scope, member_node);
3086 continue;
3087 },
3088 .@"usingnamespace" => {
3089 try astgen.usingnamespaceDecl(gz, scope, member_node);
3090 continue;
3091 },
3092 .test_decl => {
3093 try astgen.testDecl(gz, scope, member_node);
3094 continue;
3095 },
3096 else => unreachable,
3097 };
3098 if (field_index % fields_per_u32 == 0 and field_index != 0) {
3099 try bit_bag.append(gpa, cur_bit_bag);
3100 cur_bit_bag = 0;
3101 }
3102 if (member.comptime_token) |comptime_token| {
3103 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
3104 }
3105 try fields_data.ensureUnusedCapacity(gpa, 4);
3106
3107 const field_name = try gz.identAsString(member.ast.name_token);
3108 fields_data.appendAssumeCapacity(field_name);
3109
3110 const have_type = member.ast.type_expr != 0;
3111 const have_align = member.ast.align_expr != 0;
3112 const have_value = member.ast.value_expr != 0;
3113 cur_bit_bag = (cur_bit_bag >> bits_per_field) |
3114 (@as(u32, @boolToInt(have_type)) << 28) |
3115 (@as(u32, @boolToInt(have_align)) << 29) |
3116 (@as(u32, @boolToInt(have_value)) << 30) |
3117 (@as(u32, @boolToInt(have_auto_enum)) << 31);
3118
3119 if (have_type) {
3120 const field_type = try typeExpr(&block_scope, &block_scope.base, member.ast.type_expr);
3121 fields_data.appendAssumeCapacity(@enumToInt(field_type));
3122 }
3123 if (have_align) {
3124 const align_inst = try expr(&block_scope, &block_scope.base, .{ .ty = .u32_type }, member.ast.align_expr);
3125 fields_data.appendAssumeCapacity(@enumToInt(align_inst));
3126 }
3127 if (have_value) {
3128 if (arg_inst == .none) {
3129 return astgen.failNodeNotes(
3130 node,
3131 "explicitly valued tagged union missing integer tag type",
3132 .{},
3133 &[_]u32{
3134 try astgen.errNoteNode(
3135 member.ast.value_expr,
3136 "tag value specified here",
3137 .{},
3138 ),
3139 },
3140 );
3141 }
3142 const tag_value = try expr(&block_scope, &block_scope.base, .{ .ty = arg_inst }, member.ast.value_expr);
3143 fields_data.appendAssumeCapacity(@enumToInt(tag_value));
3144 }
3145
3146 field_index += 1;
3147 }
3148 if (field_index == 0) {
3149 return astgen.failNode(node, "union declarations must have at least one tag", .{});
3150 }
3151 {
3152 const empty_slot_count = fields_per_u32 - (field_index % fields_per_u32);
3153 if (empty_slot_count < fields_per_u32) {
3154 cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_field);
3155 }
3156 }
3157 {
3158 const empty_slot_count = 16 - (wip_decls.decl_index % 16);
3159 if (empty_slot_count < 16) {
3160 wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
3161 }
3162 }
3163
3164 const decl_inst = try gz.addBlock(tag, node);
3165 try gz.instructions.append(gpa, decl_inst);
3166 if (block_scope.instructions.items.len != 0) {
3167 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
3168 }
3169
3170 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).Struct.fields.len +
3171 bit_bag.items.len + 1 + fields_data.items.len +
3172 block_scope.instructions.items.len +
3173 wip_decls.bit_bag.items.len + @boolToInt(wip_decls.decl_index != 0) +
3174 wip_decls.name_and_value.items.len);
3175 const zir_datas = astgen.instructions.items(.data);
3176 zir_datas[decl_inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
3177 .tag_type = arg_inst,
3178 .body_len = @intCast(u32, block_scope.instructions.items.len),
3179 .fields_len = @intCast(u32, field_index),
3180 .decls_len = @intCast(u32, wip_decls.decl_index),
3181 });
3182 astgen.extra.appendSliceAssumeCapacity(block_scope.instructions.items);
3183
3184 astgen.extra.appendSliceAssumeCapacity(bit_bag.items); // Likely empty.
3185 astgen.extra.appendAssumeCapacity(cur_bit_bag);
3186 astgen.extra.appendSliceAssumeCapacity(fields_data.items);
3187
3188 astgen.extra.appendSliceAssumeCapacity(wip_decls.bit_bag.items); // Likely empty.
3189 if (wip_decls.decl_index != 0) {
3190 astgen.extra.appendAssumeCapacity(wip_decls.cur_bit_bag);
3191 }
3192 astgen.extra.appendSliceAssumeCapacity(wip_decls.name_and_value.items);
3193
3194 return gz.indexToRef(decl_inst);
3195}
3196
29723197fn containerDecl(
29733198 gz: *GenZir,
29743199 scope: *Scope,
......@@ -3005,7 +3230,18 @@ fn containerDecl(
30053230 return rvalue(gz, scope, rl, result, node);
30063231 },
30073232 .keyword_union => {
3008 return astgen.failTok(container_decl.ast.main_token, "TODO AstGen for union decl", .{});
3233 const tag = if (container_decl.layout_token) |t| switch (token_tags[t]) {
3234 .keyword_packed => Zir.Inst.Tag.union_decl_packed,
3235 .keyword_extern => Zir.Inst.Tag.union_decl_extern,
3236 else => unreachable,
3237 } else Zir.Inst.Tag.union_decl;
3238
3239 // See `Zir.Inst.UnionDecl` doc comments for why this is stored along
3240 // with fields instead of separately.
3241 const have_auto_enum = container_decl.ast.enum_token != null;
3242
3243 const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, tag, arg_inst, have_auto_enum);
3244 return rvalue(gz, scope, rl, result, node);
30093245 },
30103246 .keyword_enum => {
30113247 if (container_decl.layout_token) |t| {
......@@ -3224,6 +3460,20 @@ fn containerDecl(
32243460 (@as(u32, @boolToInt(have_value)) << 31);
32253461
32263462 if (have_value) {
3463 if (arg_inst == .none) {
3464 return astgen.failNodeNotes(
3465 node,
3466 "explicitly valued enum missing integer tag type",
3467 .{},
3468 &[_]u32{
3469 try astgen.errNoteNode(
3470 member.ast.value_expr,
3471 "tag value specified here",
3472 .{},
3473 ),
3474 },
3475 );
3476 }
32273477 const tag_value_inst = try expr(&block_scope, &block_scope.base, .{ .ty = arg_inst }, member.ast.value_expr);
32283478 fields_data.appendAssumeCapacity(@enumToInt(tag_value_inst));
32293479 }
......@@ -3232,12 +3482,15 @@ fn containerDecl(
32323482 }
32333483 {
32343484 const empty_slot_count = 32 - (field_index % 32);
3235 cur_bit_bag >>= @intCast(u5, empty_slot_count);
3485 if (empty_slot_count < 32) {
3486 cur_bit_bag >>= @intCast(u5, empty_slot_count);
3487 }
32363488 }
3237
3238 if (wip_decls.decl_index != 0) {
3489 {
32393490 const empty_slot_count = 16 - (wip_decls.decl_index % 16);
3240 wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
3491 if (empty_slot_count < 16) {
3492 wip_decls.cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
3493 }
32413494 }
32423495
32433496 const decl_inst = try gz.addBlock(tag, node);
......@@ -4789,7 +5042,7 @@ fn switchExpr(
47895042 .prong_index = capture_index,
47905043 } },
47915044 });
4792 const capture_name = try astgen.identifierTokenString(payload_token);
5045 const capture_name = try astgen.identifierTokenString(ident);
47935046 capture_val_scope = .{
47945047 .parent = &case_scope.base,
47955048 .gen_zir = &case_scope,
src/Sema.zig+9-2
......@@ -338,7 +338,9 @@ pub fn analyzeBody(
338338 .struct_decl_extern => try sema.zirStructDecl(block, inst, .Extern),
339339 .enum_decl => try sema.zirEnumDecl(block, inst, false),
340340 .enum_decl_nonexhaustive => try sema.zirEnumDecl(block, inst, true),
341 .union_decl => try sema.zirUnionDecl(block, inst),
341 .union_decl => try sema.zirUnionDecl(block, inst, .Auto),
342 .union_decl_packed => try sema.zirUnionDecl(block, inst, .Packed),
343 .union_decl_extern => try sema.zirUnionDecl(block, inst, .Extern),
342344 .opaque_decl => try sema.zirOpaqueDecl(block, inst),
343345 .error_set_decl => try sema.zirErrorSetDecl(block, inst),
344346
......@@ -980,7 +982,12 @@ fn zirEnumDecl(
980982 return sema.analyzeDeclVal(block, src, new_decl);
981983}
982984
983fn zirUnionDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
985fn zirUnionDecl(
986 sema: *Sema,
987 block: *Scope.Block,
988 inst: Zir.Inst.Index,
989 layout: std.builtin.TypeInfo.ContainerLayout,
990) InnerError!*Inst {
984991 const tracy = trace(@src());
985992 defer tracy.end();
986993
src/Zir.zig+140-13
......@@ -301,6 +301,10 @@ pub const Inst = struct {
301301 /// the field types and optional type tag expression.
302302 /// Uses the `pl_node` union field. Payload is `UnionDecl`.
303303 union_decl,
304 /// Same as `union_decl`, except has the `packed` layout.
305 union_decl_packed,
306 /// Same as `union_decl`, except has the `extern` layout.
307 union_decl_extern,
304308 /// An enum type definition. Contains references to ZIR instructions for
305309 /// the field value expressions and optional type tag expression.
306310 /// Uses the `pl_node` union field. Payload is `EnumDecl`.
......@@ -988,6 +992,8 @@ pub const Inst = struct {
988992 .struct_decl_packed,
989993 .struct_decl_extern,
990994 .union_decl,
995 .union_decl_packed,
996 .union_decl_extern,
991997 .enum_decl,
992998 .enum_decl_nonexhaustive,
993999 .opaque_decl,
......@@ -2022,19 +2028,37 @@ pub const Inst = struct {
20222028 };
20232029
20242030 /// Trailing:
2025 /// 0. has_bits: u32 // for every 10 fields (+1)
2026 /// - first bit is special: set if and only if auto enum tag is enabled.
2027 /// - sets of 3 bits:
2028 /// 0b00X: whether corresponding field has a type expression
2029 /// 0b0X0: whether corresponding field has a align expression
2030 /// 0bX00: whether corresponding field has a tag value expression
2031 /// 1. field_name: u32 // for every field: null terminated string index
2032 /// 2. opt_exprs // Ref for every field for which corresponding bit is set
2033 /// - interleaved. type if present, align if present, tag value if present.
2031 /// 0. inst: Index // for every body_len
2032 /// 1. has_bits: u32 // for every 8 fields
2033 /// - sets of 4 bits:
2034 /// 0b000X: whether corresponding field has a type expression
2035 /// 0b00X0: whether corresponding field has a align expression
2036 /// 0b0X00: whether corresponding field has a tag value expression
2037 /// 0bX000: unused(*)
2038 /// * the first unused bit (the unused bit of the first field) is used
2039 /// to indicate whether auto enum tag is enabled.
2040 /// 0 = union(tag_type)
2041 /// 1 = union(enum(tag_type))
2042 /// 2. fields: { // for every fields_len
2043 /// field_name: u32, // null terminated string index
2044 /// field_type: Ref, // if corresponding bit is set
2045 /// align: Ref, // if corresponding bit is set
2046 /// tag_value: Ref, // if corresponding bit is set
2047 /// }
2048 /// 3. decl_bits: u32 // for every 16 decls
2049 /// - sets of 2 bits:
2050 /// 0b0X: whether corresponding decl is pub
2051 /// 0bX0: whether corresponding decl is exported
2052 /// 4. decl: { // for every decls_len
2053 /// name: u32, // null terminated string index
2054 /// value: Index,
2055 /// }
20342056 pub const UnionDecl = struct {
20352057 /// Can be `Ref.none`.
20362058 tag_type: Ref,
2059 body_len: u32,
20372060 fields_len: u32,
2061 decls_len: u32,
20382062 };
20392063
20402064 /// Trailing: field_name: u32 // for every field: null terminated string index
......@@ -2339,7 +2363,6 @@ const Writer = struct {
23392363 .slice_start,
23402364 .slice_end,
23412365 .slice_sentinel,
2342 .union_decl,
23432366 .struct_init,
23442367 .struct_init_anon,
23452368 .array_init,
......@@ -2452,6 +2475,11 @@ const Writer = struct {
24522475 .struct_decl_extern,
24532476 => try self.writeStructDecl(stream, inst),
24542477
2478 .union_decl,
2479 .union_decl_packed,
2480 .union_decl_extern,
2481 => try self.writeUnionDecl(stream, inst),
2482
24552483 .enum_decl,
24562484 .enum_decl_nonexhaustive,
24572485 => try self.writeEnumDecl(stream, inst),
......@@ -2884,6 +2912,105 @@ const Writer = struct {
28842912 try self.writeSrc(stream, inst_data.src());
28852913 }
28862914
2915 fn writeUnionDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2916 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2917 const extra = self.code.extraData(Inst.UnionDecl, inst_data.payload_index);
2918 const body = self.code.extra[extra.end..][0..extra.data.body_len];
2919 const fields_len = extra.data.fields_len;
2920 const decls_len = extra.data.decls_len;
2921 const tag_type_ref = extra.data.tag_type;
2922
2923 assert(fields_len != 0);
2924 var first_has_auto_enum: ?bool = null;
2925
2926 if (tag_type_ref != .none) {
2927 try self.writeInstRef(stream, tag_type_ref);
2928 try stream.writeAll(", ");
2929 }
2930
2931 var extra_index: usize = undefined;
2932
2933 try stream.writeAll("{\n");
2934 self.indent += 2;
2935 try self.writeBody(stream, body);
2936
2937 try stream.writeByteNTimes(' ', self.indent - 2);
2938 try stream.writeAll("}, {\n");
2939
2940 const bits_per_field = 4;
2941 const fields_per_u32 = 32 / bits_per_field;
2942 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
2943 const body_end = extra.end + body.len;
2944 extra_index = body_end + bit_bags_count;
2945 var bit_bag_index: usize = body_end;
2946 var cur_bit_bag: u32 = undefined;
2947 var field_i: u32 = 0;
2948 while (field_i < fields_len) : (field_i += 1) {
2949 if (field_i % fields_per_u32 == 0) {
2950 cur_bit_bag = self.code.extra[bit_bag_index];
2951 bit_bag_index += 1;
2952 }
2953 const has_type = @truncate(u1, cur_bit_bag) != 0;
2954 cur_bit_bag >>= 1;
2955 const has_align = @truncate(u1, cur_bit_bag) != 0;
2956 cur_bit_bag >>= 1;
2957 const has_value = @truncate(u1, cur_bit_bag) != 0;
2958 cur_bit_bag >>= 1;
2959 const has_auto_enum = @truncate(u1, cur_bit_bag) != 0;
2960 cur_bit_bag >>= 1;
2961
2962 if (first_has_auto_enum == null) {
2963 first_has_auto_enum = has_auto_enum;
2964 }
2965
2966 const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
2967 extra_index += 1;
2968 try stream.writeByteNTimes(' ', self.indent);
2969 try stream.print("{}", .{std.zig.fmtId(field_name)});
2970
2971 if (has_type) {
2972 const field_type = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2973 extra_index += 1;
2974
2975 try stream.writeAll(": ");
2976 try self.writeInstRef(stream, field_type);
2977 }
2978 if (has_align) {
2979 const align_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2980 extra_index += 1;
2981
2982 try stream.writeAll(" align(");
2983 try self.writeInstRef(stream, align_ref);
2984 try stream.writeAll(")");
2985 }
2986 if (has_value) {
2987 const default_ref = @intToEnum(Inst.Ref, self.code.extra[extra_index]);
2988 extra_index += 1;
2989
2990 try stream.writeAll(" = ");
2991 try self.writeInstRef(stream, default_ref);
2992 }
2993 try stream.writeAll(",\n");
2994 }
2995
2996 self.indent -= 2;
2997 try stream.writeByteNTimes(' ', self.indent);
2998 try stream.writeAll("}, {");
2999 if (decls_len == 0) {
3000 try stream.writeAll("}");
3001 } else {
3002 try stream.writeAll("\n");
3003 self.indent += 2;
3004 try self.writeDecls(stream, decls_len, extra_index);
3005 self.indent -= 2;
3006 try stream.writeByteNTimes(' ', self.indent);
3007 try stream.writeAll("}");
3008 }
3009 try self.writeFlag(stream, ", autoenum", first_has_auto_enum.?);
3010 try stream.writeAll(") ");
3011 try self.writeSrc(stream, inst_data.src());
3012 }
3013
28873014 fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !void {
28883015 const parent_decl_node = self.parent_decl_node;
28893016 const bit_bags_count = std.math.divCeil(usize, decls_len, 16) catch unreachable;
......@@ -2930,10 +3057,10 @@ const Writer = struct {
29303057 const body = self.code.extra[extra.end..][0..extra.data.body_len];
29313058 const fields_len = extra.data.fields_len;
29323059 const decls_len = extra.data.decls_len;
2933 const tag_ty_ref = extra.data.tag_type;
3060 const tag_type_ref = extra.data.tag_type;
29343061
2935 if (tag_ty_ref != .none) {
2936 try self.writeInstRef(stream, tag_ty_ref);
3062 if (tag_type_ref != .none) {
3063 try self.writeInstRef(stream, tag_type_ref);
29373064 try stream.writeAll(", ");
29383065 }
29393066