authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-20 00:33:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-20 16:17:16-07:00
loge86ff712a666ab5be54fa763cc12a5f245718117
tree90166386cac32112afbb0c087453cf22938a2702
parenteb70f6e8d7f4c1a735fe25de368f6d5459cba16c

stage2: implement tuples

* AIR instruction vector_init gains the ability to init arrays and tuples in addition to vectors. This will probably also gain the ability to initialize structs and be renamed to `aggregate_init`. * AstGen prefers to use an `anon_array_init` ZIR instruction for local variables when the init expr is an array literal and there is no type.

9 files changed, 550 insertions(+), 97 deletions(-)

src/Air.zig+3-1
...@@ -510,9 +510,11 @@ pub const Inst = struct {...@@ -510,9 +510,11 @@ pub const Inst = struct {
510 /// Uses the `un_op` field.510 /// Uses the `un_op` field.
511 error_name,511 error_name,
512512
513 /// Constructs a vector value out of runtime-known elements.513 /// Constructs a vector, tuple, or array value out of runtime-known elements.
514 /// Some of the elements may be comptime-known.
514 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which515 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which
515 /// is a `Ref`. Length of the array is given by the vector type.516 /// is a `Ref`. Length of the array is given by the vector type.
517 /// TODO rename this to `array_init` and make it support array values too.
516 vector_init,518 vector_init,
517519
518 /// Communicates an intent to load memory.520 /// Communicates an intent to load memory.
src/AstGen.zig+19-13
...@@ -2581,9 +2581,12 @@ fn varDecl(...@@ -2581,9 +2581,12 @@ fn varDecl(
2581 // Depending on the type of AST the initialization expression is, we may need an lvalue2581 // Depending on the type of AST the initialization expression is, we may need an lvalue
2582 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as2582 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
2583 // the variable, no memory location needed.2583 // the variable, no memory location needed.
2584 if (align_inst == .none and !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) {2584 const type_node = var_decl.ast.type_node;
2585 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{2585 if (align_inst == .none and
2586 .ty = try typeExpr(gz, scope, var_decl.ast.type_node),2586 !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node, type_node != 0))
2587 {
2588 const result_loc: ResultLoc = if (type_node != 0) .{
2589 .ty = try typeExpr(gz, scope, type_node),
2587 } else .none;2590 } else .none;
2588 const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);2591 const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);
25892592
...@@ -6008,7 +6011,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6008,7 +6011,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6008 return Zir.Inst.Ref.unreachable_value;6011 return Zir.Inst.Ref.unreachable_value;
6009 }6012 }
60106013
6011 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{6014 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{
6012 .ptr = try gz.addNodeExtended(.ret_ptr, node),6015 .ptr = try gz.addNodeExtended(.ret_ptr, node),
6013 } else .{6016 } else .{
6014 .ty = try gz.addNodeExtended(.ret_type, node),6017 .ty = try gz.addNodeExtended(.ret_type, node),
...@@ -7725,7 +7728,7 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{...@@ -7725,7 +7728,7 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
7725 .{ "void", .void_type },7728 .{ "void", .void_type },
7726});7729});
77277730
7728fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool {7731fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {
7729 const node_tags = tree.nodes.items(.tag);7732 const node_tags = tree.nodes.items(.tag);
7730 const node_datas = tree.nodes.items(.data);7733 const node_datas = tree.nodes.items(.data);
7731 const main_tokens = tree.nodes.items(.main_token);7734 const main_tokens = tree.nodes.items(.main_token);
...@@ -7875,24 +7878,27 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -7875,24 +7878,27 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
7875 .@"orelse",7878 .@"orelse",
7876 => node = node_datas[node].rhs,7879 => node = node_datas[node].rhs,
78777880
7878 // True because these are exactly the expressions we need memory locations for.7881 // Array and struct init exprs write to result locs, but anon literals do not.
7879 .array_init_one,7882 .array_init_one,
7880 .array_init_one_comma,7883 .array_init_one_comma,
7884 .struct_init_one,
7885 .struct_init_one_comma,
7886 .array_init,
7887 .array_init_comma,
7888 .struct_init,
7889 .struct_init_comma,
7890 => return have_res_ty or node_datas[node].lhs != 0,
7891
7892 // Anon literals do not need result location.
7881 .array_init_dot_two,7893 .array_init_dot_two,
7882 .array_init_dot_two_comma,7894 .array_init_dot_two_comma,
7883 .array_init_dot,7895 .array_init_dot,
7884 .array_init_dot_comma,7896 .array_init_dot_comma,
7885 .array_init,
7886 .array_init_comma,
7887 .struct_init_one,
7888 .struct_init_one_comma,
7889 .struct_init_dot_two,7897 .struct_init_dot_two,
7890 .struct_init_dot_two_comma,7898 .struct_init_dot_two_comma,
7891 .struct_init_dot,7899 .struct_init_dot,
7892 .struct_init_dot_comma,7900 .struct_init_dot_comma,
7893 .struct_init,7901 => return have_res_ty,
7894 .struct_init_comma,
7895 => return true,
78967902
7897 // True because depending on comptime conditions, sub-expressions7903 // True because depending on comptime conditions, sub-expressions
7898 // may be the kind that need memory locations.7904 // may be the kind that need memory locations.
src/Liveness.zig+1-1
...@@ -373,7 +373,7 @@ fn analyzeInst(...@@ -373,7 +373,7 @@ fn analyzeInst(
373 .vector_init => {373 .vector_init => {
374 const ty_pl = inst_datas[inst].ty_pl;374 const ty_pl = inst_datas[inst].ty_pl;
375 const vector_ty = a.air.getRefType(ty_pl.ty);375 const vector_ty = a.air.getRefType(ty_pl.ty);
376 const len = vector_ty.vectorLen();376 const len = vector_ty.arrayLen();
377 const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]);377 const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]);
378378
379 if (elements.len <= bpi - 1) {379 if (elements.len <= bpi - 1) {
src/Sema.zig+186-53
...@@ -2628,6 +2628,7 @@ fn validateUnionInit(...@@ -2628,6 +2628,7 @@ fn validateUnionInit(
2628 // Otherwise, the bitcast should be preserved and a store instruction should be2628 // Otherwise, the bitcast should be preserved and a store instruction should be
2629 // emitted to store the constant union value through the bitcast.2629 // emitted to store the constant union value through the bitcast.
2630 },2630 },
2631 .alloc => {},
2631 else => |t| {2632 else => |t| {
2632 if (std.debug.runtime_safety) {2633 if (std.debug.runtime_safety) {
2633 std.debug.panic("unexpected AIR tag for union pointer: {s}", .{@tagName(t)});2634 std.debug.panic("unexpected AIR tag for union pointer: {s}", .{@tagName(t)});
...@@ -10694,12 +10695,77 @@ fn zirArrayInit(...@@ -10694,12 +10695,77 @@ fn zirArrayInit(
10694 }10695 }
10695}10696}
1069610697
10697fn zirArrayInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {10698fn zirArrayInitAnon(
10699 sema: *Sema,
10700 block: *Block,
10701 inst: Zir.Inst.Index,
10702 is_ref: bool,
10703) CompileError!Air.Inst.Ref {
10698 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;10704 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
10699 const src = inst_data.src();10705 const src = inst_data.src();
10706 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
10707 const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
10708
10709 const types = try sema.arena.alloc(Type, operands.len);
10710 const values = try sema.arena.alloc(Value, operands.len);
10711
10712 const opt_runtime_src = rs: {
10713 var runtime_src: ?LazySrcLoc = null;
10714 for (operands) |operand, i| {
10715 const elem = sema.resolveInst(operand);
10716 types[i] = sema.typeOf(elem);
10717 const operand_src = src; // TODO better source location
10718 if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| {
10719 values[i] = val;
10720 } else {
10721 values[i] = Value.initTag(.unreachable_value);
10722 runtime_src = operand_src;
10723 }
10724 }
10725 break :rs runtime_src;
10726 };
1070010727
10701 _ = is_ref;10728 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
10702 return sema.fail(block, src, "TODO: Sema.zirArrayInitAnon", .{});10729 .types = types,
10730 .values = values,
10731 });
10732
10733 const runtime_src = opt_runtime_src orelse {
10734 const tuple_val = try Value.Tag.@"struct".create(sema.arena, values);
10735 if (!is_ref) return sema.addConstant(tuple_ty, tuple_val);
10736
10737 var anon_decl = try block.startAnonDecl();
10738 defer anon_decl.deinit();
10739 const decl = try anon_decl.finish(
10740 try tuple_ty.copy(anon_decl.arena()),
10741 try tuple_val.copy(anon_decl.arena()),
10742 );
10743 return sema.analyzeDeclRef(decl);
10744 };
10745
10746 if (is_ref) {
10747 const alloc = try block.addTy(.alloc, tuple_ty);
10748 for (operands) |operand, i_usize| {
10749 const i = @intCast(u32, i_usize);
10750 const field_ptr_ty = try Type.ptr(sema.arena, .{
10751 .mutable = true,
10752 .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
10753 .pointee_type = types[i],
10754 });
10755 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
10756 _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand));
10757 }
10758
10759 return alloc;
10760 }
10761
10762 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
10763 for (operands) |operand, i| {
10764 element_refs[i] = sema.resolveInst(operand);
10765 }
10766
10767 try sema.requireRuntimeBlock(block, runtime_src);
10768 return block.addVectorInit(tuple_ty, element_refs);
10703}10769}
1070410770
10705fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {10771fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -13540,10 +13606,50 @@ fn elemVal(...@@ -13540,10 +13606,50 @@ fn elemVal(
13540 // TODO: If the index is a vector, the result should be a vector.13606 // TODO: If the index is a vector, the result should be a vector.
13541 return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);13607 return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
13542 },13608 },
13609 .Struct => {
13610 // Tuple field access.
13611 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index);
13612 const index = @intCast(u32, index_val.toUnsignedInt());
13613 return tupleField(sema, block, array, index, array_src, elem_index_src);
13614 },
13543 else => unreachable,13615 else => unreachable,
13544 }13616 }
13545}13617}
1354613618
13619fn tupleField(
13620 sema: *Sema,
13621 block: *Block,
13622 tuple: Air.Inst.Ref,
13623 field_index: u32,
13624 tuple_src: LazySrcLoc,
13625 field_index_src: LazySrcLoc,
13626) CompileError!Air.Inst.Ref {
13627 const tuple_ty = sema.typeOf(tuple);
13628 const tuple_info = tuple_ty.castTag(.tuple).?.data;
13629
13630 if (field_index > tuple_info.types.len) {
13631 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
13632 field_index, tuple_info.types.len,
13633 });
13634 }
13635
13636 const field_ty = tuple_info.types[field_index];
13637 const field_val = tuple_info.values[field_index];
13638
13639 if (field_val.tag() != .unreachable_value) {
13640 return sema.addConstant(field_ty, field_val); // comptime field
13641 }
13642
13643 if (try sema.resolveMaybeUndefVal(block, tuple_src, tuple)) |tuple_val| {
13644 if (tuple_val.isUndef()) return sema.addConstUndef(field_ty);
13645 const field_values = tuple_val.castTag(.@"struct").?.data;
13646 return sema.addConstant(field_ty, field_values[field_index]);
13647 }
13648
13649 try sema.requireRuntimeBlock(block, tuple_src);
13650 return block.addStructFieldVal(tuple, field_index, field_ty);
13651}
13652
13547fn elemValArray(13653fn elemValArray(
13548 sema: *Sema,13654 sema: *Sema,
13549 block: *Block,13655 block: *Block,
...@@ -13901,17 +14007,19 @@ fn coerce(...@@ -13901,17 +14007,19 @@ fn coerce(
13901 else => {},14007 else => {},
13902 },14008 },
13903 .Array => switch (inst_ty.zigTypeTag()) {14009 .Array => switch (inst_ty.zigTypeTag()) {
13904 .Vector => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),14010 .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
13905 .Struct => {14011 .Struct => {
13906 if (inst == .empty_struct) {14012 if (inst == .empty_struct) {
13907 return arrayInitEmpty(sema, dest_ty);14013 return arrayInitEmpty(sema, dest_ty);
13908 }14014 }
14015 if (inst_ty.tag() == .tuple) {
14016 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
14017 }
13909 },14018 },
13910 else => {},14019 else => {},
13911 },14020 },
13912 .Vector => switch (inst_ty.zigTypeTag()) {14021 .Vector => switch (inst_ty.zigTypeTag()) {
13913 .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),14022 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
13914 .Vector => return sema.coerceVectors(block, dest_ty, dest_ty_src, inst, inst_src),
13915 else => {},14023 else => {},
13916 },14024 },
13917 .Struct => {14025 .Struct => {
...@@ -14847,10 +14955,8 @@ fn coerceEnumToUnion(...@@ -14847,10 +14955,8 @@ fn coerceEnumToUnion(
14847 return sema.failWithOwnedErrorMsg(msg);14955 return sema.failWithOwnedErrorMsg(msg);
14848}14956}
1484914957
14850/// Coerces vectors/arrays which have the same in-memory layout. This can be used for14958/// If the lengths match, coerces element-wise.
14851/// both coercing from and to vectors.14959fn coerceArrayLike(
14852/// TODO (affects the lang spec) delete this in favor of always using `coerceVectors`.
14853fn coerceVectorInMemory(
14854 sema: *Sema,14960 sema: *Sema,
14855 block: *Block,14961 block: *Block,
14856 dest_ty: Type,14962 dest_ty: Type,
...@@ -14860,7 +14966,7 @@ fn coerceVectorInMemory(...@@ -14860,7 +14966,7 @@ fn coerceVectorInMemory(
14860) !Air.Inst.Ref {14966) !Air.Inst.Ref {
14861 const inst_ty = sema.typeOf(inst);14967 const inst_ty = sema.typeOf(inst);
14862 const inst_len = inst_ty.arrayLen();14968 const inst_len = inst_ty.arrayLen();
14863 const dest_len = dest_ty.arrayLen();14969 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());
1486414970
14865 if (dest_len != inst_len) {14971 if (dest_len != inst_len) {
14866 const msg = msg: {14972 const msg = msg: {
...@@ -14879,22 +14985,50 @@ fn coerceVectorInMemory(...@@ -14879,22 +14985,50 @@ fn coerceVectorInMemory(
14879 const dest_elem_ty = dest_ty.childType();14985 const dest_elem_ty = dest_ty.childType();
14880 const inst_elem_ty = inst_ty.childType();14986 const inst_elem_ty = inst_ty.childType();
14881 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);14987 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
14882 if (in_memory_result != .ok) {14988 if (in_memory_result == .ok) {
14883 // TODO recursive error notes for coerceInMemoryAllowed failure14989 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
14884 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });14990 // These types share the same comptime value representation.
14991 return sema.addConstant(dest_ty, inst_val);
14992 }
14993 try sema.requireRuntimeBlock(block, inst_src);
14994 return block.addBitCast(dest_ty, inst);
14885 }14995 }
1488614996
14887 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {14997 const element_vals = try sema.arena.alloc(Value, dest_len);
14888 // These types share the same comptime value representation.14998 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
14889 return sema.addConstant(dest_ty, inst_val);14999 var runtime_src: ?LazySrcLoc = null;
15000
15001 for (element_vals) |*elem, i| {
15002 const index_ref = try sema.addConstant(
15003 Type.usize,
15004 try Value.Tag.int_u64.create(sema.arena, i),
15005 );
15006 const elem_src = inst_src; // TODO better source location
15007 const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
15008 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
15009 element_refs[i] = coerced;
15010 if (runtime_src == null) {
15011 if (try sema.resolveMaybeUndefVal(block, elem_src, coerced)) |elem_val| {
15012 elem.* = elem_val;
15013 } else {
15014 runtime_src = elem_src;
15015 }
15016 }
14890 }15017 }
1489115018
14892 try sema.requireRuntimeBlock(block, inst_src);15019 if (runtime_src) |rs| {
14893 return block.addBitCast(dest_ty, inst);15020 try sema.requireRuntimeBlock(block, rs);
15021 return block.addVectorInit(dest_ty, element_refs);
15022 }
15023
15024 return sema.addConstant(
15025 dest_ty,
15026 try Value.Tag.array.create(sema.arena, element_vals),
15027 );
14894}15028}
1489515029
14896/// If the lengths match, coerces element-wise.15030/// If the lengths match, coerces element-wise.
14897fn coerceVectors(15031fn coerceTupleToArray(
14898 sema: *Sema,15032 sema: *Sema,
14899 block: *Block,15033 block: *Block,
14900 dest_ty: Type,15034 dest_ty: Type,
...@@ -14904,7 +15038,7 @@ fn coerceVectors(...@@ -14904,7 +15038,7 @@ fn coerceVectors(
14904) !Air.Inst.Ref {15038) !Air.Inst.Ref {
14905 const inst_ty = sema.typeOf(inst);15039 const inst_ty = sema.typeOf(inst);
14906 const inst_len = inst_ty.arrayLen();15040 const inst_len = inst_ty.arrayLen();
14907 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());15041 const dest_len = dest_ty.arrayLen();
1490815042
14909 if (dest_len != inst_len) {15043 if (dest_len != inst_len) {
14910 const msg = msg: {15044 const msg = msg: {
...@@ -14919,30 +15053,15 @@ fn coerceVectors(...@@ -14919,30 +15053,15 @@ fn coerceVectors(
14919 return sema.failWithOwnedErrorMsg(msg);15053 return sema.failWithOwnedErrorMsg(msg);
14920 }15054 }
1492115055
14922 const target = sema.mod.getTarget();
14923 const dest_elem_ty = dest_ty.childType();
14924 const inst_elem_ty = inst_ty.childType();
14925 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
14926 if (in_memory_result == .ok) {
14927 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
14928 // These types share the same comptime value representation.
14929 return sema.addConstant(dest_ty, inst_val);
14930 }
14931 try sema.requireRuntimeBlock(block, inst_src);
14932 return block.addBitCast(dest_ty, inst);
14933 }
14934
14935 const element_vals = try sema.arena.alloc(Value, dest_len);15056 const element_vals = try sema.arena.alloc(Value, dest_len);
14936 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);15057 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
14937 var runtime_src: ?LazySrcLoc = null;15058 const dest_elem_ty = dest_ty.childType();
1493815059
14939 for (element_vals) |*elem, i| {15060 var runtime_src: ?LazySrcLoc = null;
14940 const index_ref = try sema.addConstant(15061 for (element_vals) |*elem, i_usize| {
14941 Type.usize,15062 const i = @intCast(u32, i_usize);
14942 try Value.Tag.int_u64.create(sema.arena, i),
14943 );
14944 const elem_src = inst_src; // TODO better source location15063 const elem_src = inst_src; // TODO better source location
14945 const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);15064 const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src);
14946 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);15065 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
14947 element_refs[i] = coerced;15066 element_refs[i] = coerced;
14948 if (runtime_src == null) {15067 if (runtime_src == null) {
...@@ -15833,19 +15952,22 @@ fn resolveStructLayout(...@@ -15833,19 +15952,22 @@ fn resolveStructLayout(
15833 ty: Type,15952 ty: Type,
15834) CompileError!void {15953) CompileError!void {
15835 const resolved_ty = try sema.resolveTypeFields(block, src, ty);15954 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
15836 const struct_obj = resolved_ty.castTag(.@"struct").?.data;15955 if (resolved_ty.castTag(.@"struct")) |payload| {
15837 switch (struct_obj.status) {15956 const struct_obj = payload.data;
15838 .none, .have_field_types => {},15957 switch (struct_obj.status) {
15839 .field_types_wip, .layout_wip => {15958 .none, .have_field_types => {},
15840 return sema.fail(block, src, "struct {} depends on itself", .{ty});15959 .field_types_wip, .layout_wip => {
15841 },15960 return sema.fail(block, src, "struct {} depends on itself", .{ty});
15842 .have_layout, .fully_resolved_wip, .fully_resolved => return,15961 },
15843 }15962 .have_layout, .fully_resolved_wip, .fully_resolved => return,
15844 struct_obj.status = .layout_wip;15963 }
15845 for (struct_obj.fields.values()) |field| {15964 struct_obj.status = .layout_wip;
15846 try sema.resolveTypeLayout(block, src, field.ty);15965 for (struct_obj.fields.values()) |field| {
15966 try sema.resolveTypeLayout(block, src, field.ty);
15967 }
15968 struct_obj.status = .have_layout;
15847 }15969 }
15848 struct_obj.status = .have_layout;15970 // otherwise it's a tuple; no need to resolve anything
15849}15971}
1585015972
15851fn resolveUnionLayout(15973fn resolveUnionLayout(
...@@ -16642,6 +16764,17 @@ pub fn typeHasOnePossibleValue(...@@ -16642,6 +16764,17 @@ pub fn typeHasOnePossibleValue(
16642 }16764 }
16643 return Value.initTag(.empty_struct_value);16765 return Value.initTag(.empty_struct_value);
16644 },16766 },
16767
16768 .tuple => {
16769 const tuple = ty.castTag(.tuple).?.data;
16770 for (tuple.values) |val| {
16771 if (val.tag() == .unreachable_value) {
16772 return null; // non-comptime field
16773 }
16774 }
16775 return Value.initTag(.empty_struct_value);
16776 },
16777
16645 .enum_numbered => {16778 .enum_numbered => {
16646 const resolved_ty = try sema.resolveTypeFields(block, src, ty);16779 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
16647 const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;16780 const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
src/codegen/llvm.zig+164-14
...@@ -916,6 +916,31 @@ pub const DeclGen = struct {...@@ -916,6 +916,31 @@ pub const DeclGen = struct {
916 // reference, we need to copy it here.916 // reference, we need to copy it here.
917 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());917 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
918918
919 if (t.castTag(.tuple)) |tuple| {
920 const llvm_struct_ty = dg.context.structCreateNamed("");
921 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
922
923 const types = tuple.data.types;
924 const values = tuple.data.values;
925 var llvm_field_types = try std.ArrayListUnmanaged(*const llvm.Type).initCapacity(gpa, types.len);
926 defer llvm_field_types.deinit(gpa);
927
928 for (types) |field_ty, i| {
929 const field_val = values[i];
930 if (field_val.tag() != .unreachable_value) continue;
931
932 llvm_field_types.appendAssumeCapacity(try dg.llvmType(field_ty));
933 }
934
935 llvm_struct_ty.structSetBody(
936 llvm_field_types.items.ptr,
937 @intCast(c_uint, llvm_field_types.items.len),
938 .False,
939 );
940
941 return llvm_struct_ty;
942 }
943
919 const struct_obj = t.castTag(.@"struct").?.data;944 const struct_obj = t.castTag(.@"struct").?.data;
920945
921 const name = try struct_obj.getFullyQualifiedName(gpa);946 const name = try struct_obj.getFullyQualifiedName(gpa);
...@@ -2687,10 +2712,23 @@ pub const FuncGen = struct {...@@ -2687,10 +2712,23 @@ pub const FuncGen = struct {
2687 if (!field_ty.hasCodeGenBits()) {2712 if (!field_ty.hasCodeGenBits()) {
2688 return null;2713 return null;
2689 }2714 }
2715 const target = self.dg.module.getTarget();
26902716
2691 assert(isByRef(struct_ty));2717 if (!isByRef(struct_ty)) {
2718 assert(!isByRef(field_ty));
2719 switch (struct_ty.zigTypeTag()) {
2720 .Struct => {
2721 var ptr_ty_buf: Type.Payload.Pointer = undefined;
2722 const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?;
2723 return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, "");
2724 },
2725 .Union => {
2726 return self.todo("airStructFieldVal byval union", .{});
2727 },
2728 else => unreachable,
2729 }
2730 }
26922731
2693 const target = self.dg.module.getTarget();
2694 switch (struct_ty.zigTypeTag()) {2732 switch (struct_ty.zigTypeTag()) {
2695 .Struct => {2733 .Struct => {
2696 var ptr_ty_buf: Type.Payload.Pointer = undefined;2734 var ptr_ty_buf: Type.Payload.Pointer = undefined;
...@@ -4370,19 +4408,85 @@ pub const FuncGen = struct {...@@ -4370,19 +4408,85 @@ pub const FuncGen = struct {
4370 if (self.liveness.isUnused(inst)) return null;4408 if (self.liveness.isUnused(inst)) return null;
43714409
4372 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;4410 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4373 const vector_ty = self.air.typeOfIndex(inst);4411 const result_ty = self.air.typeOfIndex(inst);
4374 const len = vector_ty.arrayLen();4412 const len = @intCast(usize, result_ty.arrayLen());
4375 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);4413 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
4376 const llvm_vector_ty = try self.dg.llvmType(vector_ty);4414 const llvm_result_ty = try self.dg.llvmType(result_ty);
4377 const llvm_u32 = self.context.intType(32);
43784415
4379 var vector = llvm_vector_ty.getUndef();4416 switch (result_ty.zigTypeTag()) {
4380 for (elements) |elem, i| {4417 .Vector => {
4381 const index_u32 = llvm_u32.constInt(i, .False);4418 const llvm_u32 = self.context.intType(32);
4382 const llvm_elem = try self.resolveInst(elem);4419
4383 vector = self.builder.buildInsertElement(vector, llvm_elem, index_u32, "");4420 var vector = llvm_result_ty.getUndef();
4421 for (elements) |elem, i| {
4422 const index_u32 = llvm_u32.constInt(i, .False);
4423 const llvm_elem = try self.resolveInst(elem);
4424 vector = self.builder.buildInsertElement(vector, llvm_elem, index_u32, "");
4425 }
4426 return vector;
4427 },
4428 .Struct => {
4429 const tuple = result_ty.castTag(.tuple).?.data;
4430
4431 if (isByRef(result_ty)) {
4432 const llvm_u32 = self.context.intType(32);
4433 const alloca_inst = self.buildAlloca(llvm_result_ty);
4434 const target = self.dg.module.getTarget();
4435 alloca_inst.setAlignment(result_ty.abiAlignment(target));
4436
4437 var indices: [2]*const llvm.Value = .{ llvm_u32.constNull(), undefined };
4438 var llvm_i: u32 = 0;
4439
4440 for (elements) |elem, i| {
4441 if (tuple.values[i].tag() != .unreachable_value) continue;
4442 const field_ty = tuple.types[i];
4443 const llvm_elem = try self.resolveInst(elem);
4444 indices[1] = llvm_u32.constInt(llvm_i, .False);
4445 llvm_i += 1;
4446 const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
4447 const store_inst = self.builder.buildStore(llvm_elem, field_ptr);
4448 store_inst.setAlignment(field_ty.abiAlignment(target));
4449 }
4450
4451 return alloca_inst;
4452 } else {
4453 var result = llvm_result_ty.getUndef();
4454 var llvm_i: u32 = 0;
4455 for (elements) |elem, i| {
4456 if (tuple.values[i].tag() != .unreachable_value) continue;
4457
4458 const llvm_elem = try self.resolveInst(elem);
4459 result = self.builder.buildInsertValue(result, llvm_elem, llvm_i, "");
4460 llvm_i += 1;
4461 }
4462 return result;
4463 }
4464 },
4465 .Array => {
4466 assert(isByRef(result_ty));
4467
4468 const llvm_usize = try self.dg.llvmType(Type.usize);
4469 const target = self.dg.module.getTarget();
4470 const alloca_inst = self.buildAlloca(llvm_result_ty);
4471 alloca_inst.setAlignment(result_ty.abiAlignment(target));
4472
4473 const elem_ty = result_ty.childType();
4474
4475 for (elements) |elem, i| {
4476 const indices: [2]*const llvm.Value = .{
4477 llvm_usize.constNull(),
4478 llvm_usize.constInt(@intCast(c_uint, i), .False),
4479 };
4480 const elem_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
4481 const llvm_elem = try self.resolveInst(elem);
4482 const store_inst = self.builder.buildStore(llvm_elem, elem_ptr);
4483 store_inst.setAlignment(elem_ty.abiAlignment(target));
4484 }
4485
4486 return alloca_inst;
4487 },
4488 else => unreachable,
4384 }4489 }
4385 return vector;
4386 }4490 }
43874491
4388 fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {4492 fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -4956,6 +5060,29 @@ fn llvmFieldIndex(...@@ -4956,6 +5060,29 @@ fn llvmFieldIndex(
4956 target: std.Target,5060 target: std.Target,
4957 ptr_pl_buf: *Type.Payload.Pointer,5061 ptr_pl_buf: *Type.Payload.Pointer,
4958) ?c_uint {5062) ?c_uint {
5063 if (ty.castTag(.tuple)) |payload| {
5064 const values = payload.data.values;
5065 var llvm_field_index: c_uint = 0;
5066 for (values) |val, i| {
5067 if (val.tag() != .unreachable_value) {
5068 continue;
5069 }
5070 if (field_index > i) {
5071 llvm_field_index += 1;
5072 continue;
5073 }
5074 const field_ty = payload.data.types[i];
5075 ptr_pl_buf.* = .{
5076 .data = .{
5077 .pointee_type = field_ty,
5078 .@"align" = field_ty.abiAlignment(target),
5079 .@"addrspace" = .generic,
5080 },
5081 };
5082 return llvm_field_index;
5083 }
5084 return null;
5085 }
4959 const struct_obj = ty.castTag(.@"struct").?.data;5086 const struct_obj = ty.castTag(.@"struct").?.data;
4960 if (struct_obj.layout != .Packed) {5087 if (struct_obj.layout != .Packed) {
4961 var llvm_field_index: c_uint = 0;5088 var llvm_field_index: c_uint = 0;
...@@ -4976,7 +5103,7 @@ fn llvmFieldIndex(...@@ -4976,7 +5103,7 @@ fn llvmFieldIndex(
4976 };5103 };
4977 return llvm_field_index;5104 return llvm_field_index;
4978 } else {5105 } else {
4979 // We did not find an llvm field that corrispons to this zig field.5106 // We did not find an llvm field that corresponds to this zig field.
4980 return null;5107 return null;
4981 }5108 }
4982 }5109 }
...@@ -5072,6 +5199,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -5072,6 +5199,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
5072}5199}
50735200
5074fn isByRef(ty: Type) bool {5201fn isByRef(ty: Type) bool {
5202 // For tuples (and TODO structs), if there are more than this many non-void
5203 // fields, then we make it byref, otherwise byval.
5204 const max_fields_byval = 2;
5205
5075 switch (ty.zigTypeTag()) {5206 switch (ty.zigTypeTag()) {
5076 .Type,5207 .Type,
5077 .ComptimeInt,5208 .ComptimeInt,
...@@ -5096,7 +5227,26 @@ fn isByRef(ty: Type) bool {...@@ -5096,7 +5227,26 @@ fn isByRef(ty: Type) bool {
5096 .AnyFrame,5227 .AnyFrame,
5097 => return false,5228 => return false,
50985229
5099 .Array, .Struct, .Frame => return ty.hasCodeGenBits(),5230 .Array, .Frame => return ty.hasCodeGenBits(),
5231 .Struct => {
5232 if (!ty.hasCodeGenBits()) return false;
5233 if (ty.castTag(.tuple)) |tuple| {
5234 var count: usize = 0;
5235 for (tuple.data.values) |field_val, i| {
5236 if (field_val.tag() != .unreachable_value) continue;
5237 count += 1;
5238 if (count > max_fields_byval) {
5239 return true;
5240 }
5241 const field_ty = tuple.data.types[i];
5242 if (isByRef(field_ty)) {
5243 return true;
5244 }
5245 }
5246 return false;
5247 }
5248 return true;
5249 },
5100 .Union => return ty.hasCodeGenBits(),5250 .Union => return ty.hasCodeGenBits(),
5101 .ErrorUnion => return isByRef(ty.errorUnionPayload()),5251 .ErrorUnion => return isByRef(ty.errorUnionPayload()),
5102 .Optional => {5252 .Optional => {
src/print_air.zig+1-1
...@@ -296,7 +296,7 @@ const Writer = struct {...@@ -296,7 +296,7 @@ const Writer = struct {
296 fn writeVectorInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {296 fn writeVectorInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
297 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;297 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
298 const vector_ty = w.air.getRefType(ty_pl.ty);298 const vector_ty = w.air.getRefType(ty_pl.ty);
299 const len = vector_ty.vectorLen();299 const len = vector_ty.arrayLen();
300 const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]);300 const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]);
301301
302 try s.print("{}, [", .{vector_ty});302 try s.print("{}, [", .{vector_ty});
src/print_zir.zig+2-1
...@@ -1963,7 +1963,8 @@ const Writer = struct {...@@ -1963,7 +1963,8 @@ const Writer = struct {
1963 if (i != 0) try stream.writeAll(", ");1963 if (i != 0) try stream.writeAll(", ");
1964 try self.writeInstRef(stream, arg);1964 try self.writeInstRef(stream, arg);
1965 }1965 }
1966 try stream.writeAll("})");1966 try stream.writeAll("}) ");
1967 try self.writeSrc(stream, inst_data.src());
1967 }1968 }
19681969
1969 fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1970 fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
src/type.zig+172-11
...@@ -128,6 +128,7 @@ pub const Type = extern union {...@@ -128,6 +128,7 @@ pub const Type = extern union {
128 .prefetch_options,128 .prefetch_options,
129 .export_options,129 .export_options,
130 .extern_options,130 .extern_options,
131 .tuple,
131 => return .Struct,132 => return .Struct,
132133
133 .enum_full,134 .enum_full,
...@@ -604,6 +605,24 @@ pub const Type = extern union {...@@ -604,6 +605,24 @@ pub const Type = extern union {
604 return a_payload.data == b_payload.data;605 return a_payload.data == b_payload.data;
605 }606 }
606 }607 }
608 if (a.castTag(.tuple)) |a_payload| {
609 if (b.castTag(.tuple)) |b_payload| {
610 if (a_payload.data.types.len != b_payload.data.types.len) return false;
611
612 for (a_payload.data.types) |a_ty, i| {
613 const b_ty = b_payload.data.types[i];
614 if (!eql(a_ty, b_ty)) return false;
615 }
616
617 for (a_payload.data.values) |a_val, i| {
618 const ty = a_payload.data.types[i];
619 const b_val = b_payload.data.values[i];
620 if (!Value.eql(a_val, b_val, ty)) return false;
621 }
622
623 return true;
624 }
625 }
607 return a.tag() == b.tag();626 return a.tag() == b.tag();
608 },627 },
609 .Enum => {628 .Enum => {
...@@ -891,6 +910,21 @@ pub const Type = extern union {...@@ -891,6 +910,21 @@ pub const Type = extern union {
891 .elem_type = try payload.elem_type.copy(allocator),910 .elem_type = try payload.elem_type.copy(allocator),
892 });911 });
893 },912 },
913 .tuple => {
914 const payload = self.castTag(.tuple).?.data;
915 const types = try allocator.alloc(Type, payload.types.len);
916 const values = try allocator.alloc(Value, payload.values.len);
917 for (payload.types) |ty, i| {
918 types[i] = try ty.copy(allocator);
919 }
920 for (payload.values) |val, i| {
921 values[i] = try val.copy(allocator);
922 }
923 return Tag.tuple.create(allocator, .{
924 .types = types,
925 .values = values,
926 });
927 },
894 .function => {928 .function => {
895 const payload = self.castTag(.function).?.data;929 const payload = self.castTag(.function).?.data;
896 const param_types = try allocator.alloc(Type, payload.param_types.len);930 const param_types = try allocator.alloc(Type, payload.param_types.len);
...@@ -1119,6 +1153,24 @@ pub const Type = extern union {...@@ -1119,6 +1153,24 @@ pub const Type = extern union {
1119 ty = payload.elem_type;1153 ty = payload.elem_type;
1120 continue;1154 continue;
1121 },1155 },
1156 .tuple => {
1157 const tuple = ty.castTag(.tuple).?.data;
1158 try writer.writeAll("tuple{");
1159 for (tuple.types) |field_ty, i| {
1160 if (i != 0) try writer.writeAll(", ");
1161 const val = tuple.values[i];
1162 if (val.tag() != .unreachable_value) {
1163 try writer.writeAll("comptime ");
1164 }
1165 try field_ty.format("", .{}, writer);
1166 if (val.tag() != .unreachable_value) {
1167 try writer.writeAll(" = ");
1168 try val.format("", .{}, writer);
1169 }
1170 }
1171 try writer.writeAll("}");
1172 return;
1173 },
1122 .single_const_pointer => {1174 .single_const_pointer => {
1123 const pointee_type = ty.castTag(.single_const_pointer).?.data;1175 const pointee_type = ty.castTag(.single_const_pointer).?.data;
1124 try writer.writeAll("*const ");1176 try writer.writeAll("*const ");
...@@ -1480,15 +1532,40 @@ pub const Type = extern union {...@@ -1480,15 +1532,40 @@ pub const Type = extern union {
1480 return requiresComptime(optionalChild(ty, &buf));1532 return requiresComptime(optionalChild(ty, &buf));
1481 },1533 },
14821534
1483 .error_union,1535 .tuple => {
1484 .anyframe_T,1536 const tuple = ty.castTag(.tuple).?.data;
1485 .@"struct",1537 for (tuple.types) |field_ty| {
1486 .@"union",1538 if (requiresComptime(field_ty)) {
1487 .union_tagged,1539 return true;
1488 .enum_numbered,1540 }
1489 .enum_full,1541 }
1490 .enum_nonexhaustive,1542 return false;
1491 => false, // TODO some of these should be `true` depending on their child types1543 },
1544
1545 .@"struct" => {
1546 const struct_obj = ty.castTag(.@"struct").?.data;
1547 for (struct_obj.fields.values()) |field| {
1548 if (requiresComptime(field.ty)) {
1549 return true;
1550 }
1551 }
1552 return false;
1553 },
1554
1555 .@"union", .union_tagged => {
1556 const union_obj = ty.cast(Payload.Union).?.data;
1557 for (union_obj.fields.values()) |field| {
1558 if (requiresComptime(field.ty)) {
1559 return true;
1560 }
1561 }
1562 return false;
1563 },
1564
1565 .error_union => return requiresComptime(errorUnionPayload(ty)),
1566 .anyframe_T => return ty.castTag(.anyframe_T).?.data.requiresComptime(),
1567 .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty.requiresComptime(),
1568 .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty.requiresComptime(),
1492 };1569 };
1493 }1570 }
14941571
...@@ -1697,6 +1774,16 @@ pub const Type = extern union {...@@ -1697,6 +1774,16 @@ pub const Type = extern union {
1697 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();1774 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
1698 },1775 },
16991776
1777 .tuple => {
1778 const tuple = self.castTag(.tuple).?.data;
1779 for (tuple.types) |ty, i| {
1780 const val = tuple.values[i];
1781 if (val.tag() != .unreachable_value) continue; // comptime field
1782 if (ty.hasCodeGenBits()) return true;
1783 }
1784 return false;
1785 },
1786
1700 .void,1787 .void,
1701 .type,1788 .type,
1702 .comptime_int,1789 .comptime_int,
...@@ -1968,6 +2055,21 @@ pub const Type = extern union {...@@ -1968,6 +2055,21 @@ pub const Type = extern union {
1968 }2055 }
1969 return big_align;2056 return big_align;
1970 },2057 },
2058
2059 .tuple => {
2060 const tuple = self.castTag(.tuple).?.data;
2061 var big_align: u32 = 0;
2062 for (tuple.types) |field_ty, i| {
2063 const val = tuple.values[i];
2064 if (val.tag() != .unreachable_value) continue; // comptime field
2065 if (!field_ty.hasCodeGenBits()) continue;
2066
2067 const field_align = field_ty.abiAlignment(target);
2068 big_align = @maximum(big_align, field_align);
2069 }
2070 return big_align;
2071 },
2072
1971 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {2073 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
1972 var buffer: Payload.Bits = undefined;2074 var buffer: Payload.Bits = undefined;
1973 const int_tag_ty = self.intTagType(&buffer);2075 const int_tag_ty = self.intTagType(&buffer);
...@@ -2037,13 +2139,14 @@ pub const Type = extern union {...@@ -2037,13 +2139,14 @@ pub const Type = extern union {
2037 .void,2139 .void,
2038 => 0,2140 => 0,
20392141
2040 .@"struct" => {2142 .@"struct", .tuple => {
2041 const field_count = self.structFieldCount();2143 const field_count = self.structFieldCount();
2042 if (field_count == 0) {2144 if (field_count == 0) {
2043 return 0;2145 return 0;
2044 }2146 }
2045 return self.structFieldOffset(field_count, target);2147 return self.structFieldOffset(field_count, target);
2046 },2148 },
2149
2047 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {2150 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
2048 var buffer: Payload.Bits = undefined;2151 var buffer: Payload.Bits = undefined;
2049 const int_tag_ty = self.intTagType(&buffer);2152 const int_tag_ty = self.intTagType(&buffer);
...@@ -2231,6 +2334,11 @@ pub const Type = extern union {...@@ -2231,6 +2334,11 @@ pub const Type = extern union {
2231 }2334 }
2232 return total;2335 return total;
2233 },2336 },
2337
2338 .tuple => {
2339 @panic("TODO bitSize tuples");
2340 },
2341
2234 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {2342 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
2235 var buffer: Payload.Bits = undefined;2343 var buffer: Payload.Bits = undefined;
2236 const int_tag_ty = ty.intTagType(&buffer);2344 const int_tag_ty = ty.intTagType(&buffer);
...@@ -2926,6 +3034,7 @@ pub const Type = extern union {...@@ -2926,6 +3034,7 @@ pub const Type = extern union {
29263034
2927 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {3035 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
2928 return switch (ty.tag()) {3036 return switch (ty.tag()) {
3037 .tuple => .Auto,
2929 .@"struct" => ty.castTag(.@"struct").?.data.layout,3038 .@"struct" => ty.castTag(.@"struct").?.data.layout,
2930 .@"union" => ty.castTag(.@"union").?.data.layout,3039 .@"union" => ty.castTag(.@"union").?.data.layout,
2931 .union_tagged => ty.castTag(.union_tagged).?.data.layout,3040 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
...@@ -2998,6 +3107,7 @@ pub const Type = extern union {...@@ -2998,6 +3107,7 @@ pub const Type = extern union {
2998 .array_sentinel => ty.castTag(.array_sentinel).?.data.len,3107 .array_sentinel => ty.castTag(.array_sentinel).?.data.len,
2999 .array_u8 => ty.castTag(.array_u8).?.data,3108 .array_u8 => ty.castTag(.array_u8).?.data,
3000 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,3109 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
3110 .tuple => ty.castTag(.tuple).?.data.types.len,
30013111
3002 else => unreachable,3112 else => unreachable,
3003 };3113 };
...@@ -3010,6 +3120,7 @@ pub const Type = extern union {...@@ -3010,6 +3120,7 @@ pub const Type = extern union {
3010 pub fn vectorLen(ty: Type) u32 {3120 pub fn vectorLen(ty: Type) u32 {
3011 return switch (ty.tag()) {3121 return switch (ty.tag()) {
3012 .vector => @intCast(u32, ty.castTag(.vector).?.data.len),3122 .vector => @intCast(u32, ty.castTag(.vector).?.data.len),
3123 .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len),
3013 else => unreachable,3124 else => unreachable,
3014 };3125 };
3015 }3126 }
...@@ -3463,6 +3574,17 @@ pub const Type = extern union {...@@ -3463,6 +3574,17 @@ pub const Type = extern union {
3463 }3574 }
3464 return Value.initTag(.empty_struct_value);3575 return Value.initTag(.empty_struct_value);
3465 },3576 },
3577
3578 .tuple => {
3579 const tuple = ty.castTag(.tuple).?.data;
3580 for (tuple.values) |val| {
3581 if (val.tag() == .unreachable_value) {
3582 return null; // non-comptime field
3583 }
3584 }
3585 return Value.initTag(.empty_struct_value);
3586 },
3587
3466 .enum_numbered => {3588 .enum_numbered => {
3467 const enum_numbered = ty.castTag(.enum_numbered).?.data;3589 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3468 if (enum_numbered.fields.count() == 1) {3590 if (enum_numbered.fields.count() == 1) {
...@@ -3539,7 +3661,8 @@ pub const Type = extern union {...@@ -3539,7 +3661,8 @@ pub const Type = extern union {
3539 .Slice, .Many, .C => true,3661 .Slice, .Many, .C => true,
3540 .One => ty.elemType().zigTypeTag() == .Array,3662 .One => ty.elemType().zigTypeTag() == .Array,
3541 },3663 },
3542 else => false, // TODO tuples are indexable3664 .Struct => ty.tag() == .tuple,
3665 else => false,
3543 };3666 };
3544 }3667 }
35453668
...@@ -3766,6 +3889,7 @@ pub const Type = extern union {...@@ -3766,6 +3889,7 @@ pub const Type = extern union {
3766 return struct_obj.fields.count();3889 return struct_obj.fields.count();
3767 },3890 },
3768 .empty_struct => return 0,3891 .empty_struct => return 0,
3892 .tuple => return ty.castTag(.tuple).?.data.types.len,
3769 else => unreachable,3893 else => unreachable,
3770 }3894 }
3771 }3895 }
...@@ -3781,6 +3905,7 @@ pub const Type = extern union {...@@ -3781,6 +3905,7 @@ pub const Type = extern union {
3781 const union_obj = ty.cast(Payload.Union).?.data;3905 const union_obj = ty.cast(Payload.Union).?.data;
3782 return union_obj.fields.values()[index].ty;3906 return union_obj.fields.values()[index].ty;
3783 },3907 },
3908 .tuple => return ty.castTag(.tuple).?.data.types[index],
3784 else => unreachable,3909 else => unreachable,
3785 }3910 }
3786 }3911 }
...@@ -3933,6 +4058,31 @@ pub const Type = extern union {...@@ -3933,6 +4058,31 @@ pub const Type = extern union {
3933 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);4058 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
3934 return it.offset;4059 return it.offset;
3935 },4060 },
4061
4062 .tuple => {
4063 const tuple = ty.castTag(.tuple).?.data;
4064
4065 var offset: u64 = 0;
4066 var big_align: u32 = 0;
4067
4068 for (tuple.types) |field_ty, i| {
4069 const field_val = tuple.values[i];
4070 if (field_val.tag() != .unreachable_value) {
4071 // comptime field
4072 if (i == index) return offset;
4073 continue;
4074 }
4075
4076 const field_align = field_ty.abiAlignment(target);
4077 big_align = @maximum(big_align, field_align);
4078 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
4079 if (i == index) return offset;
4080 offset += field_ty.abiSize(target);
4081 }
4082 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
4083 return offset;
4084 },
4085
3936 .@"union" => return 0,4086 .@"union" => return 0,
3937 .union_tagged => {4087 .union_tagged => {
3938 const union_obj = ty.castTag(.union_tagged).?.data;4088 const union_obj = ty.castTag(.union_tagged).?.data;
...@@ -4182,6 +4332,8 @@ pub const Type = extern union {...@@ -4182,6 +4332,8 @@ pub const Type = extern union {
4182 array,4332 array,
4183 array_sentinel,4333 array_sentinel,
4184 vector,4334 vector,
4335 /// Possible Value tags for this: @"struct"
4336 tuple,
4185 pointer,4337 pointer,
4186 single_const_pointer,4338 single_const_pointer,
4187 single_mut_pointer,4339 single_mut_pointer,
...@@ -4326,6 +4478,7 @@ pub const Type = extern union {...@@ -4326,6 +4478,7 @@ pub const Type = extern union {
4326 .enum_simple => Payload.EnumSimple,4478 .enum_simple => Payload.EnumSimple,
4327 .enum_numbered => Payload.EnumNumbered,4479 .enum_numbered => Payload.EnumNumbered,
4328 .empty_struct => Payload.ContainerScope,4480 .empty_struct => Payload.ContainerScope,
4481 .tuple => Payload.Tuple,
4329 };4482 };
4330 }4483 }
43314484
...@@ -4490,6 +4643,14 @@ pub const Type = extern union {...@@ -4490,6 +4643,14 @@ pub const Type = extern union {
4490 data: *Module.Struct,4643 data: *Module.Struct,
4491 };4644 };
44924645
4646 pub const Tuple = struct {
4647 base: Payload = .{ .tag = .tuple },
4648 data: struct {
4649 types: []Type,
4650 values: []Value,
4651 },
4652 };
4653
4493 pub const Union = struct {4654 pub const Union = struct {
4494 base: Payload,4655 base: Payload,
4495 data: *Module.Union,4656 data: *Module.Union,
test/behavior/array_llvm.zig+2-2
...@@ -237,8 +237,6 @@ test "zero-sized array with recursive type definition" {...@@ -237,8 +237,6 @@ test "zero-sized array with recursive type definition" {
237}237}
238238
239test "type coercion of anon struct literal to array" {239test "type coercion of anon struct literal to array" {
240 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
241
242 const S = struct {240 const S = struct {
243 const U = union {241 const U = union {
244 a: u32,242 a: u32,
...@@ -254,6 +252,8 @@ test "type coercion of anon struct literal to array" {...@@ -254,6 +252,8 @@ test "type coercion of anon struct literal to array" {
254 try expect(arr1[1] == 56);252 try expect(arr1[1] == 56);
255 try expect(arr1[2] == 54);253 try expect(arr1[2] == 54);
256254
255 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
256
257 var x2: U = .{ .a = 42 };257 var x2: U = .{ .a = 42 };
258 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };258 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
259 var arr2: [3]U = t2;259 var arr2: [3]U = t2;