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 {
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 = @intCast(usize, 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/Module.zig+4
......@@ -821,6 +821,8 @@ pub const ErrorSet = struct {
821821 }
822822};
823823
824pub const RequiresComptime = enum { no, yes, unknown, wip };
825
824826/// Represents the data that a struct declaration provides.
825827pub const Struct = struct {
826828 /// The Decl that corresponds to the struct itself.
......@@ -849,6 +851,7 @@ pub const Struct = struct {
849851 /// If true, definitely nonzero size at runtime. If false, resolving the fields
850852 /// is necessary to determine whether it has bits at runtime.
851853 known_has_bits: bool,
854 requires_comptime: RequiresComptime = .unknown,
852855
853856 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
854857
......@@ -1038,6 +1041,7 @@ pub const Union = struct {
10381041 // which `have_layout` does not ensure.
10391042 fully_resolved,
10401043 },
1044 requires_comptime: RequiresComptime = .unknown,
10411045
10421046 pub const Field = struct {
10431047 /// undefined until `status` is `have_field_types` or `have_layout`.
src/Sema.zig+205-54
......@@ -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.isTuple()) {
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 => {
......@@ -14276,12 +14384,30 @@ fn storePtr2(
1427614384 uncasted_operand: Air.Inst.Ref,
1427714385 operand_src: LazySrcLoc,
1427814386 air_tag: Air.Inst.Tag,
14279) !void {
14387) CompileError!void {
1428014388 const ptr_ty = sema.typeOf(ptr);
1428114389 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
1428414392 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
1428514411 const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src);
1428614412 if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null)
1428714413 return;
......@@ -14847,10 +14973,8 @@ fn coerceEnumToUnion(
1484714973 return sema.failWithOwnedErrorMsg(msg);
1484814974}
1484914975
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(
14976/// If the lengths match, coerces element-wise.
14977fn coerceArrayLike(
1485414978 sema: *Sema,
1485514979 block: *Block,
1485614980 dest_ty: Type,
......@@ -14860,7 +14984,7 @@ fn coerceVectorInMemory(
1486014984) !Air.Inst.Ref {
1486114985 const inst_ty = sema.typeOf(inst);
1486214986 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
1486514989 if (dest_len != inst_len) {
1486614990 const msg = msg: {
......@@ -14879,22 +15003,50 @@ fn coerceVectorInMemory(
1487915003 const dest_elem_ty = dest_ty.childType();
1488015004 const inst_elem_ty = inst_ty.childType();
1488115005 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 });
15006 if (in_memory_result == .ok) {
15007 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
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);
1488515013 }
1488615014
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);
15015 const element_vals = try sema.arena.alloc(Value, dest_len);
15016 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
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 }
1489015035 }
1489115036
14892 try sema.requireRuntimeBlock(block, inst_src);
14893 return block.addBitCast(dest_ty, inst);
15037 if (runtime_src) |rs| {
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 );
1489415046}
1489515047
1489615048/// If the lengths match, coerces element-wise.
14897fn coerceVectors(
15049fn coerceTupleToArray(
1489815050 sema: *Sema,
1489915051 block: *Block,
1490015052 dest_ty: Type,
......@@ -14919,30 +15071,15 @@ fn coerceVectors(
1491915071 return sema.failWithOwnedErrorMsg(msg);
1492015072 }
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
1493515074 const element_vals = try sema.arena.alloc(Value, dest_len);
1493615075 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| {
14940 const index_ref = try sema.addConstant(
14941 Type.usize,
14942 try Value.Tag.int_u64.create(sema.arena, i),
14943 );
15078 var runtime_src: ?LazySrcLoc = null;
15079 for (element_vals) |*elem, i_usize| {
15080 const i = @intCast(u32, i_usize);
1494415081 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);
1494615083 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
1494715084 element_refs[i] = coerced;
1494815085 if (runtime_src == null) {
......@@ -15833,19 +15970,22 @@ fn resolveStructLayout(
1583315970 ty: Type,
1583415971) CompileError!void {
1583515972 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);
15973 if (resolved_ty.castTag(.@"struct")) |payload| {
15974 const struct_obj = payload.data;
15975 switch (struct_obj.status) {
15976 .none, .have_field_types => {},
15977 .field_types_wip, .layout_wip => {
15978 return sema.fail(block, src, "struct {} depends on itself", .{ty});
15979 },
15980 .have_layout, .fully_resolved_wip, .fully_resolved => return,
15981 }
15982 struct_obj.status = .layout_wip;
15983 for (struct_obj.fields.values()) |field| {
15984 try sema.resolveTypeLayout(block, src, field.ty);
15985 }
15986 struct_obj.status = .have_layout;
1584715987 }
15848 struct_obj.status = .have_layout;
15988 // otherwise it's a tuple; no need to resolve anything
1584915989}
1585015990
1585115991fn resolveUnionLayout(
......@@ -16642,6 +16782,17 @@ pub fn typeHasOnePossibleValue(
1664216782 }
1664316783 return Value.initTag(.empty_struct_value);
1664416784 },
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
1664516796 .enum_numbered => {
1664616797 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
1664716798 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 = @intCast(usize, 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+194-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,58 @@ 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 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(),
14921587 };
14931588 }
14941589
......@@ -1697,6 +1792,16 @@ pub const Type = extern union {
16971792 return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
16981793 },
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
17001805 .void,
17011806 .type,
17021807 .comptime_int,
......@@ -1968,6 +2073,21 @@ pub const Type = extern union {
19682073 }
19692074 return big_align;
19702075 },
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
19712091 .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
19722092 var buffer: Payload.Bits = undefined;
19732093 const int_tag_ty = self.intTagType(&buffer);
......@@ -2037,13 +2157,14 @@ pub const Type = extern union {
20372157 .void,
20382158 => 0,
20392159
2040 .@"struct" => {
2160 .@"struct", .tuple => {
20412161 const field_count = self.structFieldCount();
20422162 if (field_count == 0) {
20432163 return 0;
20442164 }
20452165 return self.structFieldOffset(field_count, target);
20462166 },
2167
20472168 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
20482169 var buffer: Payload.Bits = undefined;
20492170 const int_tag_ty = self.intTagType(&buffer);
......@@ -2231,6 +2352,11 @@ pub const Type = extern union {
22312352 }
22322353 return total;
22332354 },
2355
2356 .tuple => {
2357 @panic("TODO bitSize tuples");
2358 },
2359
22342360 .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
22352361 var buffer: Payload.Bits = undefined;
22362362 const int_tag_ty = ty.intTagType(&buffer);
......@@ -2926,6 +3052,7 @@ pub const Type = extern union {
29263052
29273053 pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout {
29283054 return switch (ty.tag()) {
3055 .tuple => .Auto,
29293056 .@"struct" => ty.castTag(.@"struct").?.data.layout,
29303057 .@"union" => ty.castTag(.@"union").?.data.layout,
29313058 .union_tagged => ty.castTag(.union_tagged).?.data.layout,
......@@ -2998,6 +3125,7 @@ pub const Type = extern union {
29983125 .array_sentinel => ty.castTag(.array_sentinel).?.data.len,
29993126 .array_u8 => ty.castTag(.array_u8).?.data,
30003127 .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data,
3128 .tuple => ty.castTag(.tuple).?.data.types.len,
30013129
30023130 else => unreachable,
30033131 };
......@@ -3010,6 +3138,7 @@ pub const Type = extern union {
30103138 pub fn vectorLen(ty: Type) u32 {
30113139 return switch (ty.tag()) {
30123140 .vector => @intCast(u32, ty.castTag(.vector).?.data.len),
3141 .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len),
30133142 else => unreachable,
30143143 };
30153144 }
......@@ -3463,6 +3592,17 @@ pub const Type = extern union {
34633592 }
34643593 return Value.initTag(.empty_struct_value);
34653594 },
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
34663606 .enum_numbered => {
34673607 const enum_numbered = ty.castTag(.enum_numbered).?.data;
34683608 if (enum_numbered.fields.count() == 1) {
......@@ -3539,7 +3679,8 @@ pub const Type = extern union {
35393679 .Slice, .Many, .C => true,
35403680 .One => ty.elemType().zigTypeTag() == .Array,
35413681 },
3542 else => false, // TODO tuples are indexable
3682 .Struct => ty.isTuple(),
3683 else => false,
35433684 };
35443685 }
35453686
......@@ -3766,6 +3907,7 @@ pub const Type = extern union {
37663907 return struct_obj.fields.count();
37673908 },
37683909 .empty_struct => return 0,
3910 .tuple => return ty.castTag(.tuple).?.data.types.len,
37693911 else => unreachable,
37703912 }
37713913 }
......@@ -3781,6 +3923,7 @@ pub const Type = extern union {
37813923 const union_obj = ty.cast(Payload.Union).?.data;
37823924 return union_obj.fields.values()[index].ty;
37833925 },
3926 .tuple => return ty.castTag(.tuple).?.data.types[index],
37843927 else => unreachable,
37853928 }
37863929 }
......@@ -3933,6 +4076,31 @@ pub const Type = extern union {
39334076 it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align);
39344077 return it.offset;
39354078 },
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
39364104 .@"union" => return 0,
39374105 .union_tagged => {
39384106 const union_obj = ty.castTag(.union_tagged).?.data;
......@@ -4182,6 +4350,8 @@ pub const Type = extern union {
41824350 array,
41834351 array_sentinel,
41844352 vector,
4353 /// Possible Value tags for this: @"struct"
4354 tuple,
41854355 pointer,
41864356 single_const_pointer,
41874357 single_mut_pointer,
......@@ -4326,6 +4496,7 @@ pub const Type = extern union {
43264496 .enum_simple => Payload.EnumSimple,
43274497 .enum_numbered => Payload.EnumNumbered,
43284498 .empty_struct => Payload.ContainerScope,
4499 .tuple => Payload.Tuple,
43294500 };
43304501 }
43314502
......@@ -4348,6 +4519,10 @@ pub const Type = extern union {
43484519 }
43494520 };
43504521
4522 pub fn isTuple(ty: Type) bool {
4523 return ty.tag() == .tuple;
4524 }
4525
43514526 /// The sub-types are named after what fields they contain.
43524527 pub const Payload = struct {
43534528 tag: Tag,
......@@ -4490,6 +4665,14 @@ pub const Type = extern union {
44904665 data: *Module.Struct,
44914666 };
44924667
4668 pub const Tuple = struct {
4669 base: Payload = .{ .tag = .tuple },
4670 data: struct {
4671 types: []Type,
4672 values: []Value,
4673 },
4674 };
4675
44934676 pub const Union = struct {
44944677 base: Payload,
44954678 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;