authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-20 18:24:01-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-20 18:24:01-05:00
logc9ae24503dc8da2e59f46619695bf4eb863fb3ac
treed55084efed19c32fbccb0c96959940796a1d0c43
parentf763000dc918c2367ebc181645eb48db896205d8
parent1f823eecdd071f619c761a743119f1a2a89af1bf
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10649 from ziglang/stage2-tuples

stage2: implement tuples

10 files changed, 595 insertions(+), 98 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 = @intCast(usize, 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/Module.zig+4
...@@ -821,6 +821,8 @@ pub const ErrorSet = struct {...@@ -821,6 +821,8 @@ pub const ErrorSet = struct {
821 }821 }
822};822};
823823
824pub const RequiresComptime = enum { no, yes, unknown, wip };
825
824/// Represents the data that a struct declaration provides.826/// Represents the data that a struct declaration provides.
825pub const Struct = struct {827pub const Struct = struct {
826 /// The Decl that corresponds to the struct itself.828 /// The Decl that corresponds to the struct itself.
...@@ -849,6 +851,7 @@ pub const Struct = struct {...@@ -849,6 +851,7 @@ pub const Struct = struct {
849 /// If true, definitely nonzero size at runtime. If false, resolving the fields851 /// If true, definitely nonzero size at runtime. If false, resolving the fields
850 /// is necessary to determine whether it has bits at runtime.852 /// is necessary to determine whether it has bits at runtime.
851 known_has_bits: bool,853 known_has_bits: bool,
854 requires_comptime: RequiresComptime = .unknown,
852855
853 pub const Fields = std.StringArrayHashMapUnmanaged(Field);856 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
854857
...@@ -1038,6 +1041,7 @@ pub const Union = struct {...@@ -1038,6 +1041,7 @@ pub const Union = struct {
1038 // which `have_layout` does not ensure.1041 // which `have_layout` does not ensure.
1039 fully_resolved,1042 fully_resolved,
1040 },1043 },
1044 requires_comptime: RequiresComptime = .unknown,
10411045
1042 pub const Field = struct {1046 pub const Field = struct {
1043 /// undefined until `status` is `have_field_types` or `have_layout`.1047 /// undefined until `status` is `have_field_types` or `have_layout`.
src/Sema.zig+205-54
...@@ -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.isTuple()) {
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 => {
...@@ -14276,12 +14384,30 @@ fn storePtr2(...@@ -14276,12 +14384,30 @@ fn storePtr2(
14276 uncasted_operand: Air.Inst.Ref,14384 uncasted_operand: Air.Inst.Ref,
14277 operand_src: LazySrcLoc,14385 operand_src: LazySrcLoc,
14278 air_tag: Air.Inst.Tag,14386 air_tag: Air.Inst.Tag,
14279) !void {14387) CompileError!void {
14280 const ptr_ty = sema.typeOf(ptr);14388 const ptr_ty = sema.typeOf(ptr);
14281 if (ptr_ty.isConstPtr())14389 if (ptr_ty.isConstPtr())
14282 return sema.fail(block, src, "cannot assign to constant", .{});14390 return sema.fail(block, ptr_src, "cannot assign to constant", .{});
1428314391
14284 const elem_ty = ptr_ty.childType();14392 const elem_ty = ptr_ty.childType();
14393
14394 // To generate better code for tuples, we detect a tuple operand here, and
14395 // analyze field loads and stores directly. This avoids an extra allocation + memcpy
14396 // which would occur if we used `coerce`.
14397 const operand_ty = sema.typeOf(uncasted_operand);
14398 if (operand_ty.castTag(.tuple)) |payload| {
14399 const tuple_fields_len = payload.data.types.len;
14400 var i: u32 = 0;
14401 while (i < tuple_fields_len) : (i += 1) {
14402 const elem_src = operand_src; // TODO better source location
14403 const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src);
14404 const elem_index = try sema.addIntUnsigned(Type.usize, i);
14405 const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src);
14406 try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
14407 }
14408 return;
14409 }
14410
14285 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);14411 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
14286 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)14412 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
14287 return;14413 return;
...@@ -14847,10 +14973,8 @@ fn coerceEnumToUnion(...@@ -14847,10 +14973,8 @@ fn coerceEnumToUnion(
14847 return sema.failWithOwnedErrorMsg(msg);14973 return sema.failWithOwnedErrorMsg(msg);
14848}14974}
1484914975
14850/// Coerces vectors/arrays which have the same in-memory layout. This can be used for14976/// If the lengths match, coerces element-wise.
14851/// both coercing from and to vectors.14977fn coerceArrayLike(
14852/// TODO (affects the lang spec) delete this in favor of always using `coerceVectors`.
14853fn coerceVectorInMemory(
14854 sema: *Sema,14978 sema: *Sema,
14855 block: *Block,14979 block: *Block,
14856 dest_ty: Type,14980 dest_ty: Type,
...@@ -14860,7 +14984,7 @@ fn coerceVectorInMemory(...@@ -14860,7 +14984,7 @@ fn coerceVectorInMemory(
14860) !Air.Inst.Ref {14984) !Air.Inst.Ref {
14861 const inst_ty = sema.typeOf(inst);14985 const inst_ty = sema.typeOf(inst);
14862 const inst_len = inst_ty.arrayLen();14986 const inst_len = inst_ty.arrayLen();
14863 const dest_len = dest_ty.arrayLen();14987 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());
1486414988
14865 if (dest_len != inst_len) {14989 if (dest_len != inst_len) {
14866 const msg = msg: {14990 const msg = msg: {
...@@ -14879,22 +15003,50 @@ fn coerceVectorInMemory(...@@ -14879,22 +15003,50 @@ fn coerceVectorInMemory(
14879 const dest_elem_ty = dest_ty.childType();15003 const dest_elem_ty = dest_ty.childType();
14880 const inst_elem_ty = inst_ty.childType();15004 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);15005 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) {15006 if (in_memory_result == .ok) {
14883 // TODO recursive error notes for coerceInMemoryAllowed failure15007 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
14884 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });15008 // These types share the same comptime value representation.
15009 return sema.addConstant(dest_ty, inst_val);
15010 }
15011 try sema.requireRuntimeBlock(block, inst_src);
15012 return block.addBitCast(dest_ty, inst);
14885 }15013 }
1488615014
14887 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {15015 const element_vals = try sema.arena.alloc(Value, dest_len);
14888 // These types share the same comptime value representation.15016 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
14889 return sema.addConstant(dest_ty, inst_val);15017 var runtime_src: ?LazySrcLoc = null;
15018
15019 for (element_vals) |*elem, i| {
15020 const index_ref = try sema.addConstant(
15021 Type.usize,
15022 try Value.Tag.int_u64.create(sema.arena, i),
15023 );
15024 const elem_src = inst_src; // TODO better source location
15025 const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);
15026 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
15027 element_refs[i] = coerced;
15028 if (runtime_src == null) {
15029 if (try sema.resolveMaybeUndefVal(block, elem_src, coerced)) |elem_val| {
15030 elem.* = elem_val;
15031 } else {
15032 runtime_src = elem_src;
15033 }
15034 }
14890 }15035 }
1489115036
14892 try sema.requireRuntimeBlock(block, inst_src);15037 if (runtime_src) |rs| {
14893 return block.addBitCast(dest_ty, inst);15038 try sema.requireRuntimeBlock(block, rs);
15039 return block.addVectorInit(dest_ty, element_refs);
15040 }
15041
15042 return sema.addConstant(
15043 dest_ty,
15044 try Value.Tag.array.create(sema.arena, element_vals),
15045 );
14894}15046}
1489515047
14896/// If the lengths match, coerces element-wise.15048/// If the lengths match, coerces element-wise.
14897fn coerceVectors(15049fn coerceTupleToArray(
14898 sema: *Sema,15050 sema: *Sema,
14899 block: *Block,15051 block: *Block,
14900 dest_ty: Type,15052 dest_ty: Type,
...@@ -14919,30 +15071,15 @@ fn coerceVectors(...@@ -14919,30 +15071,15 @@ fn coerceVectors(
14919 return sema.failWithOwnedErrorMsg(msg);15071 return sema.failWithOwnedErrorMsg(msg);
14920 }15072 }
1492115073
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);15074 const element_vals = try sema.arena.alloc(Value, dest_len);
14936 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);15075 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
14937 var runtime_src: ?LazySrcLoc = null;15076 const dest_elem_ty = dest_ty.childType();
1493815077
14939 for (element_vals) |*elem, i| {15078 var runtime_src: ?LazySrcLoc = null;
14940 const index_ref = try sema.addConstant(15079 for (element_vals) |*elem, i_usize| {
14941 Type.usize,15080 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 location15081 const elem_src = inst_src; // TODO better source location
14945 const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src);15082 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);15083 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
14947 element_refs[i] = coerced;15084 element_refs[i] = coerced;
14948 if (runtime_src == null) {15085 if (runtime_src == null) {
...@@ -15833,19 +15970,22 @@ fn resolveStructLayout(...@@ -15833,19 +15970,22 @@ fn resolveStructLayout(
15833 ty: Type,15970 ty: Type,
15834) CompileError!void {15971) CompileError!void {
15835 const resolved_ty = try sema.resolveTypeFields(block, src, ty);15972 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
15836 const struct_obj = resolved_ty.castTag(.@"struct").?.data;15973 if (resolved_ty.castTag(.@"struct")) |payload| {
15837 switch (struct_obj.status) {15974 const struct_obj = payload.data;
15838 .none, .have_field_types => {},15975 switch (struct_obj.status) {
15839 .field_types_wip, .layout_wip => {15976 .none, .have_field_types => {},
15840 return sema.fail(block, src, "struct {} depends on itself", .{ty});15977 .field_types_wip, .layout_wip => {
15841 },15978 return sema.fail(block, src, "struct {} depends on itself", .{ty});
15842 .have_layout, .fully_resolved_wip, .fully_resolved => return,15979 },
15843 }15980 .have_layout, .fully_resolved_wip, .fully_resolved => return,
15844 struct_obj.status = .layout_wip;15981 }
15845 for (struct_obj.fields.values()) |field| {15982 struct_obj.status = .layout_wip;
15846 try sema.resolveTypeLayout(block, src, field.ty);15983 for (struct_obj.fields.values()) |field| {
15984 try sema.resolveTypeLayout(block, src, field.ty);
15985 }
15986 struct_obj.status = .have_layout;
15847 }15987 }
15848 struct_obj.status = .have_layout;15988 // otherwise it's a tuple; no need to resolve anything
15849}15989}
1585015990
15851fn resolveUnionLayout(15991fn resolveUnionLayout(
...@@ -16642,6 +16782,17 @@ pub fn typeHasOnePossibleValue(...@@ -16642,6 +16782,17 @@ pub fn typeHasOnePossibleValue(
16642 }16782 }
16643 return Value.initTag(.empty_struct_value);16783 return Value.initTag(.empty_struct_value);
16644 },16784 },
16785
16786 .tuple => {
16787 const tuple = ty.castTag(.tuple).?.data;
16788 for (tuple.values) |val| {
16789 if (val.tag() == .unreachable_value) {
16790 return null; // non-comptime field
16791 }
16792 }
16793 return Value.initTag(.empty_struct_value);
16794 },
16795
16645 .enum_numbered => {16796 .enum_numbered => {
16646 const resolved_ty = try sema.resolveTypeFields(block, src, ty);16797 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
16647 const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;16798 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 = @intCast(usize, 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+194-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,58 @@ pub const Type = extern union {...@@ -1480,15 +1532,58 @@ 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 switch (struct_obj.requires_comptime) {
1548 .no, .wip => return false,
1549 .yes => return true,
1550 .unknown => {
1551 struct_obj.requires_comptime = .wip;
1552 for (struct_obj.fields.values()) |field| {
1553 if (requiresComptime(field.ty)) {
1554 struct_obj.requires_comptime = .yes;
1555 return true;
1556 }
1557 }
1558 struct_obj.requires_comptime = .no;
1559 return false;
1560 },
1561 }
1562 },
1563
1564 .@"union", .union_tagged => {
1565 const union_obj = ty.cast(Payload.Union).?.data;
1566 switch (union_obj.requires_comptime) {
1567 .no, .wip => return false,
1568 .yes => return true,
1569 .unknown => {
1570 union_obj.requires_comptime = .wip;
1571 for (union_obj.fields.values()) |field| {
1572 if (requiresComptime(field.ty)) {
1573 union_obj.requires_comptime = .yes;
1574 return true;
1575 }
1576 }
1577 union_obj.requires_comptime = .no;
1578 return false;
1579 },
1580 }
1581 },
1582
1583 .error_union => return requiresComptime(errorUnionPayload(ty)),
1584 .anyframe_T => return ty.castTag(.anyframe_T).?.data.requiresComptime(),
1585 .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty.requiresComptime(),
1586 .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty.requiresComptime(),
1492 };1587 };
1493 }1588 }
14941589
...@@ -1697,6 +1792,16 @@ pub const Type = extern union {...@@ -1697,6 +1792,16 @@ pub const Type = extern union {
1697 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();1792 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
1698 },1793 },
16991794
1795 .tuple => {
1796 const tuple = self.castTag(.tuple).?.data;
1797 for (tuple.types) |ty, i| {
1798 const val = tuple.values[i];
1799 if (val.tag() != .unreachable_value) continue; // comptime field
1800 if (ty.hasCodeGenBits()) return true;
1801 }
1802 return false;
1803 },
1804
1700 .void,1805 .void,
1701 .type,1806 .type,
1702 .comptime_int,1807 .comptime_int,
...@@ -1968,6 +2073,21 @@ pub const Type = extern union {...@@ -1968,6 +2073,21 @@ pub const Type = extern union {
1968 }2073 }
1969 return big_align;2074 return big_align;
1970 },2075 },
2076
2077 .tuple => {
2078 const tuple = self.castTag(.tuple).?.data;
2079 var big_align: u32 = 0;
2080 for (tuple.types) |field_ty, i| {
2081 const val = tuple.values[i];
2082 if (val.tag() != .unreachable_value) continue; // comptime field
2083 if (!field_ty.hasCodeGenBits()) continue;
2084
2085 const field_align = field_ty.abiAlignment(target);
2086 big_align = @maximum(big_align, field_align);
2087 }
2088 return big_align;
2089 },
2090
1971 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {2091 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
1972 var buffer: Payload.Bits = undefined;2092 var buffer: Payload.Bits = undefined;
1973 const int_tag_ty = self.intTagType(&buffer);2093 const int_tag_ty = self.intTagType(&buffer);
...@@ -2037,13 +2157,14 @@ pub const Type = extern union {...@@ -2037,13 +2157,14 @@ pub const Type = extern union {
2037 .void,2157 .void,
2038 => 0,2158 => 0,
20392159
2040 .@"struct" => {2160 .@"struct", .tuple => {
2041 const field_count = self.structFieldCount();2161 const field_count = self.structFieldCount();
2042 if (field_count == 0) {2162 if (field_count == 0) {
2043 return 0;2163 return 0;
2044 }2164 }
2045 return self.structFieldOffset(field_count, target);2165 return self.structFieldOffset(field_count, target);
2046 },2166 },
2167
2047 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {2168 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
2048 var buffer: Payload.Bits = undefined;2169 var buffer: Payload.Bits = undefined;
2049 const int_tag_ty = self.intTagType(&buffer);2170 const int_tag_ty = self.intTagType(&buffer);
...@@ -2231,6 +2352,11 @@ pub const Type = extern union {...@@ -2231,6 +2352,11 @@ pub const Type = extern union {
2231 }2352 }
2232 return total;2353 return total;
2233 },2354 },
2355
2356 .tuple => {
2357 @panic("TODO bitSize tuples");
2358 },
2359
2234 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {2360 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
2235 var buffer: Payload.Bits = undefined;2361 var buffer: Payload.Bits = undefined;
2236 const int_tag_ty = ty.intTagType(&buffer);2362 const int_tag_ty = ty.intTagType(&buffer);
...@@ -2926,6 +3052,7 @@ pub const Type = extern union {...@@ -2926,6 +3052,7 @@ pub const Type = extern union {
29263052
2927 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {3053 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
2928 return switch (ty.tag()) {3054 return switch (ty.tag()) {
3055 .tuple => .Auto,
2929 .@"struct" => ty.castTag(.@"struct").?.data.layout,3056 .@"struct" => ty.castTag(.@"struct").?.data.layout,
2930 .@"union" => ty.castTag(.@"union").?.data.layout,3057 .@"union" => ty.castTag(.@"union").?.data.layout,
2931 .union_tagged => ty.castTag(.union_tagged).?.data.layout,3058 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
...@@ -2998,6 +3125,7 @@ pub const Type = extern union {...@@ -2998,6 +3125,7 @@ pub const Type = extern union {
2998 .array_sentinel => ty.castTag(.array_sentinel).?.data.len,3125 .array_sentinel => ty.castTag(.array_sentinel).?.data.len,
2999 .array_u8 => ty.castTag(.array_u8).?.data,3126 .array_u8 => ty.castTag(.array_u8).?.data,
3000 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,3127 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
3128 .tuple => ty.castTag(.tuple).?.data.types.len,
30013129
3002 else => unreachable,3130 else => unreachable,
3003 };3131 };
...@@ -3010,6 +3138,7 @@ pub const Type = extern union {...@@ -3010,6 +3138,7 @@ pub const Type = extern union {
3010 pub fn vectorLen(ty: Type) u32 {3138 pub fn vectorLen(ty: Type) u32 {
3011 return switch (ty.tag()) {3139 return switch (ty.tag()) {
3012 .vector => @intCast(u32, ty.castTag(.vector).?.data.len),3140 .vector => @intCast(u32, ty.castTag(.vector).?.data.len),
3141 .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len),
3013 else => unreachable,3142 else => unreachable,
3014 };3143 };
3015 }3144 }
...@@ -3463,6 +3592,17 @@ pub const Type = extern union {...@@ -3463,6 +3592,17 @@ pub const Type = extern union {
3463 }3592 }
3464 return Value.initTag(.empty_struct_value);3593 return Value.initTag(.empty_struct_value);
3465 },3594 },
3595
3596 .tuple => {
3597 const tuple = ty.castTag(.tuple).?.data;
3598 for (tuple.values) |val| {
3599 if (val.tag() == .unreachable_value) {
3600 return null; // non-comptime field
3601 }
3602 }
3603 return Value.initTag(.empty_struct_value);
3604 },
3605
3466 .enum_numbered => {3606 .enum_numbered => {
3467 const enum_numbered = ty.castTag(.enum_numbered).?.data;3607 const enum_numbered = ty.castTag(.enum_numbered).?.data;
3468 if (enum_numbered.fields.count() == 1) {3608 if (enum_numbered.fields.count() == 1) {
...@@ -3539,7 +3679,8 @@ pub const Type = extern union {...@@ -3539,7 +3679,8 @@ pub const Type = extern union {
3539 .Slice, .Many, .C => true,3679 .Slice, .Many, .C => true,
3540 .One => ty.elemType().zigTypeTag() == .Array,3680 .One => ty.elemType().zigTypeTag() == .Array,
3541 },3681 },
3542 else => false, // TODO tuples are indexable3682 .Struct => ty.isTuple(),
3683 else => false,
3543 };3684 };
3544 }3685 }
35453686
...@@ -3766,6 +3907,7 @@ pub const Type = extern union {...@@ -3766,6 +3907,7 @@ pub const Type = extern union {
3766 return struct_obj.fields.count();3907 return struct_obj.fields.count();
3767 },3908 },
3768 .empty_struct => return 0,3909 .empty_struct => return 0,
3910 .tuple => return ty.castTag(.tuple).?.data.types.len,
3769 else => unreachable,3911 else => unreachable,
3770 }3912 }
3771 }3913 }
...@@ -3781,6 +3923,7 @@ pub const Type = extern union {...@@ -3781,6 +3923,7 @@ pub const Type = extern union {
3781 const union_obj = ty.cast(Payload.Union).?.data;3923 const union_obj = ty.cast(Payload.Union).?.data;
3782 return union_obj.fields.values()[index].ty;3924 return union_obj.fields.values()[index].ty;
3783 },3925 },
3926 .tuple => return ty.castTag(.tuple).?.data.types[index],
3784 else => unreachable,3927 else => unreachable,
3785 }3928 }
3786 }3929 }
...@@ -3933,6 +4076,31 @@ pub const Type = extern union {...@@ -3933,6 +4076,31 @@ pub const Type = extern union {
3933 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);4076 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
3934 return it.offset;4077 return it.offset;
3935 },4078 },
4079
4080 .tuple => {
4081 const tuple = ty.castTag(.tuple).?.data;
4082
4083 var offset: u64 = 0;
4084 var big_align: u32 = 0;
4085
4086 for (tuple.types) |field_ty, i| {
4087 const field_val = tuple.values[i];
4088 if (field_val.tag() != .unreachable_value) {
4089 // comptime field
4090 if (i == index) return offset;
4091 continue;
4092 }
4093
4094 const field_align = field_ty.abiAlignment(target);
4095 big_align = @maximum(big_align, field_align);
4096 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
4097 if (i == index) return offset;
4098 offset += field_ty.abiSize(target);
4099 }
4100 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
4101 return offset;
4102 },
4103
3936 .@"union" => return 0,4104 .@"union" => return 0,
3937 .union_tagged => {4105 .union_tagged => {
3938 const union_obj = ty.castTag(.union_tagged).?.data;4106 const union_obj = ty.castTag(.union_tagged).?.data;
...@@ -4182,6 +4350,8 @@ pub const Type = extern union {...@@ -4182,6 +4350,8 @@ pub const Type = extern union {
4182 array,4350 array,
4183 array_sentinel,4351 array_sentinel,
4184 vector,4352 vector,
4353 /// Possible Value tags for this: @"struct"
4354 tuple,
4185 pointer,4355 pointer,
4186 single_const_pointer,4356 single_const_pointer,
4187 single_mut_pointer,4357 single_mut_pointer,
...@@ -4326,6 +4496,7 @@ pub const Type = extern union {...@@ -4326,6 +4496,7 @@ pub const Type = extern union {
4326 .enum_simple => Payload.EnumSimple,4496 .enum_simple => Payload.EnumSimple,
4327 .enum_numbered => Payload.EnumNumbered,4497 .enum_numbered => Payload.EnumNumbered,
4328 .empty_struct => Payload.ContainerScope,4498 .empty_struct => Payload.ContainerScope,
4499 .tuple => Payload.Tuple,
4329 };4500 };
4330 }4501 }
43314502
...@@ -4348,6 +4519,10 @@ pub const Type = extern union {...@@ -4348,6 +4519,10 @@ pub const Type = extern union {
4348 }4519 }
4349 };4520 };
43504521
4522 pub fn isTuple(ty: Type) bool {
4523 return ty.tag() == .tuple;
4524 }
4525
4351 /// The sub-types are named after what fields they contain.4526 /// The sub-types are named after what fields they contain.
4352 pub const Payload = struct {4527 pub const Payload = struct {
4353 tag: Tag,4528 tag: Tag,
...@@ -4490,6 +4665,14 @@ pub const Type = extern union {...@@ -4490,6 +4665,14 @@ pub const Type = extern union {
4490 data: *Module.Struct,4665 data: *Module.Struct,
4491 };4666 };
44924667
4668 pub const Tuple = struct {
4669 base: Payload = .{ .tag = .tuple },
4670 data: struct {
4671 types: []Type,
4672 values: []Value,
4673 },
4674 };
4675
4493 pub const Union = struct {4676 pub const Union = struct {
4494 base: Payload,4677 base: Payload,
4495 data: *Module.Union,4678 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;