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(...@@ -1509,9 +1509,11 @@ fn arrayInitExpr(
1509 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;1509 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
1510 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);1510 return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
1511 },1511 },
1512 .ty, .coerced_ty => {1512 .ty, .coerced_ty => |ty_inst| {
1513 const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;1513 const arr_ty = if (types.array != .none) types.array else blk: {
1514 const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);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);
1515 return rvalue(gz, ri, result, node);1517 return rvalue(gz, ri, result, node);
1516 },1518 },
1517 .ptr => |ptr_res| {1519 .ptr => |ptr_res| {
...@@ -1748,7 +1750,9 @@ fn structInitExpr(...@@ -1748,7 +1750,9 @@ fn structInitExpr(
1748 },1750 },
1749 .ty, .coerced_ty => |ty_inst| {1751 .ty, .coerced_ty => |ty_inst| {
1750 if (struct_init.ast.type_expr == 0) {1752 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);
1752 return rvalue(gz, ri, result, node);1756 return rvalue(gz, ri, result, node);
1753 }1757 }
1754 const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);1758 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...@@ -2743,6 +2747,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2743 .for_len,2747 .for_len,
2744 .@"try",2748 .@"try",
2745 .try_ptr,2749 .try_ptr,
2750 .opt_eu_base_ty,
2746 => break :b false,2751 => break :b false,
27472752
2748 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {2753 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
...@@ -8314,7 +8319,10 @@ fn builtinCall(...@@ -8314,7 +8319,10 @@ fn builtinCall(
8314 local_val.used = ident_token;8319 local_val.used = ident_token;
8315 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{8320 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
8316 .operand = local_val.inst,8321 .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]),
8318 });8326 });
8319 return rvalue(gz, ri, .void_value, node);8327 return rvalue(gz, ri, .void_value, node);
8320 }8328 }
...@@ -8329,7 +8337,10 @@ fn builtinCall(...@@ -8329,7 +8337,10 @@ fn builtinCall(
8329 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);8337 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
8330 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{8338 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
8331 .operand = loaded,8339 .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]),
8333 });8344 });
8334 return rvalue(gz, ri, .void_value, node);8345 return rvalue(gz, ri, .void_value, node);
8335 }8346 }
...@@ -8363,7 +8374,10 @@ fn builtinCall(...@@ -8363,7 +8374,10 @@ fn builtinCall(
8363 },8374 },
8364 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),8375 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
8365 }8376 }
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]);
8367 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{8381 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
8368 .namespace = namespace,8382 .namespace = namespace,
8369 .decl_name = decl_name,8383 .decl_name = decl_name,
...@@ -8373,7 +8387,10 @@ fn builtinCall(...@@ -8373,7 +8387,10 @@ fn builtinCall(
8373 },8387 },
8374 .@"extern" => {8388 .@"extern" => {
8375 const type_inst = try typeExpr(gz, scope, params[0]);8389 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]);
8377 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{8394 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
8378 .node = gz.nodeIndexToRelative(node),8395 .node = gz.nodeIndexToRelative(node),
8379 .lhs = type_inst,8396 .lhs = type_inst,
...@@ -8477,7 +8494,10 @@ fn builtinCall(...@@ -8477,7 +8494,10 @@ fn builtinCall(
8477 // zig fmt: on8494 // zig fmt: on
84788495
8479 .Type => {8496 .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
8482 const gpa = gz.astgen.gpa;8502 const gpa = gz.astgen.gpa;
84838503
...@@ -8755,7 +8775,10 @@ fn builtinCall(...@@ -8755,7 +8775,10 @@ fn builtinCall(
8755 },8775 },
8756 .prefetch => {8776 .prefetch => {
8757 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);8777 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]);
8759 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{8782 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
8760 .node = gz.nodeIndexToRelative(node),8783 .node = gz.nodeIndexToRelative(node),
8761 .lhs = ptr,8784 .lhs = ptr,
src/Sema.zig+120-20
...@@ -1125,6 +1125,7 @@ fn analyzeBodyInner(...@@ -1125,6 +1125,7 @@ fn analyzeBodyInner(
1125 .array_base_ptr => try sema.zirArrayBasePtr(block, inst),1125 .array_base_ptr => try sema.zirArrayBasePtr(block, inst),
1126 .field_base_ptr => try sema.zirFieldBasePtr(block, inst),1126 .field_base_ptr => try sema.zirFieldBasePtr(block, inst),
1127 .for_len => try sema.zirForLen(block, inst),1127 .for_len => try sema.zirForLen(block, inst),
1128 .opt_eu_base_ty => try sema.zirOptEuBaseTy(block, inst),
11281129
1129 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),1130 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
1130 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),1131 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
...@@ -1359,12 +1360,12 @@ fn analyzeBodyInner(...@@ -1359,12 +1360,12 @@ fn analyzeBodyInner(
1359 continue;1360 continue;
1360 },1361 },
1361 .validate_array_init_ty => {1362 .validate_array_init_ty => {
1362 try sema.validateArrayInitTy(block, inst);1363 try sema.zirValidateArrayInitTy(block, inst);
1363 i += 1;1364 i += 1;
1364 continue;1365 continue;
1365 },1366 },
1366 .validate_struct_init_ty => {1367 .validate_struct_init_ty => {
1367 try sema.validateStructInitTy(block, inst);1368 try sema.zirValidateStructInitTy(block, inst);
1368 i += 1;1369 i += 1;
1369 continue;1370 continue;
1370 },1371 },
...@@ -4312,7 +4313,31 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -4312,7 +4313,31 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
4312 return len;4313 return len;
4313}4314}
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(
4316 sema: *Sema,4341 sema: *Sema,
4317 block: *Block,4342 block: *Block,
4318 inst: Zir.Inst.Index,4343 inst: Zir.Inst.Index,
...@@ -4322,7 +4347,11 @@ fn validateArrayInitTy(...@@ -4322,7 +4347,11 @@ fn validateArrayInitTy(
4322 const src = inst_data.src();4347 const src = inst_data.src();
4323 const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };4348 const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };
4324 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;4349 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
4327 switch (ty.zigTypeTag(mod)) {4356 switch (ty.zigTypeTag(mod)) {
4328 .Array => {4357 .Array => {
...@@ -4358,7 +4387,7 @@ fn validateArrayInitTy(...@@ -4358,7 +4387,7 @@ fn validateArrayInitTy(
4358 return sema.failWithArrayInitNotSupported(block, ty_src, ty);4387 return sema.failWithArrayInitNotSupported(block, ty_src, ty);
4359}4388}
43604389
4361fn validateStructInitTy(4390fn zirValidateStructInitTy(
4362 sema: *Sema,4391 sema: *Sema,
4363 block: *Block,4392 block: *Block,
4364 inst: Zir.Inst.Index,4393 inst: Zir.Inst.Index,
...@@ -4366,7 +4395,11 @@ fn validateStructInitTy(...@@ -4366,7 +4395,11 @@ fn validateStructInitTy(
4366 const mod = sema.mod;4395 const mod = sema.mod;
4367 const inst_data = sema.code.instructions.items(.data)[inst].un_node;4396 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
4368 const src = inst_data.src();4397 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
4371 switch (ty.zigTypeTag(mod)) {4404 switch (ty.zigTypeTag(mod)) {
4372 .Struct, .Union => return,4405 .Struct, .Union => return,
...@@ -7744,7 +7777,15 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -7744,7 +7777,15 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
7744fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7777fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7745 const mod = sema.mod;7778 const mod = sema.mod;
7746 const bin = sema.code.instructions.items(.data)[inst].bin;7779 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);
7748 assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction7789 assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction
7749 if (indexable_ty.zigTypeTag(mod) == .Struct) {7790 if (indexable_ty.zigTypeTag(mod) == .Struct) {
7750 const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod);7791 const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod);
...@@ -18794,7 +18835,13 @@ fn zirStructInit(...@@ -18794,7 +18835,13 @@ fn zirStructInit(
18794 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;18835 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
18795 const first_field_type_data = zir_datas[first_item.field_type].pl_node;18836 const first_field_type_data = zir_datas[first_item.field_type].pl_node;
18796 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;18837 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 };
18798 try sema.resolveTypeLayout(resolved_ty);18845 try sema.resolveTypeLayout(resolved_ty);
1879918846
18800 if (resolved_ty.zigTypeTag(mod) == .Struct) {18847 if (resolved_ty.zigTypeTag(mod) == .Struct) {
...@@ -19037,26 +19084,57 @@ fn zirStructInitAnon(...@@ -19037,26 +19084,57 @@ fn zirStructInitAnon(
19037 inst: Zir.Inst.Index,19084 inst: Zir.Inst.Index,
19038 is_ref: bool,19085 is_ref: bool,
19039) CompileError!Air.Inst.Ref {19086) CompileError!Air.Inst.Ref {
19040 const mod = sema.mod;
19041 const gpa = sema.gpa;
19042 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;19087 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
19043 const src = inst_data.src();19088 const src = inst_data.src();
19044 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);19089 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);
19046 const values = try sema.arena.alloc(InternPool.Index, types.len);19112 const values = try sema.arena.alloc(InternPool.Index, types.len);
19113
19047 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);19114 var fields = std.AutoArrayHashMap(InternPool.NullTerminatedString, u32).init(sema.arena);
19048 try fields.ensureUnusedCapacity(types.len);19115 try fields.ensureUnusedCapacity(types.len);
1904919116
19050 // Find which field forces the expression to be runtime, if any.19117 // Find which field forces the expression to be runtime, if any.
19051 const opt_runtime_index = rs: {19118 const opt_runtime_index = rs: {
19052 var runtime_index: ?usize = null;19119 var runtime_index: ?usize = null;
19053 var extra_index = extra.end;19120 var extra_index = extra_end;
19054 for (types, 0..) |*field_ty, i_usize| {19121 for (types, 0..) |*field_ty, i_usize| {
19055 const i = @as(u32, @intCast(i_usize));19122 const i: u32 = @intCast(i_usize);
19056 const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);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 };
19057 extra_index = item.end;19127 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 };
19060 const name_ip = try mod.intern_pool.getOrPutString(gpa, name);19138 const name_ip = try mod.intern_pool.getOrPutString(gpa, name);
19061 const gop = fields.getOrPutAssumeCapacity(name_ip);19139 const gop = fields.getOrPutAssumeCapacity(name_ip);
19062 if (gop.found_existing) {19140 if (gop.found_existing) {
...@@ -19129,10 +19207,13 @@ fn zirStructInitAnon(...@@ -19129,10 +19207,13 @@ fn zirStructInitAnon(
19129 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },19207 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19130 });19208 });
19131 const alloc = try block.addTy(.alloc, alloc_ty);19209 const alloc = try block.addTy(.alloc, alloc_ty);
19132 var extra_index = extra.end;19210 var extra_index = extra_end;
19133 for (types, 0..) |field_ty, i_usize| {19211 for (types, 0..) |field_ty, i_usize| {
19134 const i = @as(u32, @intCast(i_usize));19212 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 };
19136 extra_index = item.end;19217 extra_index = item.end;
1913719218
19138 const field_ptr_ty = try mod.ptrType(.{19219 const field_ptr_ty = try mod.ptrType(.{
...@@ -19150,9 +19231,12 @@ fn zirStructInitAnon(...@@ -19150,9 +19231,12 @@ fn zirStructInitAnon(
19150 }19231 }
1915119232
19152 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);19233 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
19153 var extra_index = extra.end;19234 var extra_index = extra_end;
19154 for (types, 0..) |_, i| {19235 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 };
19156 extra_index = item.end;19240 extra_index = item.end;
19157 element_refs[i] = try sema.resolveInst(item.data.init);19241 element_refs[i] = try sema.resolveInst(item.data.init);
19158 }19242 }
...@@ -19175,7 +19259,13 @@ fn zirArrayInit(...@@ -19175,7 +19259,13 @@ fn zirArrayInit(
19175 const args = sema.code.refSlice(extra.end, extra.data.operands_len);19259 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
19176 assert(args.len >= 2); // array_ty + at least one element19260 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 };
19179 const sentinel_val = array_ty.sentinel(mod);19269 const sentinel_val = array_ty.sentinel(mod);
1918019270
19181 const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null));19271 const resolved_args = try gpa.alloc(Air.Inst.Ref, args.len - 1 + @intFromBool(sentinel_val != null));
...@@ -19283,6 +19373,16 @@ fn zirArrayInitAnon(...@@ -19283,6 +19373,16 @@ fn zirArrayInitAnon(
19283 const src = inst_data.src();19373 const src = inst_data.src();
19284 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);19374 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
19285 const operands = sema.code.refSlice(extra.end, extra.data.operands_len);19375 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 {
19286 const mod = sema.mod;19386 const mod = sema.mod;
1928719387
19288 const types = try sema.arena.alloc(InternPool.Index, operands.len);19388 const types = try sema.arena.alloc(InternPool.Index, operands.len);
src/Zir.zig+9
...@@ -700,10 +700,16 @@ pub const Inst = struct {...@@ -700,10 +700,16 @@ pub const Inst = struct {
700 /// *?S returns *S700 /// *?S returns *S
701 /// Uses the `un_node` field.701 /// Uses the `un_node` field.
702 field_base_ptr,702 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,
703 /// Checks that the type supports array init syntax.707 /// Checks that the type supports array init syntax.
708 /// Returns the underlying indexable type (since the given type may be e.g. an optional).
704 /// Uses the `un_node` field.709 /// Uses the `un_node` field.
705 validate_array_init_ty,710 validate_array_init_ty,
706 /// Checks that the type supports struct init syntax.711 /// Checks that the type supports struct init syntax.
712 /// Returns the underlying struct type (since the given type may be e.g. an optional).
707 /// Uses the `un_node` field.713 /// Uses the `un_node` field.
708 validate_struct_init_ty,714 validate_struct_init_ty,
709 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct715 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct
...@@ -1234,6 +1240,7 @@ pub const Inst = struct {...@@ -1234,6 +1240,7 @@ pub const Inst = struct {
1234 .save_err_ret_index,1240 .save_err_ret_index,
1235 .restore_err_ret_index,1241 .restore_err_ret_index,
1236 .for_len,1242 .for_len,
1243 .opt_eu_base_ty,
1237 => false,1244 => false,
12381245
1239 .@"break",1246 .@"break",
...@@ -1522,6 +1529,7 @@ pub const Inst = struct {...@@ -1522,6 +1529,7 @@ pub const Inst = struct {
1522 .for_len,1529 .for_len,
1523 .@"try",1530 .@"try",
1524 .try_ptr,1531 .try_ptr,
1532 .opt_eu_base_ty,
1525 => false,1533 => false,
15261534
1527 .extended => switch (data.extended.opcode) {1535 .extended => switch (data.extended.opcode) {
...@@ -1676,6 +1684,7 @@ pub const Inst = struct {...@@ -1676,6 +1684,7 @@ pub const Inst = struct {
1676 .switch_block_ref = .pl_node,1684 .switch_block_ref = .pl_node,
1677 .array_base_ptr = .un_node,1685 .array_base_ptr = .un_node,
1678 .field_base_ptr = .un_node,1686 .field_base_ptr = .un_node,
1687 .opt_eu_base_ty = .un_node,
1679 .validate_array_init_ty = .pl_node,1688 .validate_array_init_ty = .pl_node,
1680 .validate_struct_init_ty = .un_node,1689 .validate_struct_init_ty = .un_node,
1681 .validate_struct_init = .pl_node,1690 .validate_struct_init = .pl_node,
src/print_zir.zig+1
...@@ -229,6 +229,7 @@ const Writer = struct {...@@ -229,6 +229,7 @@ const Writer = struct {
229 .make_ptr_const,229 .make_ptr_const,
230 .validate_deref,230 .validate_deref,
231 .check_comptime_control_flow,231 .check_comptime_control_flow,
232 .opt_eu_base_ty,
232 => try self.writeUnNode(stream, inst),233 => try self.writeUnNode(stream, inst),
233234
234 .ref,235 .ref,
src/type.zig+1
...@@ -3039,6 +3039,7 @@ pub const Type = struct {...@@ -3039,6 +3039,7 @@ pub const Type = struct {
3039 return switch (mod.intern_pool.indexToKey(ty.toIntern())) {3039 return switch (mod.intern_pool.indexToKey(ty.toIntern())) {
3040 .struct_type => |struct_type| {3040 .struct_type => |struct_type| {
3041 const struct_obj = mod.structPtrUnwrap(struct_type.index).?;3041 const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
3042 assert(struct_obj.haveFieldTypes());
3042 return struct_obj.fields.values()[index].ty;3043 return struct_obj.fields.values()[index].ty;
3043 },3044 },
3044 .union_type => |union_type| {3045 .union_type => |union_type| {
test/behavior/array.zig+14
...@@ -761,3 +761,17 @@ test "slicing array of zero-sized values" {...@@ -761,3 +761,17 @@ test "slicing array of zero-sized values" {
761 for (arr[0..]) |zero|761 for (arr[0..]) |zero|
762 try expect(zero == 0);762 try expect(zero == 0);
763}763}
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" {...@@ -1724,3 +1724,17 @@ test "packed struct field in anonymous struct" {
1724fn countFields(v: anytype) usize {1724fn countFields(v: anytype) usize {
1725 return @typeInfo(@TypeOf(v)).Struct.fields.len;1725 return @typeInfo(@TypeOf(v)).Struct.fields.len;
1726}1726}
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}