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 {
510510 /// Uses the `un_op` field.
511511 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.
514515 /// Uses the `ty_pl` field, payload is index of an array of elements, each of which
515516 /// 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.
516518 vector_init,
517519
518520 /// Communicates an intent to load memory.
src/AstGen.zig+19-13
......@@ -2581,9 +2581,12 @@ fn varDecl(
25812581 // Depending on the type of AST the initialization expression is, we may need an lvalue
25822582 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
25832583 // the variable, no memory location needed.
2584 if (align_inst == .none and !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node)) {
2585 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
2586 .ty = try typeExpr(gz, scope, var_decl.ast.type_node),
2584 const type_node = var_decl.ast.type_node;
2585 if (align_inst == .none and
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),
25872590 } else .none;
25882591 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
60086011 return Zir.Inst.Ref.unreachable_value;
60096012 }
60106013
6011 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{
6014 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{
60126015 .ptr = try gz.addNodeExtended(.ret_ptr, node),
60136016 } else .{
60146017 .ty = try gz.addNodeExtended(.ret_type, node),
......@@ -7725,7 +7728,7 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
77257728 .{ "void", .void_type },
77267729});
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 {
77297732 const node_tags = tree.nodes.items(.tag);
77307733 const node_datas = tree.nodes.items(.data);
77317734 const main_tokens = tree.nodes.items(.main_token);
......@@ -7875,24 +7878,27 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
78757878 .@"orelse",
78767879 => 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.
78797882 .array_init_one,
78807883 .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.
78817893 .array_init_dot_two,
78827894 .array_init_dot_two_comma,
78837895 .array_init_dot,
78847896 .array_init_dot_comma,
7885 .array_init,
7886 .array_init_comma,
7887 .struct_init_one,
7888 .struct_init_one_comma,
78897897 .struct_init_dot_two,
78907898 .struct_init_dot_two_comma,
78917899 .struct_init_dot,
78927900 .struct_init_dot_comma,
7893 .struct_init,
7894 .struct_init_comma,
7895 => return true,
7901 => return have_res_ty,
78967902
78977903 // True because depending on comptime conditions, sub-expressions
78987904 // may be the kind that need memory locations.
src/Liveness.zig+1-1
......@@ -373,7 +373,7 @@ fn analyzeInst(
373373 .vector_init => {
374374 const ty_pl = inst_datas[inst].ty_pl;
375375 const vector_ty = a.air.getRefType(ty_pl.ty);
376 const len = vector_ty.vectorLen();
376 const len = vector_ty.arrayLen();
377377 const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]);
378378
379379 if (elements.len <= bpi - 1) {
src/Sema.zig+186-53
......@@ -2628,6 +2628,7 @@ fn validateUnionInit(
26282628 // Otherwise, the bitcast should be preserved and a store instruction should be
26292629 // emitted to store the constant union value through the bitcast.
26302630 },
2631 .alloc => {},
26312632 else => |t| {
26322633 if (std.debug.runtime_safety) {
26332634 std.debug.panic("unexpected AIR tag for union pointer: {s}", .{@tagName(t)});
......@@ -10694,12 +10695,77 @@ fn zirArrayInit(
1069410695 }
1069510696}
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 {
1069810704 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1069910705 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;
10702 return sema.fail(block, src, "TODO: Sema.zirArrayInitAnon", .{});
10728 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
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);
1070310769}
1070410770
1070510771fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -13540,10 +13606,50 @@ fn elemVal(
1354013606 // TODO: If the index is a vector, the result should be a vector.
1354113607 return elemValArray(sema, block, array, elem_index, array_src, elem_index_src);
1354213608 },
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 },
1354313615 else => unreachable,
1354413616 }
1354513617}
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
1354713653fn elemValArray(
1354813654 sema: *Sema,
1354913655 block: *Block,
......@@ -13901,17 +14007,19 @@ fn coerce(
1390114007 else => {},
1390214008 },
1390314009 .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),
1390514011 .Struct => {
1390614012 if (inst == .empty_struct) {
1390714013 return arrayInitEmpty(sema, dest_ty);
1390814014 }
14015 if (inst_ty.tag() == .tuple) {
14016 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
14017 }
1390914018 },
1391014019 else => {},
1391114020 },
1391214021 .Vector => switch (inst_ty.zigTypeTag()) {
13913 .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
13914 .Vector => return sema.coerceVectors(block, dest_ty, dest_ty_src, inst, inst_src),
14022 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
1391514023 else => {},
1391614024 },
1391714025 .Struct => {
......@@ -14847,10 +14955,8 @@ fn coerceEnumToUnion(
1484714955 return sema.failWithOwnedErrorMsg(msg);
1484814956}
1484914957
14850/// Coerces vectors/arrays which have the same in-memory layout. This can be used for
14851/// both coercing from and to vectors.
14852/// TODO (affects the lang spec) delete this in favor of always using `coerceVectors`.
14853fn coerceVectorInMemory(
14958/// If the lengths match, coerces element-wise.
14959fn coerceArrayLike(
1485414960 sema: *Sema,
1485514961 block: *Block,
1485614962 dest_ty: Type,
......@@ -14860,7 +14966,7 @@ fn coerceVectorInMemory(
1486014966) !Air.Inst.Ref {
1486114967 const inst_ty = sema.typeOf(inst);
1486214968 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
1486514971 if (dest_len != inst_len) {
1486614972 const msg = msg: {
......@@ -14879,22 +14985,50 @@ fn coerceVectorInMemory(
1487914985 const dest_elem_ty = dest_ty.childType();
1488014986 const inst_elem_ty = inst_ty.childType();
1488114987 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) {
14883 // TODO recursive error notes for coerceInMemoryAllowed failure
14884 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
14988 if (in_memory_result == .ok) {
14989 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
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);
1488514995 }
1488614996
14887 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
14888 // These types share the same comptime value representation.
14889 return sema.addConstant(dest_ty, inst_val);
14997 const element_vals = try sema.arena.alloc(Value, dest_len);
14998 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
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 }
1489015017 }
1489115018
14892 try sema.requireRuntimeBlock(block, inst_src);
14893 return block.addBitCast(dest_ty, inst);
15019 if (runtime_src) |rs| {
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 );
1489415028}
1489515029
1489615030/// If the lengths match, coerces element-wise.
14897fn coerceVectors(
15031fn coerceTupleToArray(
1489815032 sema: *Sema,
1489915033 block: *Block,
1490015034 dest_ty: Type,
......@@ -14904,7 +15038,7 @@ fn coerceVectors(
1490415038) !Air.Inst.Ref {
1490515039 const inst_ty = sema.typeOf(inst);
1490615040 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
1490915043 if (dest_len != inst_len) {
1491015044 const msg = msg: {
......@@ -14919,30 +15053,15 @@ fn coerceVectors(
1491915053 return sema.failWithOwnedErrorMsg(msg);
1492015054 }
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
1493515056 const element_vals = try sema.arena.alloc(Value, dest_len);
1493615057 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| {
14940 const index_ref = try sema.addConstant(
14941 Type.usize,
14942 try Value.Tag.int_u64.create(sema.arena, i),
14943 );
15060 var runtime_src: ?LazySrcLoc = null;
15061 for (element_vals) |*elem, i_usize| {
15062 const i = @intCast(u32, i_usize);
1494415063 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);
1494615065 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
1494715066 element_refs[i] = coerced;
1494815067 if (runtime_src == null) {
......@@ -15833,19 +15952,22 @@ fn resolveStructLayout(
1583315952 ty: Type,
1583415953) CompileError!void {
1583515954 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
15836 const struct_obj = resolved_ty.castTag(.@"struct").?.data;
15837 switch (struct_obj.status) {
15838 .none, .have_field_types => {},
15839 .field_types_wip, .layout_wip => {
15840 return sema.fail(block, src, "struct {} depends on itself", .{ty});
15841 },
15842 .have_layout, .fully_resolved_wip, .fully_resolved => return,
15843 }
15844 struct_obj.status = .layout_wip;
15845 for (struct_obj.fields.values()) |field| {
15846 try sema.resolveTypeLayout(block, src, field.ty);
15955 if (resolved_ty.castTag(.@"struct")) |payload| {
15956 const struct_obj = payload.data;
15957 switch (struct_obj.status) {
15958 .none, .have_field_types => {},
15959 .field_types_wip, .layout_wip => {
15960 return sema.fail(block, src, "struct {} depends on itself", .{ty});
15961 },
15962 .have_layout, .fully_resolved_wip, .fully_resolved => return,
15963 }
15964 struct_obj.status = .layout_wip;
15965 for (struct_obj.fields.values()) |field| {
15966 try sema.resolveTypeLayout(block, src, field.ty);
15967 }
15968 struct_obj.status = .have_layout;
1584715969 }
15848 struct_obj.status = .have_layout;
15970 // otherwise it's a tuple; no need to resolve anything
1584915971}
1585015972
1585115973fn resolveUnionLayout(
......@@ -16642,6 +16764,17 @@ pub fn typeHasOnePossibleValue(
1664216764 }
1664316765 return Value.initTag(.empty_struct_value);
1664416766 },
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
1664516778 .enum_numbered => {
1664616779 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
1664716780 const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
src/codegen/llvm.zig+164-14
......@@ -916,6 +916,31 @@ pub const DeclGen = struct {
916916 // reference, we need to copy it here.
917917 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
919944 const struct_obj = t.castTag(.@"struct").?.data;
920945
921946 const name = try struct_obj.getFullyQualifiedName(gpa);
......@@ -2687,10 +2712,23 @@ pub const FuncGen = struct {
26872712 if (!field_ty.hasCodeGenBits()) {
26882713 return null;
26892714 }
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();
26942732 switch (struct_ty.zigTypeTag()) {
26952733 .Struct => {
26962734 var ptr_ty_buf: Type.Payload.Pointer = undefined;
......@@ -4370,19 +4408,85 @@ pub const FuncGen = struct {
43704408 if (self.liveness.isUnused(inst)) return null;
43714409
43724410 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
4373 const vector_ty = self.air.typeOfIndex(inst);
4374 const len = vector_ty.arrayLen();
4411 const result_ty = self.air.typeOfIndex(inst);
4412 const len = @intCast(usize, result_ty.arrayLen());
43754413 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);
4377 const llvm_u32 = self.context.intType(32);
4414 const llvm_result_ty = try self.dg.llvmType(result_ty);
43784415
4379 var vector = llvm_vector_ty.getUndef();
4380 for (elements) |elem, i| {
4381 const index_u32 = llvm_u32.constInt(i, .False);
4382 const llvm_elem = try self.resolveInst(elem);
4383 vector = self.builder.buildInsertElement(vector, llvm_elem, index_u32, "");
4416 switch (result_ty.zigTypeTag()) {
4417 .Vector => {
4418 const llvm_u32 = self.context.intType(32);
4419
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,
43844489 }
4385 return vector;
43864490 }
43874491
43884492 fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -4956,6 +5060,29 @@ fn llvmFieldIndex(
49565060 target: std.Target,
49575061 ptr_pl_buf: *Type.Payload.Pointer,
49585062) ?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 }
49595086 const struct_obj = ty.castTag(.@"struct").?.data;
49605087 if (struct_obj.layout != .Packed) {
49615088 var llvm_field_index: c_uint = 0;
......@@ -4976,7 +5103,7 @@ fn llvmFieldIndex(
49765103 };
49775104 return llvm_field_index;
49785105 } 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.
49805107 return null;
49815108 }
49825109 }
......@@ -5072,6 +5199,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
50725199}
50735200
50745201fn 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
50755206 switch (ty.zigTypeTag()) {
50765207 .Type,
50775208 .ComptimeInt,
......@@ -5096,7 +5227,26 @@ fn isByRef(ty: Type) bool {
50965227 .AnyFrame,
50975228 => 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 },
51005250 .Union => return ty.hasCodeGenBits(),
51015251 .ErrorUnion => return isByRef(ty.errorUnionPayload()),
51025252 .Optional => {
src/print_air.zig+1-1
......@@ -296,7 +296,7 @@ const Writer = struct {
296296 fn writeVectorInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
297297 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
298298 const vector_ty = w.air.getRefType(ty_pl.ty);
299 const len = vector_ty.vectorLen();
299 const len = vector_ty.arrayLen();
300300 const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]);
301301
302302 try s.print("{}, [", .{vector_ty});
src/print_zir.zig+2-1
......@@ -1963,7 +1963,8 @@ const Writer = struct {
19631963 if (i != 0) try stream.writeAll(", ");
19641964 try self.writeInstRef(stream, arg);
19651965 }
1966 try stream.writeAll("})");
1966 try stream.writeAll("}) ");
1967 try self.writeSrc(stream, inst_data.src());
19671968 }
19681969
19691970 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 {
128128 .prefetch_options,
129129 .export_options,
130130 .extern_options,
131 .tuple,
131132 => return .Struct,
132133
133134 .enum_full,
......@@ -604,6 +605,24 @@ pub const Type = extern union {
604605 return a_payload.data == b_payload.data;
605606 }
606607 }
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 }
607626 return a.tag() == b.tag();
608627 },
609628 .Enum => {
......@@ -891,6 +910,21 @@ pub const Type = extern union {
891910 .elem_type = try payload.elem_type.copy(allocator),
892911 });
893912 },
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 },
894928 .function => {
895929 const payload = self.castTag(.function).?.data;
896930 const param_types = try allocator.alloc(Type, payload.param_types.len);
......@@ -1119,6 +1153,24 @@ pub const Type = extern union {
11191153 ty = payload.elem_type;
11201154 continue;
11211155 },
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 },
11221174 .single_const_pointer => {
11231175 const pointee_type = ty.castTag(.single_const_pointer).?.data;
11241176 try writer.writeAll("*const ");
......@@ -1480,15 +1532,40 @@ pub const Type = extern union {
14801532 return requiresComptime(optionalChild(ty, &buf));
14811533 },
14821534
1483 .error_union,
1484 .anyframe_T,
1485 .@"struct",
1486 .@"union",
1487 .union_tagged,
1488 .enum_numbered,
1489 .enum_full,
1490 .enum_nonexhaustive,
1491 => false, // TODO some of these should be `true` depending on their child types
1535 .tuple => {
1536 const tuple = ty.castTag(.tuple).?.data;
1537 for (tuple.types) |field_ty| {
1538 if (requiresComptime(field_ty)) {
1539 return true;
1540 }
1541 }
1542 return false;
1543 },
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(),
14921569 };
14931570 }
14941571
......@@ -1697,6 +1774,16 @@ pub const Type = extern union {
16971774 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
16981775 },
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
17001787 .void,
17011788 .type,
17021789 .comptime_int,
......@@ -1968,6 +2055,21 @@ pub const Type = extern union {
19682055 }
19692056 return big_align;
19702057 },
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
19712073 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
19722074 var buffer: Payload.Bits = undefined;
19732075 const int_tag_ty = self.intTagType(&buffer);
......@@ -2037,13 +2139,14 @@ pub const Type = extern union {
20372139 .void,
20382140 => 0,
20392141
2040 .@"struct" => {
2142 .@"struct", .tuple => {
20412143 const field_count = self.structFieldCount();
20422144 if (field_count == 0) {
20432145 return 0;
20442146 }
20452147 return self.structFieldOffset(field_count, target);
20462148 },
2149
20472150 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
20482151 var buffer: Payload.Bits = undefined;
20492152 const int_tag_ty = self.intTagType(&buffer);
......@@ -2231,6 +2334,11 @@ pub const Type = extern union {
22312334 }
22322335 return total;
22332336 },
2337
2338 .tuple => {
2339 @panic("TODO bitSize tuples");
2340 },
2341
22342342 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
22352343 var buffer: Payload.Bits = undefined;
22362344 const int_tag_ty = ty.intTagType(&buffer);
......@@ -2926,6 +3034,7 @@ pub const Type = extern union {
29263034
29273035 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
29283036 return switch (ty.tag()) {
3037 .tuple => .Auto,
29293038 .@"struct" => ty.castTag(.@"struct").?.data.layout,
29303039 .@"union" => ty.castTag(.@"union").?.data.layout,
29313040 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
......@@ -2998,6 +3107,7 @@ pub const Type = extern union {
29983107 .array_sentinel => ty.castTag(.array_sentinel).?.data.len,
29993108 .array_u8 => ty.castTag(.array_u8).?.data,
30003109 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
3110 .tuple => ty.castTag(.tuple).?.data.types.len,
30013111
30023112 else => unreachable,
30033113 };
......@@ -3010,6 +3120,7 @@ pub const Type = extern union {
30103120 pub fn vectorLen(ty: Type) u32 {
30113121 return switch (ty.tag()) {
30123122 .vector => @intCast(u32, ty.castTag(.vector).?.data.len),
3123 .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len),
30133124 else => unreachable,
30143125 };
30153126 }
......@@ -3463,6 +3574,17 @@ pub const Type = extern union {
34633574 }
34643575 return Value.initTag(.empty_struct_value);
34653576 },
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
34663588 .enum_numbered => {
34673589 const enum_numbered = ty.castTag(.enum_numbered).?.data;
34683590 if (enum_numbered.fields.count() == 1) {
......@@ -3539,7 +3661,8 @@ pub const Type = extern union {
35393661 .Slice, .Many, .C => true,
35403662 .One => ty.elemType().zigTypeTag() == .Array,
35413663 },
3542 else => false, // TODO tuples are indexable
3664 .Struct => ty.tag() == .tuple,
3665 else => false,
35433666 };
35443667 }
35453668
......@@ -3766,6 +3889,7 @@ pub const Type = extern union {
37663889 return struct_obj.fields.count();
37673890 },
37683891 .empty_struct => return 0,
3892 .tuple => return ty.castTag(.tuple).?.data.types.len,
37693893 else => unreachable,
37703894 }
37713895 }
......@@ -3781,6 +3905,7 @@ pub const Type = extern union {
37813905 const union_obj = ty.cast(Payload.Union).?.data;
37823906 return union_obj.fields.values()[index].ty;
37833907 },
3908 .tuple => return ty.castTag(.tuple).?.data.types[index],
37843909 else => unreachable,
37853910 }
37863911 }
......@@ -3933,6 +4058,31 @@ pub const Type = extern union {
39334058 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
39344059 return it.offset;
39354060 },
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
39364086 .@"union" => return 0,
39374087 .union_tagged => {
39384088 const union_obj = ty.castTag(.union_tagged).?.data;
......@@ -4182,6 +4332,8 @@ pub const Type = extern union {
41824332 array,
41834333 array_sentinel,
41844334 vector,
4335 /// Possible Value tags for this: @"struct"
4336 tuple,
41854337 pointer,
41864338 single_const_pointer,
41874339 single_mut_pointer,
......@@ -4326,6 +4478,7 @@ pub const Type = extern union {
43264478 .enum_simple => Payload.EnumSimple,
43274479 .enum_numbered => Payload.EnumNumbered,
43284480 .empty_struct => Payload.ContainerScope,
4481 .tuple => Payload.Tuple,
43294482 };
43304483 }
43314484
......@@ -4490,6 +4643,14 @@ pub const Type = extern union {
44904643 data: *Module.Struct,
44914644 };
44924645
4646 pub const Tuple = struct {
4647 base: Payload = .{ .tag = .tuple },
4648 data: struct {
4649 types: []Type,
4650 values: []Value,
4651 },
4652 };
4653
44934654 pub const Union = struct {
44944655 base: Payload,
44954656 data: *Module.Union,
test/behavior/array_llvm.zig+2-2
......@@ -237,8 +237,6 @@ test "zero-sized array with recursive type definition" {
237237}
238238
239239test "type coercion of anon struct literal to array" {
240 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
241
242240 const S = struct {
243241 const U = union {
244242 a: u32,
......@@ -254,6 +252,8 @@ test "type coercion of anon struct literal to array" {
254252 try expect(arr1[1] == 56);
255253 try expect(arr1[2] == 54);
256254
255 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
256
257257 var x2: U = .{ .a = 42 };
258258 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
259259 var arr2: [3]U = t2;