| author | |
| committer | |
| log | c9ae24503dc8da2e59f46619695bf4eb863fb3ac |
| tree | d55084efed19c32fbccb0c96959940796a1d0c43 |
| parent | f763000dc918c2367ebc181645eb48db896205d8 |
| parent | 1f823eecdd071f619c761a743119f1a2a89af1bf |
| signature |
stage2: implement tuples10 files changed, 595 insertions(+), 98 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 = @intCast(usize, 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/Module.zig+4| ... | ... | @@ -821,6 +821,8 @@ pub const ErrorSet = struct { |
| 821 | 821 | } |
| 822 | 822 | }; |
| 823 | 823 | |
| 824 | pub const RequiresComptime = enum { no, yes, unknown, wip }; | |
| 825 | ||
| 824 | 826 | /// Represents the data that a struct declaration provides. |
| 825 | 827 | pub const Struct = struct { |
| 826 | 828 | /// The Decl that corresponds to the struct itself. |
| ... | ... | @@ -849,6 +851,7 @@ pub const Struct = struct { |
| 849 | 851 | /// If true, definitely nonzero size at runtime. If false, resolving the fields |
| 850 | 852 | /// is necessary to determine whether it has bits at runtime. |
| 851 | 853 | known_has_bits: bool, |
| 854 | requires_comptime: RequiresComptime = .unknown, | |
| 852 | 855 | |
| 853 | 856 | pub const Fields = std.StringArrayHashMapUnmanaged(Field); |
| 854 | 857 | |
| ... | ... | @@ -1038,6 +1041,7 @@ pub const Union = struct { |
| 1038 | 1041 | // which `have_layout` does not ensure. |
| 1039 | 1042 | fully_resolved, |
| 1040 | 1043 | }, |
| 1044 | requires_comptime: RequiresComptime = .unknown, | |
| 1041 | 1045 | |
| 1042 | 1046 | pub const Field = struct { |
| 1043 | 1047 | /// undefined until `status` is `have_field_types` or `have_layout`. |
src/Sema.zig+205-54| ... | ... | @@ -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.isTuple()) { | |
| 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 => { |
| ... | ... | @@ -14276,12 +14384,30 @@ fn storePtr2( |
| 14276 | 14384 | uncasted_operand: Air.Inst.Ref, |
| 14277 | 14385 | operand_src: LazySrcLoc, |
| 14278 | 14386 | air_tag: Air.Inst.Tag, |
| 14279 | ) !void { | |
| 14387 | ) CompileError!void { | |
| 14280 | 14388 | const ptr_ty = sema.typeOf(ptr); |
| 14281 | 14389 | if (ptr_ty.isConstPtr()) |
| 14282 | return sema.fail(block, src, "cannot assign to constant", .{}); | |
| 14390 | return sema.fail(block, ptr_src, "cannot assign to constant", .{}); | |
| 14283 | 14391 | |
| 14284 | 14392 | const elem_ty = ptr_ty.childType(); |
| 14393 | ||
| 14394 | // To generate better code for tuples, we detect a tuple operand here, and | |
| 14395 | // analyze field loads and stores directly. This avoids an extra allocation + memcpy | |
| 14396 | // which would occur if we used `coerce`. | |
| 14397 | const operand_ty = sema.typeOf(uncasted_operand); | |
| 14398 | if (operand_ty.castTag(.tuple)) |payload| { | |
| 14399 | const tuple_fields_len = payload.data.types.len; | |
| 14400 | var i: u32 = 0; | |
| 14401 | while (i < tuple_fields_len) : (i += 1) { | |
| 14402 | const elem_src = operand_src; // TODO better source location | |
| 14403 | const elem = try tupleField(sema, block, uncasted_operand, i, operand_src, elem_src); | |
| 14404 | const elem_index = try sema.addIntUnsigned(Type.usize, i); | |
| 14405 | const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src); | |
| 14406 | try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store); | |
| 14407 | } | |
| 14408 | return; | |
| 14409 | } | |
| 14410 | ||
| 14285 | 14411 | const operand = try sema.coerce(block, elem_ty, uncasted_operand, operand_src); |
| 14286 | 14412 | if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) |
| 14287 | 14413 | return; |
| ... | ... | @@ -14847,10 +14973,8 @@ fn coerceEnumToUnion( |
| 14847 | 14973 | return sema.failWithOwnedErrorMsg(msg); |
| 14848 | 14974 | } |
| 14849 | 14975 | |
| 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( | |
| 14976 | /// If the lengths match, coerces element-wise. | |
| 14977 | fn coerceArrayLike( | |
| 14854 | 14978 | sema: *Sema, |
| 14855 | 14979 | block: *Block, |
| 14856 | 14980 | dest_ty: Type, |
| ... | ... | @@ -14860,7 +14984,7 @@ fn coerceVectorInMemory( |
| 14860 | 14984 | ) !Air.Inst.Ref { |
| 14861 | 14985 | const inst_ty = sema.typeOf(inst); |
| 14862 | 14986 | const inst_len = inst_ty.arrayLen(); |
| 14863 | const dest_len = dest_ty.arrayLen(); | |
| 14987 | const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen()); | |
| 14864 | 14988 | |
| 14865 | 14989 | if (dest_len != inst_len) { |
| 14866 | 14990 | const msg = msg: { |
| ... | ... | @@ -14879,22 +15003,50 @@ fn coerceVectorInMemory( |
| 14879 | 15003 | const dest_elem_ty = dest_ty.childType(); |
| 14880 | 15004 | const inst_elem_ty = inst_ty.childType(); |
| 14881 | 15005 | const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); |
| 14882 | if (in_memory_result != .ok) { | |
| 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); | |
| 14885 | 15013 | } |
| 14886 | 15014 | |
| 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 | } | |
| 14890 | 15035 | } |
| 14891 | 15036 | |
| 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 | ); | |
| 14894 | 15046 | } |
| 14895 | 15047 | |
| 14896 | 15048 | /// If the lengths match, coerces element-wise. |
| 14897 | fn coerceVectors( | |
| 15049 | fn coerceTupleToArray( | |
| 14898 | 15050 | sema: *Sema, |
| 14899 | 15051 | block: *Block, |
| 14900 | 15052 | dest_ty: Type, |
| ... | ... | @@ -14919,30 +15071,15 @@ fn coerceVectors( |
| 14919 | 15071 | return sema.failWithOwnedErrorMsg(msg); |
| 14920 | 15072 | } |
| 14921 | 15073 | |
| 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 | 15074 | const element_vals = try sema.arena.alloc(Value, dest_len); |
| 14936 | 15075 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); |
| 14937 | var runtime_src: ?LazySrcLoc = null; | |
| 15076 | const dest_elem_ty = dest_ty.childType(); | |
| 14938 | 15077 | |
| 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); | |
| 14944 | 15081 | const elem_src = inst_src; // TODO better source location |
| 14945 | const elem_ref = try elemValArray(sema, block, inst, index_ref, inst_src, elem_src); | |
| 15082 | const elem_ref = try tupleField(sema, block, inst, i, inst_src, elem_src); | |
| 14946 | 15083 | const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); |
| 14947 | 15084 | element_refs[i] = coerced; |
| 14948 | 15085 | if (runtime_src == null) { |
| ... | ... | @@ -15833,19 +15970,22 @@ fn resolveStructLayout( |
| 15833 | 15970 | ty: Type, |
| 15834 | 15971 | ) CompileError!void { |
| 15835 | 15972 | 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; | |
| 15847 | 15987 | } |
| 15848 | struct_obj.status = .have_layout; | |
| 15988 | // otherwise it's a tuple; no need to resolve anything | |
| 15849 | 15989 | } |
| 15850 | 15990 | |
| 15851 | 15991 | fn resolveUnionLayout( |
| ... | ... | @@ -16642,6 +16782,17 @@ pub fn typeHasOnePossibleValue( |
| 16642 | 16782 | } |
| 16643 | 16783 | return Value.initTag(.empty_struct_value); |
| 16644 | 16784 | }, |
| 16785 | ||
| 16786 | .tuple => { | |
| 16787 | const tuple = ty.castTag(.tuple).?.data; | |
| 16788 | for (tuple.values) |val| { | |
| 16789 | if (val.tag() == .unreachable_value) { | |
| 16790 | return null; // non-comptime field | |
| 16791 | } | |
| 16792 | } | |
| 16793 | return Value.initTag(.empty_struct_value); | |
| 16794 | }, | |
| 16795 | ||
| 16645 | 16796 | .enum_numbered => { |
| 16646 | 16797 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); |
| 16647 | 16798 | const enum_obj = resolved_ty.castTag(.enum_numbered).?.data; |
src/codegen/llvm.zig+164-14| ... | ... | @@ -916,6 +916,31 @@ pub const DeclGen = struct { |
| 916 | 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 = @intCast(usize, 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+194-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,58 @@ 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 | switch (struct_obj.requires_comptime) { | |
| 1548 | .no, .wip => return false, | |
| 1549 | .yes => return true, | |
| 1550 | .unknown => { | |
| 1551 | struct_obj.requires_comptime = .wip; | |
| 1552 | for (struct_obj.fields.values()) |field| { | |
| 1553 | if (requiresComptime(field.ty)) { | |
| 1554 | struct_obj.requires_comptime = .yes; | |
| 1555 | return true; | |
| 1556 | } | |
| 1557 | } | |
| 1558 | struct_obj.requires_comptime = .no; | |
| 1559 | return false; | |
| 1560 | }, | |
| 1561 | } | |
| 1562 | }, | |
| 1563 | ||
| 1564 | .@"union", .union_tagged => { | |
| 1565 | const union_obj = ty.cast(Payload.Union).?.data; | |
| 1566 | switch (union_obj.requires_comptime) { | |
| 1567 | .no, .wip => return false, | |
| 1568 | .yes => return true, | |
| 1569 | .unknown => { | |
| 1570 | union_obj.requires_comptime = .wip; | |
| 1571 | for (union_obj.fields.values()) |field| { | |
| 1572 | if (requiresComptime(field.ty)) { | |
| 1573 | union_obj.requires_comptime = .yes; | |
| 1574 | return true; | |
| 1575 | } | |
| 1576 | } | |
| 1577 | union_obj.requires_comptime = .no; | |
| 1578 | return false; | |
| 1579 | }, | |
| 1580 | } | |
| 1581 | }, | |
| 1582 | ||
| 1583 | .error_union => return requiresComptime(errorUnionPayload(ty)), | |
| 1584 | .anyframe_T => return ty.castTag(.anyframe_T).?.data.requiresComptime(), | |
| 1585 | .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty.requiresComptime(), | |
| 1586 | .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty.requiresComptime(), | |
| 1492 | 1587 | }; |
| 1493 | 1588 | } |
| 1494 | 1589 | |
| ... | ... | @@ -1697,6 +1792,16 @@ pub const Type = extern union { |
| 1697 | 1792 | return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits(); |
| 1698 | 1793 | }, |
| 1699 | 1794 | |
| 1795 | .tuple => { | |
| 1796 | const tuple = self.castTag(.tuple).?.data; | |
| 1797 | for (tuple.types) |ty, i| { | |
| 1798 | const val = tuple.values[i]; | |
| 1799 | if (val.tag() != .unreachable_value) continue; // comptime field | |
| 1800 | if (ty.hasCodeGenBits()) return true; | |
| 1801 | } | |
| 1802 | return false; | |
| 1803 | }, | |
| 1804 | ||
| 1700 | 1805 | .void, |
| 1701 | 1806 | .type, |
| 1702 | 1807 | .comptime_int, |
| ... | ... | @@ -1968,6 +2073,21 @@ pub const Type = extern union { |
| 1968 | 2073 | } |
| 1969 | 2074 | return big_align; |
| 1970 | 2075 | }, |
| 2076 | ||
| 2077 | .tuple => { | |
| 2078 | const tuple = self.castTag(.tuple).?.data; | |
| 2079 | var big_align: u32 = 0; | |
| 2080 | for (tuple.types) |field_ty, i| { | |
| 2081 | const val = tuple.values[i]; | |
| 2082 | if (val.tag() != .unreachable_value) continue; // comptime field | |
| 2083 | if (!field_ty.hasCodeGenBits()) continue; | |
| 2084 | ||
| 2085 | const field_align = field_ty.abiAlignment(target); | |
| 2086 | big_align = @maximum(big_align, field_align); | |
| 2087 | } | |
| 2088 | return big_align; | |
| 2089 | }, | |
| 2090 | ||
| 1971 | 2091 | .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => { |
| 1972 | 2092 | var buffer: Payload.Bits = undefined; |
| 1973 | 2093 | const int_tag_ty = self.intTagType(&buffer); |
| ... | ... | @@ -2037,13 +2157,14 @@ pub const Type = extern union { |
| 2037 | 2157 | .void, |
| 2038 | 2158 | => 0, |
| 2039 | 2159 | |
| 2040 | .@"struct" => { | |
| 2160 | .@"struct", .tuple => { | |
| 2041 | 2161 | const field_count = self.structFieldCount(); |
| 2042 | 2162 | if (field_count == 0) { |
| 2043 | 2163 | return 0; |
| 2044 | 2164 | } |
| 2045 | 2165 | return self.structFieldOffset(field_count, target); |
| 2046 | 2166 | }, |
| 2167 | ||
| 2047 | 2168 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 2048 | 2169 | var buffer: Payload.Bits = undefined; |
| 2049 | 2170 | const int_tag_ty = self.intTagType(&buffer); |
| ... | ... | @@ -2231,6 +2352,11 @@ pub const Type = extern union { |
| 2231 | 2352 | } |
| 2232 | 2353 | return total; |
| 2233 | 2354 | }, |
| 2355 | ||
| 2356 | .tuple => { | |
| 2357 | @panic("TODO bitSize tuples"); | |
| 2358 | }, | |
| 2359 | ||
| 2234 | 2360 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 2235 | 2361 | var buffer: Payload.Bits = undefined; |
| 2236 | 2362 | const int_tag_ty = ty.intTagType(&buffer); |
| ... | ... | @@ -2926,6 +3052,7 @@ pub const Type = extern union { |
| 2926 | 3052 | |
| 2927 | 3053 | pub fn containerLayout(ty: Type) std.builtin.TypeInfo.ContainerLayout { |
| 2928 | 3054 | return switch (ty.tag()) { |
| 3055 | .tuple => .Auto, | |
| 2929 | 3056 | .@"struct" => ty.castTag(.@"struct").?.data.layout, |
| 2930 | 3057 | .@"union" => ty.castTag(.@"union").?.data.layout, |
| 2931 | 3058 | .union_tagged => ty.castTag(.union_tagged).?.data.layout, |
| ... | ... | @@ -2998,6 +3125,7 @@ pub const Type = extern union { |
| 2998 | 3125 | .array_sentinel => ty.castTag(.array_sentinel).?.data.len, |
| 2999 | 3126 | .array_u8 => ty.castTag(.array_u8).?.data, |
| 3000 | 3127 | .array_u8_sentinel_0 => ty.castTag(.array_u8_sentinel_0).?.data, |
| 3128 | .tuple => ty.castTag(.tuple).?.data.types.len, | |
| 3001 | 3129 | |
| 3002 | 3130 | else => unreachable, |
| 3003 | 3131 | }; |
| ... | ... | @@ -3010,6 +3138,7 @@ pub const Type = extern union { |
| 3010 | 3138 | pub fn vectorLen(ty: Type) u32 { |
| 3011 | 3139 | return switch (ty.tag()) { |
| 3012 | 3140 | .vector => @intCast(u32, ty.castTag(.vector).?.data.len), |
| 3141 | .tuple => @intCast(u32, ty.castTag(.tuple).?.data.types.len), | |
| 3013 | 3142 | else => unreachable, |
| 3014 | 3143 | }; |
| 3015 | 3144 | } |
| ... | ... | @@ -3463,6 +3592,17 @@ pub const Type = extern union { |
| 3463 | 3592 | } |
| 3464 | 3593 | return Value.initTag(.empty_struct_value); |
| 3465 | 3594 | }, |
| 3595 | ||
| 3596 | .tuple => { | |
| 3597 | const tuple = ty.castTag(.tuple).?.data; | |
| 3598 | for (tuple.values) |val| { | |
| 3599 | if (val.tag() == .unreachable_value) { | |
| 3600 | return null; // non-comptime field | |
| 3601 | } | |
| 3602 | } | |
| 3603 | return Value.initTag(.empty_struct_value); | |
| 3604 | }, | |
| 3605 | ||
| 3466 | 3606 | .enum_numbered => { |
| 3467 | 3607 | const enum_numbered = ty.castTag(.enum_numbered).?.data; |
| 3468 | 3608 | if (enum_numbered.fields.count() == 1) { |
| ... | ... | @@ -3539,7 +3679,8 @@ pub const Type = extern union { |
| 3539 | 3679 | .Slice, .Many, .C => true, |
| 3540 | 3680 | .One => ty.elemType().zigTypeTag() == .Array, |
| 3541 | 3681 | }, |
| 3542 | else => false, // TODO tuples are indexable | |
| 3682 | .Struct => ty.isTuple(), | |
| 3683 | else => false, | |
| 3543 | 3684 | }; |
| 3544 | 3685 | } |
| 3545 | 3686 | |
| ... | ... | @@ -3766,6 +3907,7 @@ pub const Type = extern union { |
| 3766 | 3907 | return struct_obj.fields.count(); |
| 3767 | 3908 | }, |
| 3768 | 3909 | .empty_struct => return 0, |
| 3910 | .tuple => return ty.castTag(.tuple).?.data.types.len, | |
| 3769 | 3911 | else => unreachable, |
| 3770 | 3912 | } |
| 3771 | 3913 | } |
| ... | ... | @@ -3781,6 +3923,7 @@ pub const Type = extern union { |
| 3781 | 3923 | const union_obj = ty.cast(Payload.Union).?.data; |
| 3782 | 3924 | return union_obj.fields.values()[index].ty; |
| 3783 | 3925 | }, |
| 3926 | .tuple => return ty.castTag(.tuple).?.data.types[index], | |
| 3784 | 3927 | else => unreachable, |
| 3785 | 3928 | } |
| 3786 | 3929 | } |
| ... | ... | @@ -3933,6 +4076,31 @@ pub const Type = extern union { |
| 3933 | 4076 | it.offset = std.mem.alignForwardGeneric(u64, it.offset, it.big_align); |
| 3934 | 4077 | return it.offset; |
| 3935 | 4078 | }, |
| 4079 | ||
| 4080 | .tuple => { | |
| 4081 | const tuple = ty.castTag(.tuple).?.data; | |
| 4082 | ||
| 4083 | var offset: u64 = 0; | |
| 4084 | var big_align: u32 = 0; | |
| 4085 | ||
| 4086 | for (tuple.types) |field_ty, i| { | |
| 4087 | const field_val = tuple.values[i]; | |
| 4088 | if (field_val.tag() != .unreachable_value) { | |
| 4089 | // comptime field | |
| 4090 | if (i == index) return offset; | |
| 4091 | continue; | |
| 4092 | } | |
| 4093 | ||
| 4094 | const field_align = field_ty.abiAlignment(target); | |
| 4095 | big_align = @maximum(big_align, field_align); | |
| 4096 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | |
| 4097 | if (i == index) return offset; | |
| 4098 | offset += field_ty.abiSize(target); | |
| 4099 | } | |
| 4100 | offset = std.mem.alignForwardGeneric(u64, offset, big_align); | |
| 4101 | return offset; | |
| 4102 | }, | |
| 4103 | ||
| 3936 | 4104 | .@"union" => return 0, |
| 3937 | 4105 | .union_tagged => { |
| 3938 | 4106 | const union_obj = ty.castTag(.union_tagged).?.data; |
| ... | ... | @@ -4182,6 +4350,8 @@ pub const Type = extern union { |
| 4182 | 4350 | array, |
| 4183 | 4351 | array_sentinel, |
| 4184 | 4352 | vector, |
| 4353 | /// Possible Value tags for this: @"struct" | |
| 4354 | tuple, | |
| 4185 | 4355 | pointer, |
| 4186 | 4356 | single_const_pointer, |
| 4187 | 4357 | single_mut_pointer, |
| ... | ... | @@ -4326,6 +4496,7 @@ pub const Type = extern union { |
| 4326 | 4496 | .enum_simple => Payload.EnumSimple, |
| 4327 | 4497 | .enum_numbered => Payload.EnumNumbered, |
| 4328 | 4498 | .empty_struct => Payload.ContainerScope, |
| 4499 | .tuple => Payload.Tuple, | |
| 4329 | 4500 | }; |
| 4330 | 4501 | } |
| 4331 | 4502 | |
| ... | ... | @@ -4348,6 +4519,10 @@ pub const Type = extern union { |
| 4348 | 4519 | } |
| 4349 | 4520 | }; |
| 4350 | 4521 | |
| 4522 | pub fn isTuple(ty: Type) bool { | |
| 4523 | return ty.tag() == .tuple; | |
| 4524 | } | |
| 4525 | ||
| 4351 | 4526 | /// The sub-types are named after what fields they contain. |
| 4352 | 4527 | pub const Payload = struct { |
| 4353 | 4528 | tag: Tag, |
| ... | ... | @@ -4490,6 +4665,14 @@ pub const Type = extern union { |
| 4490 | 4665 | data: *Module.Struct, |
| 4491 | 4666 | }; |
| 4492 | 4667 | |
| 4668 | pub const Tuple = struct { | |
| 4669 | base: Payload = .{ .tag = .tuple }, | |
| 4670 | data: struct { | |
| 4671 | types: []Type, | |
| 4672 | values: []Value, | |
| 4673 | }, | |
| 4674 | }; | |
| 4675 | ||
| 4493 | 4676 | pub const Union = struct { |
| 4494 | 4677 | base: Payload, |
| 4495 | 4678 | 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; |