authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-07-29 06:03:51+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-08-09 19:46:55+01:00
log6917a8c25824d12f00327171b583d6cd9a830c29
tree63b51fef7ea792e31c4cb14fdd732f632e62b03b
parent0461a64a93f0596e98b62d596bb547e5455577d2
signaturelock-open Commit is signed but in an unrecognized format.

AstGen: handle `ty` result location for struct and array init correctly

Well, this was a journey! The original issue I was trying to fix is covered by the new behavior test in array.zig: in essence, `ty` and `coerced_ty` result locations were not correctly propagated. While fixing this, I noticed a similar bug in struct inits: the type was propagated to *fields* fine, but the actual struct init was unnecessarily anonymous, which could lead to unnecessary copies. Note that the behavior test added in struct.zig was already passing - the bug here didn't change any easy-to-test behavior - but I figured I'd add it anyway. This is a little harder than it seems, because the result type may not itself be an array/struct type: it could be an optional / error union wrapper. A new ZIR instruction is introduced to unwrap these. This is also made a little tricky by the fact that it's possible for result types to be unknown at the time of semantic analysis (due to `anytype` parameters), leading to generic poison. In these cases, we must essentially downgrade to an anonymous initialization. Fixing these issues exposed *another* bug, related to type resolution in Sema. That issue is now tracked by #16603. As a temporary workaround for this bug, a few result locations for builtin function operands have been disabled in AstGen. This is technically a breaking change, but it's very minor: I doubt it'll cause any breakage in the wild.

7 files changed, 192 insertions(+), 30 deletions(-)

src/AstGen.zig+33-10
......@@ -1509,9 +1509,11 @@ fn arrayInitExpr(
15091509 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
15101510 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
15111511 },
1512 .ty, .coerced_ty => {
1513 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
1514 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
1512 .ty, .coerced_ty => |ty_inst| {
1513 const arr_ty = if (types.array != .none) types.array else blk: {
1514 break :blk try gz.addUnNode(.opt_eu_base_ty, ty_inst, node);
1515 };
1516 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, arr_ty, types.elem, .array_init);
15151517 return rvalue(gz, ri, result, node);
15161518 },
15171519 .ptr => |ptr_res| {
......@@ -1748,7 +1750,9 @@ fn structInitExpr(
17481750 },
17491751 .ty, .coerced_ty => |ty_inst| {
17501752 if (struct_init.ast.type_expr == 0) {
1751 const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
1753 const struct_ty_inst = try gz.addUnNode(.opt_eu_base_ty, ty_inst, node);
1754 _ = try gz.addUnNode(.validate_struct_init_ty, struct_ty_inst, node);
1755 const result = try structInitExprRlTy(gz, scope, node, struct_init, struct_ty_inst, .struct_init);
17521756 return rvalue(gz, ri, result, node);
17531757 }
17541758 const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
......@@ -2743,6 +2747,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
27432747 .for_len,
27442748 .@"try",
27452749 .try_ptr,
2750 .opt_eu_base_ty,
27462751 => break :b false,
27472752
27482753 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
......@@ -8314,7 +8319,10 @@ fn builtinCall(
83148319 local_val.used = ident_token;
83158320 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
83168321 .operand = local_val.inst,
8317 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
8322 // TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but
8323 // that currently hits assertions in Sema due to type resolution issues.
8324 // See #16603
8325 .options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]),
83188326 });
83198327 return rvalue(gz, ri, .void_value, node);
83208328 }
......@@ -8329,7 +8337,10 @@ fn builtinCall(
83298337 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
83308338 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
83318339 .operand = loaded,
8332 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
8340 // TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but
8341 // that currently hits assertions in Sema due to type resolution issues.
8342 // See #16603
8343 .options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]),
83338344 });
83348345 return rvalue(gz, ri, .void_value, node);
83358346 }
......@@ -8363,7 +8374,10 @@ fn builtinCall(
83638374 },
83648375 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
83658376 }
8366 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .export_options_type } }, params[1]);
8377 // TODO: the result location here should be `.{ .coerced_ty = .export_options_type }`, but
8378 // that currently hits assertions in Sema due to type resolution issues.
8379 // See #16603
8380 const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
83678381 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
83688382 .namespace = namespace,
83698383 .decl_name = decl_name,
......@@ -8373,7 +8387,10 @@ fn builtinCall(
83738387 },
83748388 .@"extern" => {
83758389 const type_inst = try typeExpr(gz, scope, params[0]);
8376 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .extern_options_type } }, params[1]);
8390 // TODO: the result location here should be `.{ .coerced_ty = .extern_options_type }`, but
8391 // that currently hits assertions in Sema due to type resolution issues.
8392 // See #16603
8393 const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
83778394 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
83788395 .node = gz.nodeIndexToRelative(node),
83798396 .lhs = type_inst,
......@@ -8477,7 +8494,10 @@ fn builtinCall(
84778494 // zig fmt: on
84788495
84798496 .Type => {
8480 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .type_info_type } }, params[0]);
8497 // TODO: the result location here should be `.{ .coerced_ty = .type_info_type }`, but
8498 // that currently hits assertions in Sema due to type resolution issues.
8499 // See #16603
8500 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
84818501
84828502 const gpa = gz.astgen.gpa;
84838503
......@@ -8755,7 +8775,10 @@ fn builtinCall(
87558775 },
87568776 .prefetch => {
87578777 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
8758 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]);
8778 // TODO: the result location here should be `.{ .coerced_ty = .preftech_options_type }`, but
8779 // that currently hits assertions in Sema due to type resolution issues.
8780 // See #16603
8781 const options = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
87598782 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
87608783 .node = gz.nodeIndexToRelative(node),
87618784 .lhs = ptr,
src/Sema.zig+120-20
......@@ -1125,6 +1125,7 @@ fn analyzeBodyInner(
11251125 .array_base_ptr => try sema.zirArrayBasePtr(block, inst),
11261126 .field_base_ptr => try sema.zirFieldBasePtr(block, inst),
11271127 .for_len => try sema.zirForLen(block, inst),
1128 .opt_eu_base_ty => try sema.zirOptEuBaseTy(block, inst),
11281129
11291130 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
11301131 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
......@@ -1359,12 +1360,12 @@ fn analyzeBodyInner(
13591360 continue;
13601361 },
13611362 .validate_array_init_ty => {
1362 try sema.validateArrayInitTy(block, inst);
1363 try sema.zirValidateArrayInitTy(block, inst);
13631364 i += 1;
13641365 continue;
13651366 },
13661367 .validate_struct_init_ty => {
1367 try sema.validateStructInitTy(block, inst);
1368 try sema.zirValidateStructInitTy(block, inst);
13681369 i += 1;
13691370 continue;
13701371 },
......@@ -4312,7 +4313,31 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
43124313 return len;
43134314}
43144315
4315fn validateArrayInitTy(
4316fn zirOptEuBaseTy(
4317 sema: *Sema,
4318 block: *Block,
4319 inst: Zir.Inst.Index,
4320) CompileError!Air.Inst.Ref {
4321 const mod = sema.mod;
4322 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
4323 var ty = sema.resolveType(block, .unneeded, inst_data.operand) catch |err| switch (err) {
4324 // Since this is a ZIR instruction that returns a type, encountering
4325 // generic poison should not result in a failed compilation, but the
4326 // generic poison type. This prevents unnecessary failures when
4327 // constructing types at compile-time.
4328 error.GenericPoison => return .generic_poison_type,
4329 else => |e| return e,
4330 };
4331 while (true) {
4332 switch (ty.zigTypeTag(mod)) {
4333 .Optional => ty = ty.optionalChild(mod),
4334 .ErrorUnion => ty = ty.errorUnionPayload(mod),
4335 else => return sema.addType(ty),
4336 }
4337 }
4338}
4339
4340fn zirValidateArrayInitTy(
43164341 sema: *Sema,
43174342 block: *Block,
43184343 inst: Zir.Inst.Index,
......@@ -4322,7 +4347,11 @@ fn validateArrayInitTy(
43224347 const src = inst_data.src();
43234348 const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };
43244349 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
4325 const ty = try sema.resolveType(block, ty_src, extra.ty);
4350 const ty = sema.resolveType(block, ty_src, extra.ty) catch |err| switch (err) {
4351 // It's okay for the type to be unknown: this will result in an anonymous array init.
4352 error.GenericPoison => return,
4353 else => |e| return e,
4354 };
43264355
43274356 switch (ty.zigTypeTag(mod)) {
43284357 .Array => {
......@@ -4358,7 +4387,7 @@ fn validateArrayInitTy(
43584387 return sema.failWithArrayInitNotSupported(block, ty_src, ty);
43594388}
43604389
4361fn validateStructInitTy(
4390fn zirValidateStructInitTy(
43624391 sema: *Sema,
43634392 block: *Block,
43644393 inst: Zir.Inst.Index,
......@@ -4366,7 +4395,11 @@ fn validateStructInitTy(
43664395 const mod = sema.mod;
43674396 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
43684397 const src = inst_data.src();
4369 const ty = try sema.resolveType(block, src, inst_data.operand);
4398 const ty = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) {
4399 // It's okay for the type to be unknown: this will result in an anonymous struct init.
4400 error.GenericPoison => return,
4401 else => |e| return e,
4402 };
43704403
43714404 switch (ty.zigTypeTag(mod)) {
43724405 .Struct, .Union => return,
......@@ -7744,7 +7777,15 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
77447777fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
77457778 const mod = sema.mod;
77467779 const bin = sema.code.instructions.items(.data)[inst].bin;
7747 const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs);
7780 const operand = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) {
7781 // Since this is a ZIR instruction that returns a type, encountering
7782 // generic poison should not result in a failed compilation, but the
7783 // generic poison type. This prevents unnecessary failures when
7784 // constructing types at compile-time.
7785 error.GenericPoison => return .generic_poison_type,
7786 else => |e| return e,
7787 };
7788 const indexable_ty = try sema.resolveTypeFields(operand);
77487789 assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction
77497790 if (indexable_ty.zigTypeTag(mod) == .Struct) {
77507791 const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod);
......@@ -18794,7 +18835,13 @@ fn zirStructInit(
1879418835 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
1879518836 const first_field_type_data = zir_datas[first_item.field_type].pl_node;
1879618837 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
18797 const resolved_ty = try sema.resolveType(block, src, first_field_type_extra.container_type);
18838 const resolved_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) {
18839 error.GenericPoison => {
18840 // The type wasn't actually known, so treat this as an anon struct init.
18841 return sema.structInitAnon(block, src, .typed_init, extra.data, extra.end, is_ref);
18842 },
18843 else => |e| return e,
18844 };
1879818845 try sema.resolveTypeLayout(resolved_ty);
1879918846
1880018847 if (resolved_ty.zigTypeTag(mod) == .Struct) {
......@@ -19037,26 +19084,57 @@ fn zirStructInitAnon(
1903719084 inst: Zir.Inst.Index,
1903819085 is_ref: bool,
1903919086) CompileError!Air.Inst.Ref {
19040 const mod = sema.mod;
19041 const gpa = sema.gpa;
1904219087 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1904319088 const src = inst_data.src();
1904419089 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
19045 const types = try sema.arena.alloc(InternPool.Index, extra.data.fields_len);
19090 return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, is_ref);
19091}
19092
19093fn structInitAnon(
19094 sema: *Sema,
19095 block: *Block,
19096 src: LazySrcLoc,
19097 /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a
19098 /// generic poison type. In this case, we need to know to interpret the extra data differently.
19099 comptime kind: enum { anon_init, typed_init },
19100 extra_data: switch (kind) {
19101 .anon_init => Zir.Inst.StructInitAnon,
19102 .typed_init => Zir.Inst.StructInit,
19103 },
19104 extra_end: usize,
19105 is_ref: bool,
19106) CompileError!Air.Inst.Ref {
19107 const mod = sema.mod;
19108 const gpa = sema.gpa;
19109 const zir_datas = sema.code.instructions.items(.data);
19110
19111 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
1904619112 const values = try sema.arena.alloc(InternPool.Index, types.len);
19113
1904719114 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
1904819115 try fields.ensureUnusedCapacity(types.len);
1904919116
1905019117 // Find which field forces the expression to be runtime, if any.
1905119118 const opt_runtime_index = rs: {
1905219119 var runtime_index: ?usize = null;
19053 var extra_index = extra.end;
19120 var extra_index = extra_end;
1905419121 for (types, 0..) |*field_ty, i_usize| {
19055 const i = @as(u32, @intCast(i_usize));
19056 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
19122 const i: u32 = @intCast(i_usize);
19123 const item = switch (kind) {
19124 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19125 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
19126 };
1905719127 extra_index = item.end;
1905819128
19059 const name = sema.code.nullTerminatedString(item.data.field_name);
19129 const name = switch (kind) {
19130 .anon_init => sema.code.nullTerminatedString(item.data.field_name),
19131 .typed_init => name: {
19132 // `item.data.field_type` references a `field_type` instruction
19133 const field_type_data = zir_datas[item.data.field_type].pl_node;
19134 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index);
19135 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
19136 },
19137 };
1906019138 const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
1906119139 const gop = fields.getOrPutAssumeCapacity(name_ip);
1906219140 if (gop.found_existing) {
......@@ -19129,10 +19207,13 @@ fn zirStructInitAnon(
1912919207 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
1913019208 });
1913119209 const alloc = try block.addTy(.alloc, alloc_ty);
19132 var extra_index = extra.end;
19210 var extra_index = extra_end;
1913319211 for (types, 0..) |field_ty, i_usize| {
1913419212 const i = @as(u32, @intCast(i_usize));
19135 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
19213 const item = switch (kind) {
19214 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19215 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
19216 };
1913619217 extra_index = item.end;
1913719218
1913819219 const field_ptr_ty = try mod.ptrType(.{
......@@ -19150,9 +19231,12 @@ fn zirStructInitAnon(
1915019231 }
1915119232
1915219233 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
19153 var extra_index = extra.end;
19234 var extra_index = extra_end;
1915419235 for (types, 0..) |_, i| {
19155 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
19236 const item = switch (kind) {
19237 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19238 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
19239 };
1915619240 extra_index = item.end;
1915719241 element_refs[i] = try sema.resolveInst(item.data.init);
1915819242 }
......@@ -19175,7 +19259,13 @@ fn zirArrayInit(
1917519259 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
1917619260 assert(args.len >= 2); // array_ty + at least one element
1917719261
19178 const array_ty = try sema.resolveType(block, src, args[0]);
19262 const array_ty = sema.resolveType(block, src, args[0]) catch |err| switch (err) {
19263 error.GenericPoison => {
19264 // The type wasn't actually known, so treat this as an anon array init.
19265 return sema.arrayInitAnon(block, src, args[1..], is_ref);
19266 },
19267 else => |e| return e,
19268 };
1917919269 const sentinel_val = array_ty.sentinel(mod);
1918019270
1918119271 const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null));
......@@ -19283,6 +19373,16 @@ fn zirArrayInitAnon(
1928319373 const src = inst_data.src();
1928419374 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
1928519375 const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
19376 return sema.arrayInitAnon(block, src, operands, is_ref);
19377}
19378
19379fn arrayInitAnon(
19380 sema: *Sema,
19381 block: *Block,
19382 src: LazySrcLoc,
19383 operands: []const Zir.Inst.Ref,
19384 is_ref: bool,
19385) CompileError!Air.Inst.Ref {
1928619386 const mod = sema.mod;
1928719387
1928819388 const types = try sema.arena.alloc(InternPool.Index, operands.len);
src/Zir.zig+9
......@@ -700,10 +700,16 @@ pub const Inst = struct {
700700 /// *?S returns *S
701701 /// Uses the `un_node` field.
702702 field_base_ptr,
703 /// Given a type, strips all optional and error union types wrapping it.
704 /// e.g. `E!?u32` becomes `u32`, `[]u8` becomes `[]u8`.
705 /// Uses the `un_node` field.
706 opt_eu_base_ty,
703707 /// Checks that the type supports array init syntax.
708 /// Returns the underlying indexable type (since the given type may be e.g. an optional).
704709 /// Uses the `un_node` field.
705710 validate_array_init_ty,
706711 /// Checks that the type supports struct init syntax.
712 /// Returns the underlying struct type (since the given type may be e.g. an optional).
707713 /// Uses the `un_node` field.
708714 validate_struct_init_ty,
709715 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct
......@@ -1234,6 +1240,7 @@ pub const Inst = struct {
12341240 .save_err_ret_index,
12351241 .restore_err_ret_index,
12361242 .for_len,
1243 .opt_eu_base_ty,
12371244 => false,
12381245
12391246 .@"break",
......@@ -1522,6 +1529,7 @@ pub const Inst = struct {
15221529 .for_len,
15231530 .@"try",
15241531 .try_ptr,
1532 .opt_eu_base_ty,
15251533 => false,
15261534
15271535 .extended => switch (data.extended.opcode) {
......@@ -1676,6 +1684,7 @@ pub const Inst = struct {
16761684 .switch_block_ref = .pl_node,
16771685 .array_base_ptr = .un_node,
16781686 .field_base_ptr = .un_node,
1687 .opt_eu_base_ty = .un_node,
16791688 .validate_array_init_ty = .pl_node,
16801689 .validate_struct_init_ty = .un_node,
16811690 .validate_struct_init = .pl_node,
src/print_zir.zig+1
......@@ -229,6 +229,7 @@ const Writer = struct {
229229 .make_ptr_const,
230230 .validate_deref,
231231 .check_comptime_control_flow,
232 .opt_eu_base_ty,
232233 => try self.writeUnNode(stream, inst),
233234
234235 .ref,
src/type.zig+1
......@@ -3039,6 +3039,7 @@ pub const Type = struct {
30393039 return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
30403040 .struct_type => |struct_type| {
30413041 const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
3042 assert(struct_obj.haveFieldTypes());
30423043 return struct_obj.fields.values()[index].ty;
30433044 },
30443045 .union_type => |union_type| {
test/behavior/array.zig+14
......@@ -761,3 +761,17 @@ test "slicing array of zero-sized values" {
761761 for (arr[0..]) |zero|
762762 try expect(zero == 0);
763763}
764
765test "array init with no result pointer sets field result types" {
766 const S = struct {
767 // A function parameter has a result type, but no result pointer.
768 fn f(arr: [1]u32) u32 {
769 return arr[0];
770 }
771 };
772
773 const x: u64 = 123;
774 const y = S.f(.{@intCast(x)});
775
776 try expect(y == x);
777}
test/behavior/struct.zig+14
......@@ -1724,3 +1724,17 @@ test "packed struct field in anonymous struct" {
17241724fn countFields(v: anytype) usize {
17251725 return @typeInfo(@TypeOf(v)).Struct.fields.len;
17261726}
1727
1728test "struct init with no result pointer sets field result types" {
1729 const S = struct {
1730 // A function parameter has a result type, but no result pointer.
1731 fn f(s: struct { x: u32 }) u32 {
1732 return s.x;
1733 }
1734 };
1735
1736 const x: u64 = 123;
1737 const y = S.f(.{ .x = @intCast(x) });
1738
1739 try expect(y == x);
1740}