| author | |
| committer | |
| log | e86ff712a666ab5be54fa763cc12a5f245718117 |
| tree | 90166386cac32112afbb0c087453cf22938a2702 |
| parent | eb70f6e8d7f4c1a735fe25de368f6d5459cba16c |
* AIR instruction vector_init gains the ability to init arrays and
tuples in addition to vectors. This will probably also gain the
ability to initialize structs and be renamed to `aggregate_init`.
* AstGen prefers to use an `anon_array_init` ZIR instruction for
local variables when the init expr is an array literal and there is
no type.9 files changed, 550 insertions(+), 97 deletions(-)
src/Air.zig+3-1| ... | ... | @@ -510,9 +510,11 @@ pub const Inst = struct { |
| 510 | 510 | /// Uses the `un_op` field. |
| 511 | 511 | error_name, |
| 512 | 512 | |
| 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 | 515 | /// Uses the `ty_pl` field, payload is index of an array of elements, each of which |
| 515 | 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 | 518 | vector_init, |
| 517 | 519 | |
| 518 | 520 | /// Communicates an intent to load memory. |
src/AstGen.zig+19-13| ... | ... | @@ -2581,9 +2581,12 @@ fn varDecl( |
| 2581 | 2581 | // Depending on the type of AST the initialization expression is, we may need an lvalue |
| 2582 | 2582 | // or an rvalue as a result location. If it is an rvalue, we can use the instruction as |
| 2583 | 2583 | // 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), | |
| 2587 | 2590 | } else .none; |
| 2588 | 2591 | const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node); |
| 2589 | 2592 | |
| ... | ... | @@ -6008,7 +6011,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6008 | 6011 | return Zir.Inst.Ref.unreachable_value; |
| 6009 | 6012 | } |
| 6010 | 6013 | |
| 6011 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ | |
| 6014 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{ | |
| 6012 | 6015 | .ptr = try gz.addNodeExtended(.ret_ptr, node), |
| 6013 | 6016 | } else .{ |
| 6014 | 6017 | .ty = try gz.addNodeExtended(.ret_type, node), |
| ... | ... | @@ -7725,7 +7728,7 @@ const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{ |
| 7725 | 7728 | .{ "void", .void_type }, |
| 7726 | 7729 | }); |
| 7727 | 7730 | |
| 7728 | fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool { | |
| 7731 | fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool { | |
| 7729 | 7732 | const node_tags = tree.nodes.items(.tag); |
| 7730 | 7733 | const node_datas = tree.nodes.items(.data); |
| 7731 | 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 | 7878 | .@"orelse", |
| 7876 | 7879 | => node = node_datas[node].rhs, |
| 7877 | 7880 | |
| 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 | 7882 | .array_init_one, |
| 7880 | 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 | 7893 | .array_init_dot_two, |
| 7882 | 7894 | .array_init_dot_two_comma, |
| 7883 | 7895 | .array_init_dot, |
| 7884 | 7896 | .array_init_dot_comma, |
| 7885 | .array_init, | |
| 7886 | .array_init_comma, | |
| 7887 | .struct_init_one, | |
| 7888 | .struct_init_one_comma, | |
| 7889 | 7897 | .struct_init_dot_two, |
| 7890 | 7898 | .struct_init_dot_two_comma, |
| 7891 | 7899 | .struct_init_dot, |
| 7892 | 7900 | .struct_init_dot_comma, |
| 7893 | .struct_init, | |
| 7894 | .struct_init_comma, | |
| 7895 | => return true, | |
| 7901 | => return have_res_ty, | |
| 7896 | 7902 | |
| 7897 | 7903 | // True because depending on comptime conditions, sub-expressions |
| 7898 | 7904 | // may be the kind that need memory locations. |
src/Liveness.zig+1-1| ... | ... | @@ -373,7 +373,7 @@ fn analyzeInst( |
| 373 | 373 | .vector_init => { |
| 374 | 374 | const ty_pl = inst_datas[inst].ty_pl; |
| 375 | 375 | const vector_ty = a.air.getRefType(ty_pl.ty); |
| 376 | const len = vector_ty.vectorLen(); | |
| 376 | const len = vector_ty.arrayLen(); | |
| 377 | 377 | const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]); |
| 378 | 378 | |
| 379 | 379 | if (elements.len <= bpi - 1) { |
src/Sema.zig+186-53| ... | ... | @@ -2628,6 +2628,7 @@ fn validateUnionInit( |
| 2628 | 2628 | // Otherwise, the bitcast should be preserved and a store instruction should be |
| 2629 | 2629 | // emitted to store the constant union value through the bitcast. |
| 2630 | 2630 | }, |
| 2631 | .alloc => {}, | |
| 2631 | 2632 | else => |t| { |
| 2632 | 2633 | if (std.debug.runtime_safety) { |
| 2633 | 2634 | std.debug.panic("unexpected AIR tag for union pointer: {s}", .{@tagName(t)}); |
| ... | ... | @@ -10694,12 +10695,77 @@ fn zirArrayInit( |
| 10694 | 10695 | } |
| 10695 | 10696 | } |
| 10696 | 10697 | |
| 10697 | fn zirArrayInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | |
| 10698 | fn zirArrayInitAnon( | |
| 10699 | sema: *Sema, | |
| 10700 | block: *Block, | |
| 10701 | inst: Zir.Inst.Index, | |
| 10702 | is_ref: bool, | |
| 10703 | ) CompileError!Air.Inst.Ref { | |
| 10698 | 10704 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 10699 | 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 | }; | |
| 10700 | 10727 | |
| 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); | |
| 10703 | 10769 | } |
| 10704 | 10770 | |
| 10705 | 10771 | fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -13540,10 +13606,50 @@ fn elemVal( |
| 13540 | 13606 | // TODO: If the index is a vector, the result should be a vector. |
| 13541 | 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 | 13615 | else => unreachable, |
| 13544 | 13616 | } |
| 13545 | 13617 | } |
| 13546 | 13618 | |
| 13619 | fn 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 | ||
| 13547 | 13653 | fn elemValArray( |
| 13548 | 13654 | sema: *Sema, |
| 13549 | 13655 | block: *Block, |
| ... | ... | @@ -13901,17 +14007,19 @@ fn coerce( |
| 13901 | 14007 | else => {}, |
| 13902 | 14008 | }, |
| 13903 | 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 | 14011 | .Struct => { |
| 13906 | 14012 | if (inst == .empty_struct) { |
| 13907 | 14013 | return arrayInitEmpty(sema, dest_ty); |
| 13908 | 14014 | } |
| 14015 | if (inst_ty.tag() == .tuple) { | |
| 14016 | return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); | |
| 14017 | } | |
| 13909 | 14018 | }, |
| 13910 | 14019 | else => {}, |
| 13911 | 14020 | }, |
| 13912 | 14021 | .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), | |
| 13915 | 14023 | else => {}, |
| 13916 | 14024 | }, |
| 13917 | 14025 | .Struct => { |
| ... | ... | @@ -14847,10 +14955,8 @@ fn coerceEnumToUnion( |
| 14847 | 14955 | return sema.failWithOwnedErrorMsg(msg); |
| 14848 | 14956 | } |
| 14849 | 14957 | |
| 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`. | |
| 14853 | fn coerceVectorInMemory( | |
| 14958 | /// If the lengths match, coerces element-wise. | |
| 14959 | fn coerceArrayLike( | |
| 14854 | 14960 | sema: *Sema, |
| 14855 | 14961 | block: *Block, |
| 14856 | 14962 | dest_ty: Type, |
| ... | ... | @@ -14860,7 +14966,7 @@ fn coerceVectorInMemory( |
| 14860 | 14966 | ) !Air.Inst.Ref { |
| 14861 | 14967 | const inst_ty = sema.typeOf(inst); |
| 14862 | 14968 | const inst_len = inst_ty.arrayLen(); |
| 14863 | const dest_len = dest_ty.arrayLen(); | |
| 14969 | const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); | |
| 14864 | 14970 | |
| 14865 | 14971 | if (dest_len != inst_len) { |
| 14866 | 14972 | const msg = msg: { |
| ... | ... | @@ -14879,22 +14985,50 @@ fn coerceVectorInMemory( |
| 14879 | 14985 | const dest_elem_ty = dest_ty.childType(); |
| 14880 | 14986 | const inst_elem_ty = inst_ty.childType(); |
| 14881 | 14987 | const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); |
| 14882 | if (in_memory_result != .ok) { | |
| 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); | |
| 14885 | 14995 | } |
| 14886 | 14996 | |
| 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 | } | |
| 14890 | 15017 | } |
| 14891 | 15018 | |
| 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 | ); | |
| 14894 | 15028 | } |
| 14895 | 15029 | |
| 14896 | 15030 | /// If the lengths match, coerces element-wise. |
| 14897 | fn coerceVectors( | |
| 15031 | fn coerceTupleToArray( | |
| 14898 | 15032 | sema: *Sema, |
| 14899 | 15033 | block: *Block, |
| 14900 | 15034 | dest_ty: Type, |
| ... | ... | @@ -14904,7 +15038,7 @@ fn coerceVectors( |
| 14904 | 15038 | ) !Air.Inst.Ref { |
| 14905 | 15039 | const inst_ty = sema.typeOf(inst); |
| 14906 | 15040 | const inst_len = inst_ty.arrayLen(); |
| 14907 | const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); | |
| 15041 | const dest_len = dest_ty.arrayLen(); | |
| 14908 | 15042 | |
| 14909 | 15043 | if (dest_len != inst_len) { |
| 14910 | 15044 | const msg = msg: { |
| ... | ... | @@ -14919,30 +15053,15 @@ fn coerceVectors( |
| 14919 | 15053 | return sema.failWithOwnedErrorMsg(msg); |
| 14920 | 15054 | } |
| 14921 | 15055 | |
| 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 | 15056 | const element_vals = try sema.arena.alloc(Value, dest_len); |
| 14936 | 15057 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); |
| 14937 | var runtime_src: ?LazySrcLoc = null; | |
| 15058 | const dest_elem_ty = dest_ty.childType(); | |
| 14938 | 15059 | |
| 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); | |
| 14944 | 15063 | const elem_src = inst_src; // TODO better source location |
| 14945 | const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src); | |
| 15064 | const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src); | |
| 14946 | 15065 | const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); |
| 14947 | 15066 | element_refs[i] = coerced; |
| 14948 | 15067 | if (runtime_src == null) { |
| ... | ... | @@ -15833,19 +15952,22 @@ fn resolveStructLayout( |
| 15833 | 15952 | ty: Type, |
| 15834 | 15953 | ) CompileError!void { |
| 15835 | 15954 | 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; | |
| 15847 | 15969 | } |
| 15848 | struct_obj.status = .have_layout; | |
| 15970 | // otherwise it's a tuple; no need to resolve anything | |
| 15849 | 15971 | } |
| 15850 | 15972 | |
| 15851 | 15973 | fn resolveUnionLayout( |
| ... | ... | @@ -16642,6 +16764,17 @@ pub fn typeHasOnePossibleValue( |
| 16642 | 16764 | } |
| 16643 | 16765 | return Value.initTag(.empty_struct_value); |
| 16644 | 16766 | }, |
| 16767 | ||
| 16768 | .tuple => { | |
| 16769 | const tuple = ty.castTag(.tuple).?.data; | |
| 16770 | for (tuple.values) |val| { | |
| 16771 | if (val.tag() == .unreachable_value) { | |
| 16772 | return null; // non-comptime field | |
| 16773 | } | |
| 16774 | } | |
| 16775 | return Value.initTag(.empty_struct_value); | |
| 16776 | }, | |
| 16777 | ||
| 16645 | 16778 | .enum_numbered => { |
| 16646 | 16779 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); |
| 16647 | 16780 | const enum_obj = resolved_ty.castTag(.enum_numbered).?.data; |
src/codegen/llvm.zig+164-14| ... | ... | @@ -916,6 +916,31 @@ pub const DeclGen = struct { |
| 916 | 916 | // reference, we need to copy it here. |
| 917 | 917 | gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator()); |
| 918 | 918 | |
| 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 | 944 | const struct_obj = t.castTag(.@"struct").?.data; |
| 920 | 945 | |
| 921 | 946 | const name = try struct_obj.getFullyQualifiedName(gpa); |
| ... | ... | @@ -2687,10 +2712,23 @@ pub const FuncGen = struct { |
| 2687 | 2712 | if (!field_ty.hasCodeGenBits()) { |
| 2688 | 2713 | return null; |
| 2689 | 2714 | } |
| 2715 | const target = self.dg.module.getTarget(); | |
| 2690 | 2716 | |
| 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 | } | |
| 2692 | 2731 | |
| 2693 | const target = self.dg.module.getTarget(); | |
| 2694 | 2732 | switch (struct_ty.zigTypeTag()) { |
| 2695 | 2733 | .Struct => { |
| 2696 | 2734 | var ptr_ty_buf: Type.Payload.Pointer = undefined; |
| ... | ... | @@ -4370,19 +4408,85 @@ pub const FuncGen = struct { |
| 4370 | 4408 | if (self.liveness.isUnused(inst)) return null; |
| 4371 | 4409 | |
| 4372 | 4410 | 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()); | |
| 4375 | 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); | |
| 4377 | const llvm_u32 = self.context.intType(32); | |
| 4414 | const llvm_result_ty = try self.dg.llvmType(result_ty); | |
| 4378 | 4415 | |
| 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, | |
| 4384 | 4489 | } |
| 4385 | return vector; | |
| 4386 | 4490 | } |
| 4387 | 4491 | |
| 4388 | 4492 | fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -4956,6 +5060,29 @@ fn llvmFieldIndex( |
| 4956 | 5060 | target: std.Target, |
| 4957 | 5061 | ptr_pl_buf: *Type.Payload.Pointer, |
| 4958 | 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 | 5086 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 4960 | 5087 | if (struct_obj.layout != .Packed) { |
| 4961 | 5088 | var llvm_field_index: c_uint = 0; |
| ... | ... | @@ -4976,7 +5103,7 @@ fn llvmFieldIndex( |
| 4976 | 5103 | }; |
| 4977 | 5104 | return llvm_field_index; |
| 4978 | 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 | 5107 | return null; |
| 4981 | 5108 | } |
| 4982 | 5109 | } |
| ... | ... | @@ -5072,6 +5199,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool |
| 5072 | 5199 | } |
| 5073 | 5200 | |
| 5074 | 5201 | fn 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 | 5206 | switch (ty.zigTypeTag()) { |
| 5076 | 5207 | .Type, |
| 5077 | 5208 | .ComptimeInt, |
| ... | ... | @@ -5096,7 +5227,26 @@ fn isByRef(ty: Type) bool { |
| 5096 | 5227 | .AnyFrame, |
| 5097 | 5228 | => return false, |
| 5098 | 5229 | |
| 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 | 5250 | .Union => return ty.hasCodeGenBits(), |
| 5101 | 5251 | .ErrorUnion => return isByRef(ty.errorUnionPayload()), |
| 5102 | 5252 | .Optional => { |
src/print_air.zig+1-1| ... | ... | @@ -296,7 +296,7 @@ const Writer = struct { |
| 296 | 296 | fn writeVectorInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 297 | 297 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 298 | 298 | const vector_ty = w.air.getRefType(ty_pl.ty); |
| 299 | const len = vector_ty.vectorLen(); | |
| 299 | const len = vector_ty.arrayLen(); | |
| 300 | 300 | const elements = @bitCast([]const Air.Inst.Ref, w.air.extra[ty_pl.payload..][0..len]); |
| 301 | 301 | |
| 302 | 302 | try s.print("{}, [", .{vector_ty}); |
src/print_zir.zig+2-1| ... | ... | @@ -1963,7 +1963,8 @@ const Writer = struct { |
| 1963 | 1963 | if (i != 0) try stream.writeAll(", "); |
| 1964 | 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 | } |
| 1968 | 1969 | |
| 1969 | 1970 | fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
src/type.zig+172-11| ... | ... | @@ -128,6 +128,7 @@ pub const Type = extern union { |
| 128 | 128 | .prefetch_options, |
| 129 | 129 | .export_options, |
| 130 | 130 | .extern_options, |
| 131 | .tuple, | |
| 131 | 132 | => return .Struct, |
| 132 | 133 | |
| 133 | 134 | .enum_full, |
| ... | ... | @@ -604,6 +605,24 @@ pub const Type = extern union { |
| 604 | 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 | 626 | return a.tag() == b.tag(); |
| 608 | 627 | }, |
| 609 | 628 | .Enum => { |
| ... | ... | @@ -891,6 +910,21 @@ pub const Type = extern union { |
| 891 | 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 | 928 | .function => { |
| 895 | 929 | const payload = self.castTag(.function).?.data; |
| 896 | 930 | const param_types = try allocator.alloc(Type, payload.param_types.len); |
| ... | ... | @@ -1119,6 +1153,24 @@ pub const Type = extern union { |
| 1119 | 1153 | ty = payload.elem_type; |
| 1120 | 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 | 1174 | .single_const_pointer => { |
| 1123 | 1175 | const pointee_type = ty.castTag(.single_const_pointer).?.data; |
| 1124 | 1176 | try writer.writeAll("*const "); |
| ... | ... | @@ -1480,15 +1532,40 @@ pub const Type = extern union { |
| 1480 | 1532 | return requiresComptime(optionalChild(ty, &buf)); |
| 1481 | 1533 | }, |
| 1482 | 1534 | |
| 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(), | |
| 1492 | 1569 | }; |
| 1493 | 1570 | } |
| 1494 | 1571 | |
| ... | ... | @@ -1697,6 +1774,16 @@ pub const Type = extern union { |
| 1697 | 1774 | return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); |
| 1698 | 1775 | }, |
| 1699 | 1776 | |
| 1777 | .tuple => { | |
| 1778 | const tuple = self.castTag(.tuple).?.data; | |
| 1779 | for (tuple.types) |ty, i| { | |
| 1780 | const val = tuple.values[i]; | |
| 1781 | if (val.tag() != .unreachable_value) continue; // comptime field | |
| 1782 | if (ty.hasCodeGenBits()) return true; | |
| 1783 | } | |
| 1784 | return false; | |
| 1785 | }, | |
| 1786 | ||
| 1700 | 1787 | .void, |
| 1701 | 1788 | .type, |
| 1702 | 1789 | .comptime_int, |
| ... | ... | @@ -1968,6 +2055,21 @@ pub const Type = extern union { |
| 1968 | 2055 | } |
| 1969 | 2056 | return big_align; |
| 1970 | 2057 | }, |
| 2058 | ||
| 2059 | .tuple => { | |
| 2060 | const tuple = self.castTag(.tuple).?.data; | |
| 2061 | var big_align: u32 = 0; | |
| 2062 | for (tuple.types) |field_ty, i| { | |
| 2063 | const val = tuple.values[i]; | |
| 2064 | if (val.tag() != .unreachable_value) continue; // comptime field | |
| 2065 | if (!field_ty.hasCodeGenBits()) continue; | |
| 2066 | ||
| 2067 | const field_align = field_ty.abiAlignment(target); | |
| 2068 | big_align = @maximum(big_align, field_align); | |
| 2069 | } | |
| 2070 | return big_align; | |
| 2071 | }, | |
| 2072 | ||
| 1971 | 2073 | .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => { |
| 1972 | 2074 | var buffer: Payload.Bits = undefined; |
| 1973 | 2075 | const int_tag_ty = self.intTagType(&buffer); |
| ... | ... | @@ -2037,13 +2139,14 @@ pub const Type = extern union { |
| 2037 | 2139 | .void, |
| 2038 | 2140 | => 0, |
| 2039 | 2141 | |
| 2040 | .@"struct" => { | |
| 2142 | .@"struct", .tuple => { | |
| 2041 | 2143 | const field_count = self.structFieldCount(); |
| 2042 | 2144 | if (field_count == 0) { |
| 2043 | 2145 | return 0; |
| 2044 | 2146 | } |
| 2045 | 2147 | return self.structFieldOffset(field_count, target); |
| 2046 | 2148 | }, |
| 2149 | ||
| 2047 | 2150 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 2048 | 2151 | var buffer: Payload.Bits = undefined; |
| 2049 | 2152 | const int_tag_ty = self.intTagType(&buffer); |
| ... | ... | @@ -2231,6 +2334,11 @@ pub const Type = extern union { |
| 2231 | 2334 | } |
| 2232 | 2335 | return total; |
| 2233 | 2336 | }, |
| 2337 | ||
| 2338 | .tuple => { | |
| 2339 | @panic("TODO bitSize tuples"); | |
| 2340 | }, | |
| 2341 | ||
| 2234 | 2342 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 2235 | 2343 | var buffer: Payload.Bits = undefined; |
| 2236 | 2344 | const int_tag_ty = ty.intTagType(&buffer); |
| ... | ... | @@ -2926,6 +3034,7 @@ pub const Type = extern union { |
| 2926 | 3034 | |
| 2927 | 3035 | pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout { |
| 2928 | 3036 | return switch (ty.tag()) { |
| 3037 | .tuple => .Auto, | |
| 2929 | 3038 | .@"struct" => ty.castTag(.@"struct").?.data.layout, |
| 2930 | 3039 | .@"union" => ty.castTag(.@"union").?.data.layout, |
| 2931 | 3040 | .union_tagged => ty.castTag(.union_tagged).?.data.layout, |
| ... | ... | @@ -2998,6 +3107,7 @@ pub const Type = extern union { |
| 2998 | 3107 | .array_sentinel => ty.castTag(.array_sentinel).?.data.len, |
| 2999 | 3108 | .array_u8 => ty.castTag(.array_u8).?.data, |
| 3000 | 3109 | .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data, |
| 3110 | .tuple => ty.castTag(.tuple).?.data.types.len, | |
| 3001 | 3111 | |
| 3002 | 3112 | else => unreachable, |
| 3003 | 3113 | }; |
| ... | ... | @@ -3010,6 +3120,7 @@ pub const Type = extern union { |
| 3010 | 3120 | pub fn vectorLen(ty: Type) u32 { |
| 3011 | 3121 | return switch (ty.tag()) { |
| 3012 | 3122 | .vector => @intCast(u32, ty.castTag(.vector).?.data.len), |
| 3123 | .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len), | |
| 3013 | 3124 | else => unreachable, |
| 3014 | 3125 | }; |
| 3015 | 3126 | } |
| ... | ... | @@ -3463,6 +3574,17 @@ pub const Type = extern union { |
| 3463 | 3574 | } |
| 3464 | 3575 | return Value.initTag(.empty_struct_value); |
| 3465 | 3576 | }, |
| 3577 | ||
| 3578 | .tuple => { | |
| 3579 | const tuple = ty.castTag(.tuple).?.data; | |
| 3580 | for (tuple.values) |val| { | |
| 3581 | if (val.tag() == .unreachable_value) { | |
| 3582 | return null; // non-comptime field | |
| 3583 | } | |
| 3584 | } | |
| 3585 | return Value.initTag(.empty_struct_value); | |
| 3586 | }, | |
| 3587 | ||
| 3466 | 3588 | .enum_numbered => { |
| 3467 | 3589 | const enum_numbered = ty.castTag(.enum_numbered).?.data; |
| 3468 | 3590 | if (enum_numbered.fields.count() == 1) { |
| ... | ... | @@ -3539,7 +3661,8 @@ pub const Type = extern union { |
| 3539 | 3661 | .Slice, .Many, .C => true, |
| 3540 | 3662 | .One => ty.elemType().zigTypeTag() == .Array, |
| 3541 | 3663 | }, |
| 3542 | else => false, // TODO tuples are indexable | |
| 3664 | .Struct => ty.tag() == .tuple, | |
| 3665 | else => false, | |
| 3543 | 3666 | }; |
| 3544 | 3667 | } |
| 3545 | 3668 | |
| ... | ... | @@ -3766,6 +3889,7 @@ pub const Type = extern union { |
| 3766 | 3889 | return struct_obj.fields.count(); |
| 3767 | 3890 | }, |
| 3768 | 3891 | .empty_struct => return 0, |
| 3892 | .tuple => return ty.castTag(.tuple).?.data.types.len, | |
| 3769 | 3893 | else => unreachable, |
| 3770 | 3894 | } |
| 3771 | 3895 | } |
| ... | ... | @@ -3781,6 +3905,7 @@ pub const Type = extern union { |
| 3781 | 3905 | const union_obj = ty.cast(Payload.Union).?.data; |
| 3782 | 3906 | return union_obj.fields.values()[index].ty; |
| 3783 | 3907 | }, |
| 3908 | .tuple => return ty.castTag(.tuple).?.data.types[index], | |
| 3784 | 3909 | else => unreachable, |
| 3785 | 3910 | } |
| 3786 | 3911 | } |
| ... | ... | @@ -3933,6 +4058,31 @@ pub const Type = extern union { |
| 3933 | 4058 | it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align); |
| 3934 | 4059 | return it.offset; |
| 3935 | 4060 | }, |
| 4061 | ||
| 4062 | .tuple => { | |
| 4063 | const tuple = ty.castTag(.tuple).?.data; | |
| 4064 | ||
| 4065 | var offset: u64 = 0; | |
| 4066 | var big_align: u32 = 0; | |
| 4067 | ||
| 4068 | for (tuple.types) |field_ty, i| { | |
| 4069 | const field_val = tuple.values[i]; | |
| 4070 | if (field_val.tag() != .unreachable_value) { | |
| 4071 | // comptime field | |
| 4072 | if (i == index) return offset; | |
| 4073 | continue; | |
| 4074 | } | |
| 4075 | ||
| 4076 | const field_align = field_ty.abiAlignment(target); | |
| 4077 | big_align = @maximum(big_align, field_align); | |
| 4078 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | |
| 4079 | if (i == index) return offset; | |
| 4080 | offset += field_ty.abiSize(target); | |
| 4081 | } | |
| 4082 | offset = std.mem.alignForwardGeneric(u64, offset, big_align); | |
| 4083 | return offset; | |
| 4084 | }, | |
| 4085 | ||
| 3936 | 4086 | .@"union" => return 0, |
| 3937 | 4087 | .union_tagged => { |
| 3938 | 4088 | const union_obj = ty.castTag(.union_tagged).?.data; |
| ... | ... | @@ -4182,6 +4332,8 @@ pub const Type = extern union { |
| 4182 | 4332 | array, |
| 4183 | 4333 | array_sentinel, |
| 4184 | 4334 | vector, |
| 4335 | /// Possible Value tags for this: @"struct" | |
| 4336 | tuple, | |
| 4185 | 4337 | pointer, |
| 4186 | 4338 | single_const_pointer, |
| 4187 | 4339 | single_mut_pointer, |
| ... | ... | @@ -4326,6 +4478,7 @@ pub const Type = extern union { |
| 4326 | 4478 | .enum_simple => Payload.EnumSimple, |
| 4327 | 4479 | .enum_numbered => Payload.EnumNumbered, |
| 4328 | 4480 | .empty_struct => Payload.ContainerScope, |
| 4481 | .tuple => Payload.Tuple, | |
| 4329 | 4482 | }; |
| 4330 | 4483 | } |
| 4331 | 4484 | |
| ... | ... | @@ -4490,6 +4643,14 @@ pub const Type = extern union { |
| 4490 | 4643 | data: *Module.Struct, |
| 4491 | 4644 | }; |
| 4492 | 4645 | |
| 4646 | pub const Tuple = struct { | |
| 4647 | base: Payload = .{ .tag = .tuple }, | |
| 4648 | data: struct { | |
| 4649 | types: []Type, | |
| 4650 | values: []Value, | |
| 4651 | }, | |
| 4652 | }; | |
| 4653 | ||
| 4493 | 4654 | pub const Union = struct { |
| 4494 | 4655 | base: Payload, |
| 4495 | 4656 | data: *Module.Union, |
test/behavior/array_llvm.zig+2-2| ... | ... | @@ -237,8 +237,6 @@ test "zero-sized array with recursive type definition" { |
| 237 | 237 | } |
| 238 | 238 | |
| 239 | 239 | test "type coercion of anon struct literal to array" { |
| 240 | if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | |
| 241 | ||
| 242 | 240 | const S = struct { |
| 243 | 241 | const U = union { |
| 244 | 242 | a: u32, |
| ... | ... | @@ -254,6 +252,8 @@ test "type coercion of anon struct literal to array" { |
| 254 | 252 | try expect(arr1[1] == 56); |
| 255 | 253 | try expect(arr1[2] == 54); |
| 256 | 254 | |
| 255 | if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO | |
| 256 | ||
| 257 | 257 | var x2: U = .{ .a = 42 }; |
| 258 | 258 | const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; |
| 259 | 259 | var arr2: [3]U = t2; |