| author | |
| committer | |
| log | 82bd0ac572f14d1e3a13737f4daf00a1ee8041a2 |
| tree | 38efbcea86e28b16a65d36c8866f4870085f9e44 |
| parent | db55f469c12c01831bd393c6701c26c15ffe726c |
Takes advantage of the pattern already established with
array_init_anon. Also upgrades array_init (non-anon) to the pattern.
Implements comptime struct value equality and pointer value hashing.9 files changed, 704 insertions(+), 589 deletions(-)
src/Sema.zig+94-54| ... | @@ -10433,7 +10433,13 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -10433,7 +10433,13 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 10433 | } | 10433 | } |
| 10434 | } | 10434 | } |
| 10435 | 10435 | ||
| 10436 | fn structInitEmpty(sema: *Sema, block: *Block, obj_ty: Type, dest_src: LazySrcLoc, init_src: LazySrcLoc) CompileError!Air.Inst.Ref { | 10436 | fn structInitEmpty( |
| 10437 | sema: *Sema, | ||
| 10438 | block: *Block, | ||
| 10439 | obj_ty: Type, | ||
| 10440 | dest_src: LazySrcLoc, | ||
| 10441 | init_src: LazySrcLoc, | ||
| 10442 | ) CompileError!Air.Inst.Ref { | ||
| 10437 | const gpa = sema.gpa; | 10443 | const gpa = sema.gpa; |
| 10438 | // This logic must be synchronized with that in `zirStructInit`. | 10444 | // This logic must be synchronized with that in `zirStructInit`. |
| 10439 | const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty); | 10445 | const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty); |
| ... | @@ -10477,7 +10483,12 @@ fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro | ... | @@ -10477,7 +10483,12 @@ fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 10477 | return sema.fail(block, src, "TODO: Sema.zirUnionInitPtr", .{}); | 10483 | return sema.fail(block, src, "TODO: Sema.zirUnionInitPtr", .{}); |
| 10478 | } | 10484 | } |
| 10479 | 10485 | ||
| 10480 | fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | 10486 | fn zirStructInit( |
| 10487 | sema: *Sema, | ||
| 10488 | block: *Block, | ||
| 10489 | inst: Zir.Inst.Index, | ||
| 10490 | is_ref: bool, | ||
| 10491 | ) CompileError!Air.Inst.Ref { | ||
| 10481 | const gpa = sema.gpa; | 10492 | const gpa = sema.gpa; |
| 10482 | const zir_datas = sema.code.instructions.items(.data); | 10493 | const zir_datas = sema.code.instructions.items(.data); |
| 10483 | const inst_data = zir_datas[inst].pl_node; | 10494 | const inst_data = zir_datas[inst].pl_node; |
| ... | @@ -10612,10 +10623,6 @@ fn finishStructInit( | ... | @@ -10612,10 +10623,6 @@ fn finishStructInit( |
| 10612 | return sema.failWithOwnedErrorMsg(msg); | 10623 | return sema.failWithOwnedErrorMsg(msg); |
| 10613 | } | 10624 | } |
| 10614 | 10625 | ||
| 10615 | if (is_ref) { | ||
| 10616 | return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{}); | ||
| 10617 | } | ||
| 10618 | |||
| 10619 | const is_comptime = for (field_inits) |field_init| { | 10626 | const is_comptime = for (field_inits) |field_init| { |
| 10620 | if (!(try sema.isComptimeKnown(block, src, field_init))) { | 10627 | if (!(try sema.isComptimeKnown(block, src, field_init))) { |
| 10621 | break false; | 10628 | break false; |
| ... | @@ -10627,10 +10634,24 @@ fn finishStructInit( | ... | @@ -10627,10 +10634,24 @@ fn finishStructInit( |
| 10627 | for (field_inits) |field_init, i| { | 10634 | for (field_inits) |field_init, i| { |
| 10628 | values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; | 10635 | values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; |
| 10629 | } | 10636 | } |
| 10630 | return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values)); | 10637 | const struct_val = try Value.Tag.@"struct".create(sema.arena, values); |
| 10638 | return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref); | ||
| 10639 | } | ||
| 10640 | |||
| 10641 | if (is_ref) { | ||
| 10642 | const alloc = try block.addTy(.alloc, struct_ty); | ||
| 10643 | for (field_inits) |field_init, i_usize| { | ||
| 10644 | const i = @intCast(u32, i_usize); | ||
| 10645 | const field_src = src; | ||
| 10646 | const field_ptr = try sema.structFieldPtrByIndex(block, src, alloc, i, struct_obj, field_src); | ||
| 10647 | try sema.storePtr(block, src, field_ptr, field_init); | ||
| 10648 | } | ||
| 10649 | |||
| 10650 | return alloc; | ||
| 10631 | } | 10651 | } |
| 10632 | 10652 | ||
| 10633 | return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); | 10653 | try sema.requireRuntimeBlock(block, src); |
| 10654 | return block.addVectorInit(struct_ty, field_inits); | ||
| 10634 | } | 10655 | } |
| 10635 | 10656 | ||
| 10636 | fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | 10657 | fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { |
| ... | @@ -10674,51 +10695,43 @@ fn zirArrayInit( | ... | @@ -10674,51 +10695,43 @@ fn zirArrayInit( |
| 10674 | } else null; | 10695 | } else null; |
| 10675 | 10696 | ||
| 10676 | const runtime_src = opt_runtime_src orelse { | 10697 | const runtime_src = opt_runtime_src orelse { |
| 10677 | var anon_decl = try block.startAnonDecl(src); | 10698 | const elem_vals = try sema.arena.alloc(Value, resolved_args.len); |
| 10678 | defer anon_decl.deinit(); | ||
| 10679 | 10699 | ||
| 10680 | const elem_vals = try anon_decl.arena().alloc(Value, resolved_args.len); | ||
| 10681 | for (resolved_args) |arg, i| { | 10700 | for (resolved_args) |arg, i| { |
| 10682 | // We checked that all args are comptime above. | 10701 | // We checked that all args are comptime above. |
| 10683 | const arg_val = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?; | 10702 | elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?; |
| 10684 | elem_vals[i] = try arg_val.copy(anon_decl.arena()); | ||
| 10685 | } | 10703 | } |
| 10686 | 10704 | ||
| 10687 | const val = try Value.Tag.array.create(anon_decl.arena(), elem_vals); | 10705 | const array_val = try Value.Tag.array.create(sema.arena, elem_vals); |
| 10688 | const decl = try anon_decl.finish(try array_ty.copy(anon_decl.arena()), val); | 10706 | return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref); |
| 10689 | if (is_ref) { | ||
| 10690 | return sema.analyzeDeclRef(decl); | ||
| 10691 | } else { | ||
| 10692 | return sema.analyzeDeclVal(block, .unneeded, decl); | ||
| 10693 | } | ||
| 10694 | }; | 10707 | }; |
| 10695 | 10708 | ||
| 10696 | try sema.requireRuntimeBlock(block, runtime_src); | 10709 | try sema.requireRuntimeBlock(block, runtime_src); |
| 10697 | try sema.resolveTypeLayout(block, src, elem_ty); | 10710 | try sema.resolveTypeLayout(block, src, elem_ty); |
| 10698 | 10711 | ||
| 10699 | const alloc_ty = try Type.ptr(sema.arena, .{ | 10712 | if (is_ref) { |
| 10700 | .pointee_type = array_ty, | 10713 | const alloc_ty = try Type.ptr(sema.arena, .{ |
| 10701 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | 10714 | .pointee_type = array_ty, |
| 10702 | }); | 10715 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), |
| 10703 | const alloc = try block.addTy(.alloc, alloc_ty); | 10716 | }); |
| 10717 | const alloc = try block.addTy(.alloc, alloc_ty); | ||
| 10704 | 10718 | ||
| 10705 | const elem_ptr_ty = try Type.ptr(sema.arena, .{ | 10719 | const elem_ptr_ty = try Type.ptr(sema.arena, .{ |
| 10706 | .mutable = true, | 10720 | .mutable = true, |
| 10707 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | 10721 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), |
| 10708 | .pointee_type = elem_ty, | 10722 | .pointee_type = elem_ty, |
| 10709 | }); | 10723 | }); |
| 10710 | const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); | 10724 | const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); |
| 10711 | 10725 | ||
| 10712 | for (resolved_args) |arg, i| { | 10726 | for (resolved_args) |arg, i| { |
| 10713 | const index = try sema.addIntUnsigned(Type.u64, i); | 10727 | const index = try sema.addIntUnsigned(Type.u64, i); |
| 10714 | const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); | 10728 | const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); |
| 10715 | _ = try block.addBinOp(.store, elem_ptr, arg); | 10729 | _ = try block.addBinOp(.store, elem_ptr, arg); |
| 10716 | } | 10730 | } |
| 10717 | if (is_ref) { | ||
| 10718 | return alloc; | 10731 | return alloc; |
| 10719 | } else { | ||
| 10720 | return sema.analyzeLoad(block, .unneeded, alloc, .unneeded); | ||
| 10721 | } | 10732 | } |
| 10733 | |||
| 10734 | return block.addVectorInit(array_ty, resolved_args); | ||
| 10722 | } | 10735 | } |
| 10723 | 10736 | ||
| 10724 | fn zirArrayInitAnon( | 10737 | fn zirArrayInitAnon( |
| ... | @@ -10758,17 +10771,11 @@ fn zirArrayInitAnon( | ... | @@ -10758,17 +10771,11 @@ fn zirArrayInitAnon( |
| 10758 | 10771 | ||
| 10759 | const runtime_src = opt_runtime_src orelse { | 10772 | const runtime_src = opt_runtime_src orelse { |
| 10760 | const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); | 10773 | const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); |
| 10761 | if (!is_ref) return sema.addConstant(tuple_ty, tuple_val); | 10774 | return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); |
| 10762 | |||
| 10763 | var anon_decl = try block.startAnonDecl(src); | ||
| 10764 | defer anon_decl.deinit(); | ||
| 10765 | const decl = try anon_decl.finish( | ||
| 10766 | try tuple_ty.copy(anon_decl.arena()), | ||
| 10767 | try tuple_val.copy(anon_decl.arena()), | ||
| 10768 | ); | ||
| 10769 | return sema.analyzeDeclRef(decl); | ||
| 10770 | }; | 10775 | }; |
| 10771 | 10776 | ||
| 10777 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 10778 | |||
| 10772 | if (is_ref) { | 10779 | if (is_ref) { |
| 10773 | const alloc = try block.addTy(.alloc, tuple_ty); | 10780 | const alloc = try block.addTy(.alloc, tuple_ty); |
| 10774 | for (operands) |operand, i_usize| { | 10781 | for (operands) |operand, i_usize| { |
| ... | @@ -10790,10 +10797,28 @@ fn zirArrayInitAnon( | ... | @@ -10790,10 +10797,28 @@ fn zirArrayInitAnon( |
| 10790 | element_refs[i] = sema.resolveInst(operand); | 10797 | element_refs[i] = sema.resolveInst(operand); |
| 10791 | } | 10798 | } |
| 10792 | 10799 | ||
| 10793 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 10794 | return block.addVectorInit(tuple_ty, element_refs); | 10800 | return block.addVectorInit(tuple_ty, element_refs); |
| 10795 | } | 10801 | } |
| 10796 | 10802 | ||
| 10803 | fn addConstantMaybeRef( | ||
| 10804 | sema: *Sema, | ||
| 10805 | block: *Block, | ||
| 10806 | src: LazySrcLoc, | ||
| 10807 | ty: Type, | ||
| 10808 | val: Value, | ||
| 10809 | is_ref: bool, | ||
| 10810 | ) !Air.Inst.Ref { | ||
| 10811 | if (!is_ref) return sema.addConstant(ty, val); | ||
| 10812 | |||
| 10813 | var anon_decl = try block.startAnonDecl(src); | ||
| 10814 | defer anon_decl.deinit(); | ||
| 10815 | const decl = try anon_decl.finish( | ||
| 10816 | try ty.copy(anon_decl.arena()), | ||
| 10817 | try val.copy(anon_decl.arena()), | ||
| 10818 | ); | ||
| 10819 | return sema.analyzeDeclRef(decl); | ||
| 10820 | } | ||
| 10821 | |||
| 10797 | fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 10822 | fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10798 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 10823 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 10799 | const src = inst_data.src(); | 10824 | const src = inst_data.src(); |
| ... | @@ -13444,18 +13469,30 @@ fn structFieldPtr( | ... | @@ -13444,18 +13469,30 @@ fn structFieldPtr( |
| 13444 | field_name_src: LazySrcLoc, | 13469 | field_name_src: LazySrcLoc, |
| 13445 | unresolved_struct_ty: Type, | 13470 | unresolved_struct_ty: Type, |
| 13446 | ) CompileError!Air.Inst.Ref { | 13471 | ) CompileError!Air.Inst.Ref { |
| 13447 | const arena = sema.arena; | ||
| 13448 | assert(unresolved_struct_ty.zigTypeTag() == .Struct); | 13472 | assert(unresolved_struct_ty.zigTypeTag() == .Struct); |
| 13449 | 13473 | ||
| 13450 | const struct_ptr_ty = sema.typeOf(struct_ptr); | ||
| 13451 | const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); | 13474 | const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); |
| 13452 | const struct_obj = struct_ty.castTag(.@"struct").?.data; | 13475 | const struct_obj = struct_ty.castTag(.@"struct").?.data; |
| 13453 | 13476 | ||
| 13454 | const field_index_big = struct_obj.fields.getIndex(field_name) orelse | 13477 | const field_index_big = struct_obj.fields.getIndex(field_name) orelse |
| 13455 | return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); | 13478 | return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); |
| 13456 | const field_index = @intCast(u32, field_index_big); | 13479 | const field_index = @intCast(u32, field_index_big); |
| 13480 | |||
| 13481 | return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_obj, field_name_src); | ||
| 13482 | } | ||
| 13483 | |||
| 13484 | fn structFieldPtrByIndex( | ||
| 13485 | sema: *Sema, | ||
| 13486 | block: *Block, | ||
| 13487 | src: LazySrcLoc, | ||
| 13488 | struct_ptr: Air.Inst.Ref, | ||
| 13489 | field_index: u32, | ||
| 13490 | struct_obj: *Module.Struct, | ||
| 13491 | field_src: LazySrcLoc, | ||
| 13492 | ) CompileError!Air.Inst.Ref { | ||
| 13457 | const field = struct_obj.fields.values()[field_index]; | 13493 | const field = struct_obj.fields.values()[field_index]; |
| 13458 | 13494 | ||
| 13495 | const struct_ptr_ty = sema.typeOf(struct_ptr); | ||
| 13459 | var ptr_ty_data: Type.Payload.Pointer.Data = .{ | 13496 | var ptr_ty_data: Type.Payload.Pointer.Data = .{ |
| 13460 | .pointee_type = field.ty, | 13497 | .pointee_type = field.ty, |
| 13461 | .mutable = struct_ptr_ty.ptrIsMutable(), | 13498 | .mutable = struct_ptr_ty.ptrIsMutable(), |
| ... | @@ -13470,7 +13507,7 @@ fn structFieldPtr( | ... | @@ -13470,7 +13507,7 @@ fn structFieldPtr( |
| 13470 | var offset: u64 = 0; | 13507 | var offset: u64 = 0; |
| 13471 | var running_bits: u16 = 0; | 13508 | var running_bits: u16 = 0; |
| 13472 | for (struct_obj.fields.values()) |f, i| { | 13509 | for (struct_obj.fields.values()) |f, i| { |
| 13473 | if (!(try sema.typeHasRuntimeBits(block, field_name_src, f.ty))) continue; | 13510 | if (!(try sema.typeHasRuntimeBits(block, field_src, f.ty))) continue; |
| 13474 | 13511 | ||
| 13475 | const field_align = f.packedAlignment(); | 13512 | const field_align = f.packedAlignment(); |
| 13476 | if (field_align == 0) { | 13513 | if (field_align == 0) { |
| ... | @@ -13509,12 +13546,12 @@ fn structFieldPtr( | ... | @@ -13509,12 +13546,12 @@ fn structFieldPtr( |
| 13509 | const int_ty: Type = .{ .ptr_otherwise = &int_payload.base }; | 13546 | const int_ty: Type = .{ .ptr_otherwise = &int_payload.base }; |
| 13510 | ptr_ty_data.host_size = @intCast(u16, int_ty.abiSize(target)); | 13547 | ptr_ty_data.host_size = @intCast(u16, int_ty.abiSize(target)); |
| 13511 | } | 13548 | } |
| 13512 | const ptr_field_ty = try Type.ptr(arena, ptr_ty_data); | 13549 | const ptr_field_ty = try Type.ptr(sema.arena, ptr_ty_data); |
| 13513 | 13550 | ||
| 13514 | if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { | 13551 | if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { |
| 13515 | return sema.addConstant( | 13552 | return sema.addConstant( |
| 13516 | ptr_field_ty, | 13553 | ptr_field_ty, |
| 13517 | try Value.Tag.field_ptr.create(arena, .{ | 13554 | try Value.Tag.field_ptr.create(sema.arena, .{ |
| 13518 | .container_ptr = struct_ptr_val, | 13555 | .container_ptr = struct_ptr_val, |
| 13519 | .field_index = field_index, | 13556 | .field_index = field_index, |
| 13520 | }), | 13557 | }), |
| ... | @@ -13546,6 +13583,9 @@ fn structFieldVal( | ... | @@ -13546,6 +13583,9 @@ fn structFieldVal( |
| 13546 | 13583 | ||
| 13547 | if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { | 13584 | if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { |
| 13548 | if (struct_val.isUndef()) return sema.addConstUndef(field.ty); | 13585 | if (struct_val.isUndef()) return sema.addConstUndef(field.ty); |
| 13586 | if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| { | ||
| 13587 | return sema.addConstant(field.ty, opv); | ||
| 13588 | } | ||
| 13549 | 13589 | ||
| 13550 | const field_values = struct_val.castTag(.@"struct").?.data; | 13590 | const field_values = struct_val.castTag(.@"struct").?.data; |
| 13551 | return sema.addConstant(field.ty, field_values[field_index]); | 13591 | return sema.addConstant(field.ty, field_values[field_index]); |
src/value.zig+138-98| ... | @@ -1530,60 +1530,69 @@ pub const Value = extern union { | ... | @@ -1530,60 +1530,69 @@ pub const Value = extern union { |
| 1530 | const b_tag = b.tag(); | 1530 | const b_tag = b.tag(); |
| 1531 | assert(a_tag != .undef); | 1531 | assert(a_tag != .undef); |
| 1532 | assert(b_tag != .undef); | 1532 | assert(b_tag != .undef); |
| 1533 | if (a_tag == b_tag) { | 1533 | if (a_tag == b_tag) switch (a_tag) { |
| 1534 | switch (a_tag) { | 1534 | .void_value, .null_value, .the_only_possible_value => return true, |
| 1535 | .void_value, .null_value, .the_only_possible_value => return true, | 1535 | .enum_literal => { |
| 1536 | .enum_literal => { | 1536 | const a_name = a.castTag(.enum_literal).?.data; |
| 1537 | const a_name = a.castTag(.enum_literal).?.data; | 1537 | const b_name = b.castTag(.enum_literal).?.data; |
| 1538 | const b_name = b.castTag(.enum_literal).?.data; | 1538 | return std.mem.eql(u8, a_name, b_name); |
| 1539 | return std.mem.eql(u8, a_name, b_name); | 1539 | }, |
| 1540 | }, | 1540 | .enum_field_index => { |
| 1541 | .enum_field_index => { | 1541 | const a_field_index = a.castTag(.enum_field_index).?.data; |
| 1542 | const a_field_index = a.castTag(.enum_field_index).?.data; | 1542 | const b_field_index = b.castTag(.enum_field_index).?.data; |
| 1543 | const b_field_index = b.castTag(.enum_field_index).?.data; | 1543 | return a_field_index == b_field_index; |
| 1544 | return a_field_index == b_field_index; | 1544 | }, |
| 1545 | }, | 1545 | .opt_payload => { |
| 1546 | .opt_payload => { | 1546 | const a_payload = a.castTag(.opt_payload).?.data; |
| 1547 | const a_payload = a.castTag(.opt_payload).?.data; | 1547 | const b_payload = b.castTag(.opt_payload).?.data; |
| 1548 | const b_payload = b.castTag(.opt_payload).?.data; | 1548 | var buffer: Type.Payload.ElemType = undefined; |
| 1549 | var buffer: Type.Payload.ElemType = undefined; | 1549 | return eql(a_payload, b_payload, ty.optionalChild(&buffer)); |
| 1550 | return eql(a_payload, b_payload, ty.optionalChild(&buffer)); | 1550 | }, |
| 1551 | }, | 1551 | .slice => { |
| 1552 | .slice => { | 1552 | const a_payload = a.castTag(.slice).?.data; |
| 1553 | const a_payload = a.castTag(.slice).?.data; | 1553 | const b_payload = b.castTag(.slice).?.data; |
| 1554 | const b_payload = b.castTag(.slice).?.data; | 1554 | if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; |
| 1555 | if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; | ||
| 1556 | 1555 | ||
| 1557 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; | 1556 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 1558 | const ptr_ty = ty.slicePtrFieldType(&ptr_buf); | 1557 | const ptr_ty = ty.slicePtrFieldType(&ptr_buf); |
| 1559 | 1558 | ||
| 1560 | return eql(a_payload.ptr, b_payload.ptr, ptr_ty); | 1559 | return eql(a_payload.ptr, b_payload.ptr, ptr_ty); |
| 1561 | }, | 1560 | }, |
| 1562 | .elem_ptr => @panic("TODO: Implement more pointer eql cases"), | 1561 | .elem_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1563 | .field_ptr => @panic("TODO: Implement more pointer eql cases"), | 1562 | .field_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1564 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | 1563 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1565 | .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | 1564 | .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), |
| 1566 | .array => { | 1565 | .array => { |
| 1567 | const a_array = a.castTag(.array).?.data; | 1566 | const a_array = a.castTag(.array).?.data; |
| 1568 | const b_array = b.castTag(.array).?.data; | 1567 | const b_array = b.castTag(.array).?.data; |
| 1569 | 1568 | ||
| 1570 | if (a_array.len != b_array.len) return false; | 1569 | if (a_array.len != b_array.len) return false; |
| 1571 | 1570 | ||
| 1572 | const elem_ty = ty.childType(); | 1571 | const elem_ty = ty.childType(); |
| 1573 | for (a_array) |a_elem, i| { | 1572 | for (a_array) |a_elem, i| { |
| 1574 | const b_elem = b_array[i]; | 1573 | const b_elem = b_array[i]; |
| 1575 | 1574 | ||
| 1576 | if (!eql(a_elem, b_elem, elem_ty)) return false; | 1575 | if (!eql(a_elem, b_elem, elem_ty)) return false; |
| 1577 | } | 1576 | } |
| 1578 | return true; | 1577 | return true; |
| 1579 | }, | 1578 | }, |
| 1580 | .function => { | 1579 | .function => { |
| 1581 | const a_payload = a.castTag(.function).?.data; | 1580 | const a_payload = a.castTag(.function).?.data; |
| 1582 | const b_payload = b.castTag(.function).?.data; | 1581 | const b_payload = b.castTag(.function).?.data; |
| 1583 | return a_payload == b_payload; | 1582 | return a_payload == b_payload; |
| 1584 | }, | 1583 | }, |
| 1585 | else => {}, | 1584 | .@"struct" => { |
| 1586 | } | 1585 | const fields = ty.structFields().values(); |
| 1586 | const a_field_vals = a.castTag(.@"struct").?.data; | ||
| 1587 | const b_field_vals = b.castTag(.@"struct").?.data; | ||
| 1588 | assert(a_field_vals.len == b_field_vals.len); | ||
| 1589 | assert(fields.len == a_field_vals.len); | ||
| 1590 | for (fields) |field, i| { | ||
| 1591 | if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false; | ||
| 1592 | } | ||
| 1593 | return true; | ||
| 1594 | }, | ||
| 1595 | else => {}, | ||
| 1587 | } else if (a_tag == .null_value or b_tag == .null_value) { | 1596 | } else if (a_tag == .null_value or b_tag == .null_value) { |
| 1588 | return false; | 1597 | return false; |
| 1589 | } | 1598 | } |
| ... | @@ -1628,6 +1637,13 @@ pub const Value = extern union { | ... | @@ -1628,6 +1637,13 @@ pub const Value = extern union { |
| 1628 | } | 1637 | } |
| 1629 | return true; | 1638 | return true; |
| 1630 | }, | 1639 | }, |
| 1640 | .Struct => { | ||
| 1641 | // must be a struct with no fields since we checked for if | ||
| 1642 | // both have the struct tag above. | ||
| 1643 | const fields = ty.structFields().values(); | ||
| 1644 | assert(fields.len == 0); | ||
| 1645 | return true; | ||
| 1646 | }, | ||
| 1631 | else => return order(a, b).compare(.eq), | 1647 | else => return order(a, b).compare(.eq), |
| 1632 | } | 1648 | } |
| 1633 | } | 1649 | } |
| ... | @@ -1651,31 +1667,13 @@ pub const Value = extern union { | ... | @@ -1651,31 +1667,13 @@ pub const Value = extern union { |
| 1651 | var buf: ToTypeBuffer = undefined; | 1667 | var buf: ToTypeBuffer = undefined; |
| 1652 | return val.toType(&buf).hashWithHasher(hasher); | 1668 | return val.toType(&buf).hashWithHasher(hasher); |
| 1653 | }, | 1669 | }, |
| 1654 | .Bool => { | ||
| 1655 | std.hash.autoHash(hasher, val.toBool()); | ||
| 1656 | }, | ||
| 1657 | .Int, .ComptimeInt => { | ||
| 1658 | var space: BigIntSpace = undefined; | ||
| 1659 | const big = val.toBigInt(&space); | ||
| 1660 | std.hash.autoHash(hasher, big.positive); | ||
| 1661 | for (big.limbs) |limb| { | ||
| 1662 | std.hash.autoHash(hasher, limb); | ||
| 1663 | } | ||
| 1664 | }, | ||
| 1665 | .Float, .ComptimeFloat => { | 1670 | .Float, .ComptimeFloat => { |
| 1666 | // TODO double check the lang spec. should we to bitwise hashing here, | 1671 | // TODO double check the lang spec. should we to bitwise hashing here, |
| 1667 | // or a hash that normalizes the float value? | 1672 | // or a hash that normalizes the float value? |
| 1668 | const float = val.toFloat(f128); | 1673 | const float = val.toFloat(f128); |
| 1669 | std.hash.autoHash(hasher, @bitCast(u128, float)); | 1674 | std.hash.autoHash(hasher, @bitCast(u128, float)); |
| 1670 | }, | 1675 | }, |
| 1671 | .Pointer => switch (val.tag()) { | 1676 | .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { |
| 1672 | .decl_ref_mut, | ||
| 1673 | .extern_fn, | ||
| 1674 | .decl_ref, | ||
| 1675 | .function, | ||
| 1676 | .variable, | ||
| 1677 | => std.hash.autoHash(hasher, val.pointerDecl().?), | ||
| 1678 | |||
| 1679 | .slice => { | 1677 | .slice => { |
| 1680 | const slice = val.castTag(.slice).?.data; | 1678 | const slice = val.castTag(.slice).?.data; |
| 1681 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; | 1679 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| ... | @@ -1684,22 +1682,7 @@ pub const Value = extern union { | ... | @@ -1684,22 +1682,7 @@ pub const Value = extern union { |
| 1684 | hash(slice.len, Type.usize, hasher); | 1682 | hash(slice.len, Type.usize, hasher); |
| 1685 | }, | 1683 | }, |
| 1686 | 1684 | ||
| 1687 | // For these, hash them as hash of a pointer to the decl, | 1685 | else => return hashPtr(val, hasher), |
| 1688 | // combined with a hash of the byte offset from the decl. | ||
| 1689 | .elem_ptr => @panic("TODO: Implement more pointer hashing cases"), | ||
| 1690 | .field_ptr => @panic("TODO: Implement more pointer hashing cases"), | ||
| 1691 | .eu_payload_ptr => @panic("TODO: Implement more pointer hashing cases"), | ||
| 1692 | .opt_payload_ptr => @panic("TODO: Implement more pointer hashing cases"), | ||
| 1693 | |||
| 1694 | .zero, | ||
| 1695 | .one, | ||
| 1696 | .int_u64, | ||
| 1697 | .int_i64, | ||
| 1698 | .int_big_positive, | ||
| 1699 | .int_big_negative, | ||
| 1700 | => @panic("TODO: Implement pointer hashing for int pointers"), | ||
| 1701 | |||
| 1702 | else => unreachable, | ||
| 1703 | }, | 1686 | }, |
| 1704 | .Array, .Vector => { | 1687 | .Array, .Vector => { |
| 1705 | const len = ty.arrayLen(); | 1688 | const len = ty.arrayLen(); |
| ... | @@ -1739,14 +1722,7 @@ pub const Value = extern union { | ... | @@ -1739,14 +1722,7 @@ pub const Value = extern union { |
| 1739 | .Enum => { | 1722 | .Enum => { |
| 1740 | var enum_space: Payload.U64 = undefined; | 1723 | var enum_space: Payload.U64 = undefined; |
| 1741 | const int_val = val.enumToInt(ty, &enum_space); | 1724 | const int_val = val.enumToInt(ty, &enum_space); |
| 1742 | 1725 | hashInt(int_val, hasher); | |
| 1743 | var space: BigIntSpace = undefined; | ||
| 1744 | const big = int_val.toBigInt(&space); | ||
| 1745 | |||
| 1746 | std.hash.autoHash(hasher, big.positive); | ||
| 1747 | for (big.limbs) |limb| { | ||
| 1748 | std.hash.autoHash(hasher, limb); | ||
| 1749 | } | ||
| 1750 | }, | 1726 | }, |
| 1751 | .Union => { | 1727 | .Union => { |
| 1752 | const union_obj = val.cast(Payload.Union).?.data; | 1728 | const union_obj = val.cast(Payload.Union).?.data; |
| ... | @@ -1757,8 +1733,12 @@ pub const Value = extern union { | ... | @@ -1757,8 +1733,12 @@ pub const Value = extern union { |
| 1757 | union_obj.val.hash(active_field_ty, hasher); | 1733 | union_obj.val.hash(active_field_ty, hasher); |
| 1758 | }, | 1734 | }, |
| 1759 | .Fn => { | 1735 | .Fn => { |
| 1760 | const func = val.castTag(.function).?.data; | 1736 | const func: *Module.Fn = val.castTag(.function).?.data; |
| 1761 | return std.hash.autoHash(hasher, func.owner_decl); | 1737 | // Note that his hashes the *Fn rather than the *Decl. This is |
| 1738 | // to differentiate function bodies from function pointers. | ||
| 1739 | // This is currently redundant since we already hash the zig type tag | ||
| 1740 | // at the top of this function. | ||
| 1741 | std.hash.autoHash(hasher, func); | ||
| 1762 | }, | 1742 | }, |
| 1763 | .Frame => { | 1743 | .Frame => { |
| 1764 | @panic("TODO implement hashing frame values"); | 1744 | @panic("TODO implement hashing frame values"); |
| ... | @@ -1824,6 +1804,65 @@ pub const Value = extern union { | ... | @@ -1824,6 +1804,65 @@ pub const Value = extern union { |
| 1824 | }; | 1804 | }; |
| 1825 | } | 1805 | } |
| 1826 | 1806 | ||
| 1807 | fn hashInt(int_val: Value, hasher: *std.hash.Wyhash) void { | ||
| 1808 | var buffer: BigIntSpace = undefined; | ||
| 1809 | const big = int_val.toBigInt(&buffer); | ||
| 1810 | std.hash.autoHash(hasher, big.positive); | ||
| 1811 | for (big.limbs) |limb| { | ||
| 1812 | std.hash.autoHash(hasher, limb); | ||
| 1813 | } | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | fn hashPtr(ptr_val: Value, hasher: *std.hash.Wyhash) void { | ||
| 1817 | switch (ptr_val.tag()) { | ||
| 1818 | .decl_ref, | ||
| 1819 | .decl_ref_mut, | ||
| 1820 | .extern_fn, | ||
| 1821 | .function, | ||
| 1822 | .variable, | ||
| 1823 | => { | ||
| 1824 | const decl: *Module.Decl = ptr_val.pointerDecl().?; | ||
| 1825 | std.hash.autoHash(hasher, decl); | ||
| 1826 | }, | ||
| 1827 | |||
| 1828 | .elem_ptr => { | ||
| 1829 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; | ||
| 1830 | hashPtr(elem_ptr.array_ptr, hasher); | ||
| 1831 | std.hash.autoHash(hasher, Value.Tag.elem_ptr); | ||
| 1832 | std.hash.autoHash(hasher, elem_ptr.index); | ||
| 1833 | }, | ||
| 1834 | .field_ptr => { | ||
| 1835 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; | ||
| 1836 | std.hash.autoHash(hasher, Value.Tag.field_ptr); | ||
| 1837 | hashPtr(field_ptr.container_ptr, hasher); | ||
| 1838 | std.hash.autoHash(hasher, field_ptr.field_index); | ||
| 1839 | }, | ||
| 1840 | .eu_payload_ptr => { | ||
| 1841 | const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; | ||
| 1842 | std.hash.autoHash(hasher, Value.Tag.eu_payload_ptr); | ||
| 1843 | hashPtr(err_union_ptr, hasher); | ||
| 1844 | }, | ||
| 1845 | .opt_payload_ptr => { | ||
| 1846 | const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; | ||
| 1847 | std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr); | ||
| 1848 | hashPtr(opt_ptr, hasher); | ||
| 1849 | }, | ||
| 1850 | |||
| 1851 | .zero, | ||
| 1852 | .one, | ||
| 1853 | .int_u64, | ||
| 1854 | .int_i64, | ||
| 1855 | .int_big_positive, | ||
| 1856 | .int_big_negative, | ||
| 1857 | .bool_false, | ||
| 1858 | .bool_true, | ||
| 1859 | .the_only_possible_value, | ||
| 1860 | => return hashInt(ptr_val, hasher), | ||
| 1861 | |||
| 1862 | else => unreachable, | ||
| 1863 | } | ||
| 1864 | } | ||
| 1865 | |||
| 1827 | pub fn markReferencedDeclsAlive(val: Value) void { | 1866 | pub fn markReferencedDeclsAlive(val: Value) void { |
| 1828 | switch (val.tag()) { | 1867 | switch (val.tag()) { |
| 1829 | .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(), | 1868 | .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(), |
| ... | @@ -1876,7 +1915,8 @@ pub const Value = extern union { | ... | @@ -1876,7 +1915,8 @@ pub const Value = extern union { |
| 1876 | pub fn slicePtr(val: Value) Value { | 1915 | pub fn slicePtr(val: Value) Value { |
| 1877 | return switch (val.tag()) { | 1916 | return switch (val.tag()) { |
| 1878 | .slice => val.castTag(.slice).?.data.ptr, | 1917 | .slice => val.castTag(.slice).?.data.ptr, |
| 1879 | .decl_ref, .decl_ref_mut => val, | 1918 | // TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc. |
| 1919 | .decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr => val, | ||
| 1880 | else => unreachable, | 1920 | else => unreachable, |
| 1881 | }; | 1921 | }; |
| 1882 | } | 1922 | } |
test/behavior.zig-3| ... | @@ -98,7 +98,6 @@ test { | ... | @@ -98,7 +98,6 @@ test { |
| 98 | _ = @import("behavior/bugs/3007.zig"); | 98 | _ = @import("behavior/bugs/3007.zig"); |
| 99 | _ = @import("behavior/bugs/9584.zig"); | 99 | _ = @import("behavior/bugs/9584.zig"); |
| 100 | _ = @import("behavior/cast_llvm.zig"); | 100 | _ = @import("behavior/cast_llvm.zig"); |
| 101 | _ = @import("behavior/enum_llvm.zig"); | ||
| 102 | _ = @import("behavior/error_llvm.zig"); | 101 | _ = @import("behavior/error_llvm.zig"); |
| 103 | _ = @import("behavior/eval.zig"); | 102 | _ = @import("behavior/eval.zig"); |
| 104 | _ = @import("behavior/floatop.zig"); | 103 | _ = @import("behavior/floatop.zig"); |
| ... | @@ -163,9 +162,7 @@ test { | ... | @@ -163,9 +162,7 @@ test { |
| 163 | _ = @import("behavior/muladd.zig"); | 162 | _ = @import("behavior/muladd.zig"); |
| 164 | _ = @import("behavior/null_stage1.zig"); | 163 | _ = @import("behavior/null_stage1.zig"); |
| 165 | _ = @import("behavior/optional_stage1.zig"); | 164 | _ = @import("behavior/optional_stage1.zig"); |
| 166 | _ = @import("behavior/pointers_stage1.zig"); | ||
| 167 | _ = @import("behavior/popcount_stage1.zig"); | 165 | _ = @import("behavior/popcount_stage1.zig"); |
| 168 | _ = @import("behavior/ptrcast_stage1.zig"); | ||
| 169 | _ = @import("behavior/reflection.zig"); | 166 | _ = @import("behavior/reflection.zig"); |
| 170 | _ = @import("behavior/saturating_arithmetic_stage1.zig"); | 167 | _ = @import("behavior/saturating_arithmetic_stage1.zig"); |
| 171 | _ = @import("behavior/select.zig"); | 168 | _ = @import("behavior/select.zig"); |
test/behavior/enum.zig+113| ... | @@ -972,3 +972,116 @@ fn test3_2(f: Test3Foo) !void { | ... | @@ -972,3 +972,116 @@ fn test3_2(f: Test3Foo) !void { |
| 972 | else => unreachable, | 972 | else => unreachable, |
| 973 | } | 973 | } |
| 974 | } | 974 | } |
| 975 | |||
| 976 | test "@tagName" { | ||
| 977 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 978 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 979 | |||
| 980 | try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); | ||
| 981 | comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); | ||
| 982 | } | ||
| 983 | |||
| 984 | fn testEnumTagNameBare(n: anytype) []const u8 { | ||
| 985 | return @tagName(n); | ||
| 986 | } | ||
| 987 | |||
| 988 | const BareNumber = enum { One, Two, Three }; | ||
| 989 | |||
| 990 | test "@tagName non-exhaustive enum" { | ||
| 991 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 992 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 993 | |||
| 994 | try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); | ||
| 995 | comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); | ||
| 996 | } | ||
| 997 | const NonExhaustive = enum(u8) { A, B, _ }; | ||
| 998 | |||
| 999 | test "@tagName is null-terminated" { | ||
| 1000 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1001 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1002 | |||
| 1003 | const S = struct { | ||
| 1004 | fn doTheTest(n: BareNumber) !void { | ||
| 1005 | try expect(@tagName(n)[3] == 0); | ||
| 1006 | } | ||
| 1007 | }; | ||
| 1008 | try S.doTheTest(.Two); | ||
| 1009 | try comptime S.doTheTest(.Two); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | test "tag name with assigned enum values" { | ||
| 1013 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1014 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1015 | |||
| 1016 | const LocalFoo = enum(u8) { | ||
| 1017 | A = 1, | ||
| 1018 | B = 0, | ||
| 1019 | }; | ||
| 1020 | var b = LocalFoo.B; | ||
| 1021 | try expect(mem.eql(u8, @tagName(b), "B")); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | test "@tagName on enum literals" { | ||
| 1025 | try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); | ||
| 1026 | comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | test "enum literal casting to optional" { | ||
| 1030 | var bar: ?Bar = undefined; | ||
| 1031 | bar = .B; | ||
| 1032 | |||
| 1033 | try expect(bar.? == Bar.B); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 }; | ||
| 1037 | const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 }; | ||
| 1038 | const C = enum(u2) { One4, Two4, Three4, Four4 }; | ||
| 1039 | |||
| 1040 | const BitFieldOfEnums = packed struct { | ||
| 1041 | a: A, | ||
| 1042 | b: B, | ||
| 1043 | c: C, | ||
| 1044 | }; | ||
| 1045 | |||
| 1046 | const bit_field_1 = BitFieldOfEnums{ | ||
| 1047 | .a = A.Two, | ||
| 1048 | .b = B.Three3, | ||
| 1049 | .c = C.Four4, | ||
| 1050 | }; | ||
| 1051 | |||
| 1052 | test "bit field access with enum fields" { | ||
| 1053 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1054 | |||
| 1055 | var data = bit_field_1; | ||
| 1056 | try expect(getA(&data) == A.Two); | ||
| 1057 | try expect(getB(&data) == B.Three3); | ||
| 1058 | try expect(getC(&data) == C.Four4); | ||
| 1059 | comptime try expect(@sizeOf(BitFieldOfEnums) == 1); | ||
| 1060 | |||
| 1061 | data.b = B.Four3; | ||
| 1062 | try expect(data.b == B.Four3); | ||
| 1063 | |||
| 1064 | data.a = A.Three; | ||
| 1065 | try expect(data.a == A.Three); | ||
| 1066 | try expect(data.b == B.Four3); | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | fn getA(data: *const BitFieldOfEnums) A { | ||
| 1070 | return data.a; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | fn getB(data: *const BitFieldOfEnums) B { | ||
| 1074 | return data.b; | ||
| 1075 | } | ||
| 1076 | |||
| 1077 | fn getC(data: *const BitFieldOfEnums) C { | ||
| 1078 | return data.c; | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | test "enum literal in array literal" { | ||
| 1082 | const Items = enum { one, two }; | ||
| 1083 | const array = [_]Items{ .one, .two }; | ||
| 1084 | |||
| 1085 | try expect(array[0] == .one); | ||
| 1086 | try expect(array[1] == .two); | ||
| 1087 | } |
test/behavior/enum_llvm.zig deleted-105| ... | @@ -1,105 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const mem = std.mem; | ||
| 4 | const Tag = std.meta.Tag; | ||
| 5 | |||
| 6 | test "@tagName" { | ||
| 7 | try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); | ||
| 8 | comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); | ||
| 9 | } | ||
| 10 | |||
| 11 | fn testEnumTagNameBare(n: anytype) []const u8 { | ||
| 12 | return @tagName(n); | ||
| 13 | } | ||
| 14 | |||
| 15 | const BareNumber = enum { One, Two, Three }; | ||
| 16 | |||
| 17 | test "@tagName non-exhaustive enum" { | ||
| 18 | try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); | ||
| 19 | comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); | ||
| 20 | } | ||
| 21 | const NonExhaustive = enum(u8) { A, B, _ }; | ||
| 22 | |||
| 23 | test "@tagName is null-terminated" { | ||
| 24 | const S = struct { | ||
| 25 | fn doTheTest(n: BareNumber) !void { | ||
| 26 | try expect(@tagName(n)[3] == 0); | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | try S.doTheTest(.Two); | ||
| 30 | try comptime S.doTheTest(.Two); | ||
| 31 | } | ||
| 32 | |||
| 33 | test "tag name with assigned enum values" { | ||
| 34 | const LocalFoo = enum(u8) { | ||
| 35 | A = 1, | ||
| 36 | B = 0, | ||
| 37 | }; | ||
| 38 | var b = LocalFoo.B; | ||
| 39 | try expect(mem.eql(u8, @tagName(b), "B")); | ||
| 40 | } | ||
| 41 | |||
| 42 | test "@tagName on enum literals" { | ||
| 43 | try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); | ||
| 44 | comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); | ||
| 45 | } | ||
| 46 | |||
| 47 | const Bar = enum { A, B, C, D }; | ||
| 48 | |||
| 49 | test "enum literal casting to optional" { | ||
| 50 | var bar: ?Bar = undefined; | ||
| 51 | bar = .B; | ||
| 52 | |||
| 53 | try expect(bar.? == Bar.B); | ||
| 54 | } | ||
| 55 | |||
| 56 | const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 }; | ||
| 57 | const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 }; | ||
| 58 | const C = enum(u2) { One4, Two4, Three4, Four4 }; | ||
| 59 | |||
| 60 | const BitFieldOfEnums = packed struct { | ||
| 61 | a: A, | ||
| 62 | b: B, | ||
| 63 | c: C, | ||
| 64 | }; | ||
| 65 | |||
| 66 | const bit_field_1 = BitFieldOfEnums{ | ||
| 67 | .a = A.Two, | ||
| 68 | .b = B.Three3, | ||
| 69 | .c = C.Four4, | ||
| 70 | }; | ||
| 71 | |||
| 72 | test "bit field access with enum fields" { | ||
| 73 | var data = bit_field_1; | ||
| 74 | try expect(getA(&data) == A.Two); | ||
| 75 | try expect(getB(&data) == B.Three3); | ||
| 76 | try expect(getC(&data) == C.Four4); | ||
| 77 | comptime try expect(@sizeOf(BitFieldOfEnums) == 1); | ||
| 78 | |||
| 79 | data.b = B.Four3; | ||
| 80 | try expect(data.b == B.Four3); | ||
| 81 | |||
| 82 | data.a = A.Three; | ||
| 83 | try expect(data.a == A.Three); | ||
| 84 | try expect(data.b == B.Four3); | ||
| 85 | } | ||
| 86 | |||
| 87 | fn getA(data: *const BitFieldOfEnums) A { | ||
| 88 | return data.a; | ||
| 89 | } | ||
| 90 | |||
| 91 | fn getB(data: *const BitFieldOfEnums) B { | ||
| 92 | return data.b; | ||
| 93 | } | ||
| 94 | |||
| 95 | fn getC(data: *const BitFieldOfEnums) C { | ||
| 96 | return data.c; | ||
| 97 | } | ||
| 98 | |||
| 99 | test "enum literal in array literal" { | ||
| 100 | const Items = enum { one, two }; | ||
| 101 | const array = [_]Items{ .one, .two }; | ||
| 102 | |||
| 103 | try expect(array[0] == .one); | ||
| 104 | try expect(array[1] == .two); | ||
| 105 | } | ||
test/behavior/pointers.zig+282| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const std = @import("std"); | 2 | const std = @import("std"); |
| 2 | const testing = std.testing; | 3 | const testing = std.testing; |
| 3 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| ... | @@ -97,3 +98,284 @@ test "C pointer comparison and arithmetic" { | ... | @@ -97,3 +98,284 @@ test "C pointer comparison and arithmetic" { |
| 97 | try S.doTheTest(); | 98 | try S.doTheTest(); |
| 98 | comptime try S.doTheTest(); | 99 | comptime try S.doTheTest(); |
| 99 | } | 100 | } |
| 101 | |||
| 102 | test "dereference pointer again" { | ||
| 103 | try testDerefPtrOneVal(); | ||
| 104 | comptime try testDerefPtrOneVal(); | ||
| 105 | } | ||
| 106 | |||
| 107 | const Foo1 = struct { | ||
| 108 | x: void, | ||
| 109 | }; | ||
| 110 | |||
| 111 | fn testDerefPtrOneVal() !void { | ||
| 112 | // Foo1 satisfies the OnePossibleValueYes criteria | ||
| 113 | const x = &Foo1{ .x = {} }; | ||
| 114 | const y = x.*; | ||
| 115 | try expect(@TypeOf(y.x) == void); | ||
| 116 | } | ||
| 117 | |||
| 118 | test "peer type resolution with C pointers" { | ||
| 119 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 120 | |||
| 121 | var ptr_one: *u8 = undefined; | ||
| 122 | var ptr_many: [*]u8 = undefined; | ||
| 123 | var ptr_c: [*c]u8 = undefined; | ||
| 124 | var t = true; | ||
| 125 | var x1 = if (t) ptr_one else ptr_c; | ||
| 126 | var x2 = if (t) ptr_many else ptr_c; | ||
| 127 | var x3 = if (t) ptr_c else ptr_one; | ||
| 128 | var x4 = if (t) ptr_c else ptr_many; | ||
| 129 | try expect(@TypeOf(x1) == [*c]u8); | ||
| 130 | try expect(@TypeOf(x2) == [*c]u8); | ||
| 131 | try expect(@TypeOf(x3) == [*c]u8); | ||
| 132 | try expect(@TypeOf(x4) == [*c]u8); | ||
| 133 | } | ||
| 134 | |||
| 135 | test "implicit casting between C pointer and optional non-C pointer" { | ||
| 136 | var slice: []const u8 = "aoeu"; | ||
| 137 | const opt_many_ptr: ?[*]const u8 = slice.ptr; | ||
| 138 | var ptr_opt_many_ptr = &opt_many_ptr; | ||
| 139 | var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; | ||
| 140 | try expect(c_ptr.*.* == 'a'); | ||
| 141 | ptr_opt_many_ptr = c_ptr; | ||
| 142 | try expect(ptr_opt_many_ptr.*.?[1] == 'o'); | ||
| 143 | } | ||
| 144 | |||
| 145 | test "implicit cast error unions with non-optional to optional pointer" { | ||
| 146 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 147 | |||
| 148 | const S = struct { | ||
| 149 | fn doTheTest() !void { | ||
| 150 | try expectError(error.Fail, foo()); | ||
| 151 | } | ||
| 152 | fn foo() anyerror!?*u8 { | ||
| 153 | return bar() orelse error.Fail; | ||
| 154 | } | ||
| 155 | fn bar() ?*u8 { | ||
| 156 | return null; | ||
| 157 | } | ||
| 158 | }; | ||
| 159 | try S.doTheTest(); | ||
| 160 | comptime try S.doTheTest(); | ||
| 161 | } | ||
| 162 | |||
| 163 | test "compare equality of optional and non-optional pointer" { | ||
| 164 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 165 | |||
| 166 | const a = @intToPtr(*const usize, 0x12345678); | ||
| 167 | const b = @intToPtr(?*usize, 0x12345678); | ||
| 168 | try expect(a == b); | ||
| 169 | try expect(b == a); | ||
| 170 | } | ||
| 171 | |||
| 172 | test "allowzero pointer and slice" { | ||
| 173 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 174 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 175 | |||
| 176 | var ptr = @intToPtr([*]allowzero i32, 0); | ||
| 177 | var opt_ptr: ?[*]allowzero i32 = ptr; | ||
| 178 | try expect(opt_ptr != null); | ||
| 179 | try expect(@ptrToInt(ptr) == 0); | ||
| 180 | var runtime_zero: usize = 0; | ||
| 181 | var slice = ptr[runtime_zero..10]; | ||
| 182 | comptime try expect(@TypeOf(slice) == []allowzero i32); | ||
| 183 | try expect(@ptrToInt(&slice[5]) == 20); | ||
| 184 | |||
| 185 | comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); | ||
| 186 | comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); | ||
| 187 | } | ||
| 188 | |||
| 189 | test "assign null directly to C pointer and test null equality" { | ||
| 190 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 191 | |||
| 192 | var x: [*c]i32 = null; | ||
| 193 | try expect(x == null); | ||
| 194 | try expect(null == x); | ||
| 195 | try expect(!(x != null)); | ||
| 196 | try expect(!(null != x)); | ||
| 197 | if (x) |same_x| { | ||
| 198 | _ = same_x; | ||
| 199 | @panic("fail"); | ||
| 200 | } | ||
| 201 | var otherx: i32 = undefined; | ||
| 202 | try expect((x orelse &otherx) == &otherx); | ||
| 203 | |||
| 204 | const y: [*c]i32 = null; | ||
| 205 | comptime try expect(y == null); | ||
| 206 | comptime try expect(null == y); | ||
| 207 | comptime try expect(!(y != null)); | ||
| 208 | comptime try expect(!(null != y)); | ||
| 209 | if (y) |same_y| { | ||
| 210 | _ = same_y; | ||
| 211 | @panic("fail"); | ||
| 212 | } | ||
| 213 | const othery: i32 = undefined; | ||
| 214 | comptime try expect((y orelse &othery) == &othery); | ||
| 215 | |||
| 216 | var n: i32 = 1234; | ||
| 217 | var x1: [*c]i32 = &n; | ||
| 218 | try expect(!(x1 == null)); | ||
| 219 | try expect(!(null == x1)); | ||
| 220 | try expect(x1 != null); | ||
| 221 | try expect(null != x1); | ||
| 222 | try expect(x1.?.* == 1234); | ||
| 223 | if (x1) |same_x1| { | ||
| 224 | try expect(same_x1.* == 1234); | ||
| 225 | } else { | ||
| 226 | @panic("fail"); | ||
| 227 | } | ||
| 228 | try expect((x1 orelse &otherx) == x1); | ||
| 229 | |||
| 230 | const nc: i32 = 1234; | ||
| 231 | const y1: [*c]const i32 = &nc; | ||
| 232 | comptime try expect(!(y1 == null)); | ||
| 233 | comptime try expect(!(null == y1)); | ||
| 234 | comptime try expect(y1 != null); | ||
| 235 | comptime try expect(null != y1); | ||
| 236 | comptime try expect(y1.?.* == 1234); | ||
| 237 | if (y1) |same_y1| { | ||
| 238 | try expect(same_y1.* == 1234); | ||
| 239 | } else { | ||
| 240 | @compileError("fail"); | ||
| 241 | } | ||
| 242 | comptime try expect((y1 orelse &othery) == y1); | ||
| 243 | } | ||
| 244 | |||
| 245 | test "null terminated pointer" { | ||
| 246 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 247 | |||
| 248 | const S = struct { | ||
| 249 | fn doTheTest() !void { | ||
| 250 | var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' }; | ||
| 251 | var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero); | ||
| 252 | var no_zero_ptr: [*]const u8 = zero_ptr; | ||
| 253 | var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr); | ||
| 254 | try expect(std.mem.eql(u8, std.mem.sliceTo(zero_ptr_again, 0), "hello")); | ||
| 255 | } | ||
| 256 | }; | ||
| 257 | try S.doTheTest(); | ||
| 258 | comptime try S.doTheTest(); | ||
| 259 | } | ||
| 260 | |||
| 261 | test "allow any sentinel" { | ||
| 262 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 263 | |||
| 264 | const S = struct { | ||
| 265 | fn doTheTest() !void { | ||
| 266 | var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 }; | ||
| 267 | var ptr: [*:std.math.minInt(i32)]i32 = &array; | ||
| 268 | try expect(ptr[4] == std.math.minInt(i32)); | ||
| 269 | } | ||
| 270 | }; | ||
| 271 | try S.doTheTest(); | ||
| 272 | comptime try S.doTheTest(); | ||
| 273 | } | ||
| 274 | |||
| 275 | test "pointer sentinel with enums" { | ||
| 276 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 277 | |||
| 278 | const S = struct { | ||
| 279 | const Number = enum { | ||
| 280 | one, | ||
| 281 | two, | ||
| 282 | sentinel, | ||
| 283 | }; | ||
| 284 | |||
| 285 | fn doTheTest() !void { | ||
| 286 | var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one }; | ||
| 287 | try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731 | ||
| 288 | } | ||
| 289 | }; | ||
| 290 | try S.doTheTest(); | ||
| 291 | comptime try S.doTheTest(); | ||
| 292 | } | ||
| 293 | |||
| 294 | test "pointer sentinel with optional element" { | ||
| 295 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 296 | |||
| 297 | const S = struct { | ||
| 298 | fn doTheTest() !void { | ||
| 299 | var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 }; | ||
| 300 | try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731 | ||
| 301 | } | ||
| 302 | }; | ||
| 303 | try S.doTheTest(); | ||
| 304 | comptime try S.doTheTest(); | ||
| 305 | } | ||
| 306 | |||
| 307 | test "pointer sentinel with +inf" { | ||
| 308 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 309 | |||
| 310 | const S = struct { | ||
| 311 | fn doTheTest() !void { | ||
| 312 | const inf = std.math.inf_f32; | ||
| 313 | var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 }; | ||
| 314 | try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731 | ||
| 315 | } | ||
| 316 | }; | ||
| 317 | try S.doTheTest(); | ||
| 318 | comptime try S.doTheTest(); | ||
| 319 | } | ||
| 320 | |||
| 321 | test "pointer to array at fixed address" { | ||
| 322 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 323 | |||
| 324 | const array = @intToPtr(*volatile [1]u32, 0x10); | ||
| 325 | // Silly check just to reference `array` | ||
| 326 | try expect(@ptrToInt(&array[0]) == 0x10); | ||
| 327 | } | ||
| 328 | |||
| 329 | test "pointer arithmetic affects the alignment" { | ||
| 330 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 331 | |||
| 332 | { | ||
| 333 | var ptr: [*]align(8) u32 = undefined; | ||
| 334 | var x: usize = 1; | ||
| 335 | |||
| 336 | try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); | ||
| 337 | const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 | ||
| 338 | try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); | ||
| 339 | const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 | ||
| 340 | try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); | ||
| 341 | const ptr3 = ptr + 0; // no-op | ||
| 342 | try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); | ||
| 343 | const ptr4 = ptr + x; // runtime-known addend | ||
| 344 | try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); | ||
| 345 | } | ||
| 346 | { | ||
| 347 | var ptr: [*]align(8) [3]u8 = undefined; | ||
| 348 | var x: usize = 1; | ||
| 349 | |||
| 350 | const ptr1 = ptr + 17; // 3 * 17 = 51 | ||
| 351 | try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); | ||
| 352 | const ptr2 = ptr + x; // runtime-known addend | ||
| 353 | try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); | ||
| 354 | const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 | ||
| 355 | try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); | ||
| 356 | const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 | ||
| 357 | try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | test "@ptrToInt on null optional at comptime" { | ||
| 362 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 363 | |||
| 364 | { | ||
| 365 | const pointer = @intToPtr(?*u8, 0x000); | ||
| 366 | const x = @ptrToInt(pointer); | ||
| 367 | _ = x; | ||
| 368 | comptime try expect(0 == @ptrToInt(pointer)); | ||
| 369 | } | ||
| 370 | { | ||
| 371 | const pointer = @intToPtr(?*u8, 0xf00); | ||
| 372 | comptime try expect(0xf00 == @ptrToInt(pointer)); | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 376 | test "indexing array with sentinel returns correct type" { | ||
| 377 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 378 | |||
| 379 | var s: [:0]const u8 = "abc"; | ||
| 380 | try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); | ||
| 381 | } |
test/behavior/pointers_stage1.zig deleted-256| ... | @@ -1,256 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const expect = testing.expect; | ||
| 4 | const expectError = testing.expectError; | ||
| 5 | |||
| 6 | const Foo1 = struct { | ||
| 7 | x: void, | ||
| 8 | }; | ||
| 9 | |||
| 10 | test "dereference pointer again" { | ||
| 11 | try testDerefPtrOneVal(); | ||
| 12 | comptime try testDerefPtrOneVal(); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn testDerefPtrOneVal() !void { | ||
| 16 | // Foo1 satisfies the OnePossibleValueYes criteria | ||
| 17 | const x = &Foo1{ .x = {} }; | ||
| 18 | const y = x.*; | ||
| 19 | try expect(@TypeOf(y.x) == void); | ||
| 20 | } | ||
| 21 | |||
| 22 | test "peer type resolution with C pointers" { | ||
| 23 | var ptr_one: *u8 = undefined; | ||
| 24 | var ptr_many: [*]u8 = undefined; | ||
| 25 | var ptr_c: [*c]u8 = undefined; | ||
| 26 | var t = true; | ||
| 27 | var x1 = if (t) ptr_one else ptr_c; | ||
| 28 | var x2 = if (t) ptr_many else ptr_c; | ||
| 29 | var x3 = if (t) ptr_c else ptr_one; | ||
| 30 | var x4 = if (t) ptr_c else ptr_many; | ||
| 31 | try expect(@TypeOf(x1) == [*c]u8); | ||
| 32 | try expect(@TypeOf(x2) == [*c]u8); | ||
| 33 | try expect(@TypeOf(x3) == [*c]u8); | ||
| 34 | try expect(@TypeOf(x4) == [*c]u8); | ||
| 35 | } | ||
| 36 | |||
| 37 | test "implicit casting between C pointer and optional non-C pointer" { | ||
| 38 | var slice: []const u8 = "aoeu"; | ||
| 39 | const opt_many_ptr: ?[*]const u8 = slice.ptr; | ||
| 40 | var ptr_opt_many_ptr = &opt_many_ptr; | ||
| 41 | var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; | ||
| 42 | try expect(c_ptr.*.* == 'a'); | ||
| 43 | ptr_opt_many_ptr = c_ptr; | ||
| 44 | try expect(ptr_opt_many_ptr.*.?[1] == 'o'); | ||
| 45 | } | ||
| 46 | |||
| 47 | test "implicit cast error unions with non-optional to optional pointer" { | ||
| 48 | const S = struct { | ||
| 49 | fn doTheTest() !void { | ||
| 50 | try expectError(error.Fail, foo()); | ||
| 51 | } | ||
| 52 | fn foo() anyerror!?*u8 { | ||
| 53 | return bar() orelse error.Fail; | ||
| 54 | } | ||
| 55 | fn bar() ?*u8 { | ||
| 56 | return null; | ||
| 57 | } | ||
| 58 | }; | ||
| 59 | try S.doTheTest(); | ||
| 60 | comptime try S.doTheTest(); | ||
| 61 | } | ||
| 62 | |||
| 63 | test "compare equality of optional and non-optional pointer" { | ||
| 64 | const a = @intToPtr(*const usize, 0x12345678); | ||
| 65 | const b = @intToPtr(?*usize, 0x12345678); | ||
| 66 | try expect(a == b); | ||
| 67 | try expect(b == a); | ||
| 68 | } | ||
| 69 | |||
| 70 | test "allowzero pointer and slice" { | ||
| 71 | var ptr = @intToPtr([*]allowzero i32, 0); | ||
| 72 | var opt_ptr: ?[*]allowzero i32 = ptr; | ||
| 73 | try expect(opt_ptr != null); | ||
| 74 | try expect(@ptrToInt(ptr) == 0); | ||
| 75 | var runtime_zero: usize = 0; | ||
| 76 | var slice = ptr[runtime_zero..10]; | ||
| 77 | comptime try expect(@TypeOf(slice) == []allowzero i32); | ||
| 78 | try expect(@ptrToInt(&slice[5]) == 20); | ||
| 79 | |||
| 80 | comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); | ||
| 81 | comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); | ||
| 82 | } | ||
| 83 | |||
| 84 | test "assign null directly to C pointer and test null equality" { | ||
| 85 | var x: [*c]i32 = null; | ||
| 86 | try expect(x == null); | ||
| 87 | try expect(null == x); | ||
| 88 | try expect(!(x != null)); | ||
| 89 | try expect(!(null != x)); | ||
| 90 | if (x) |same_x| { | ||
| 91 | _ = same_x; | ||
| 92 | @panic("fail"); | ||
| 93 | } | ||
| 94 | var otherx: i32 = undefined; | ||
| 95 | try expect((x orelse &otherx) == &otherx); | ||
| 96 | |||
| 97 | const y: [*c]i32 = null; | ||
| 98 | comptime try expect(y == null); | ||
| 99 | comptime try expect(null == y); | ||
| 100 | comptime try expect(!(y != null)); | ||
| 101 | comptime try expect(!(null != y)); | ||
| 102 | if (y) |same_y| { | ||
| 103 | _ = same_y; | ||
| 104 | @panic("fail"); | ||
| 105 | } | ||
| 106 | const othery: i32 = undefined; | ||
| 107 | comptime try expect((y orelse &othery) == &othery); | ||
| 108 | |||
| 109 | var n: i32 = 1234; | ||
| 110 | var x1: [*c]i32 = &n; | ||
| 111 | try expect(!(x1 == null)); | ||
| 112 | try expect(!(null == x1)); | ||
| 113 | try expect(x1 != null); | ||
| 114 | try expect(null != x1); | ||
| 115 | try expect(x1.?.* == 1234); | ||
| 116 | if (x1) |same_x1| { | ||
| 117 | try expect(same_x1.* == 1234); | ||
| 118 | } else { | ||
| 119 | @panic("fail"); | ||
| 120 | } | ||
| 121 | try expect((x1 orelse &otherx) == x1); | ||
| 122 | |||
| 123 | const nc: i32 = 1234; | ||
| 124 | const y1: [*c]const i32 = &nc; | ||
| 125 | comptime try expect(!(y1 == null)); | ||
| 126 | comptime try expect(!(null == y1)); | ||
| 127 | comptime try expect(y1 != null); | ||
| 128 | comptime try expect(null != y1); | ||
| 129 | comptime try expect(y1.?.* == 1234); | ||
| 130 | if (y1) |same_y1| { | ||
| 131 | try expect(same_y1.* == 1234); | ||
| 132 | } else { | ||
| 133 | @compileError("fail"); | ||
| 134 | } | ||
| 135 | comptime try expect((y1 orelse &othery) == y1); | ||
| 136 | } | ||
| 137 | |||
| 138 | test "null terminated pointer" { | ||
| 139 | const S = struct { | ||
| 140 | fn doTheTest() !void { | ||
| 141 | var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' }; | ||
| 142 | var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero); | ||
| 143 | var no_zero_ptr: [*]const u8 = zero_ptr; | ||
| 144 | var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr); | ||
| 145 | try expect(std.mem.eql(u8, std.mem.sliceTo(zero_ptr_again, 0), "hello")); | ||
| 146 | } | ||
| 147 | }; | ||
| 148 | try S.doTheTest(); | ||
| 149 | comptime try S.doTheTest(); | ||
| 150 | } | ||
| 151 | |||
| 152 | test "allow any sentinel" { | ||
| 153 | const S = struct { | ||
| 154 | fn doTheTest() !void { | ||
| 155 | var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 }; | ||
| 156 | var ptr: [*:std.math.minInt(i32)]i32 = &array; | ||
| 157 | try expect(ptr[4] == std.math.minInt(i32)); | ||
| 158 | } | ||
| 159 | }; | ||
| 160 | try S.doTheTest(); | ||
| 161 | comptime try S.doTheTest(); | ||
| 162 | } | ||
| 163 | |||
| 164 | test "pointer sentinel with enums" { | ||
| 165 | const S = struct { | ||
| 166 | const Number = enum { | ||
| 167 | one, | ||
| 168 | two, | ||
| 169 | sentinel, | ||
| 170 | }; | ||
| 171 | |||
| 172 | fn doTheTest() !void { | ||
| 173 | var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one }; | ||
| 174 | try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731 | ||
| 175 | } | ||
| 176 | }; | ||
| 177 | try S.doTheTest(); | ||
| 178 | comptime try S.doTheTest(); | ||
| 179 | } | ||
| 180 | |||
| 181 | test "pointer sentinel with optional element" { | ||
| 182 | const S = struct { | ||
| 183 | fn doTheTest() !void { | ||
| 184 | var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 }; | ||
| 185 | try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731 | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | try S.doTheTest(); | ||
| 189 | comptime try S.doTheTest(); | ||
| 190 | } | ||
| 191 | |||
| 192 | test "pointer sentinel with +inf" { | ||
| 193 | const S = struct { | ||
| 194 | fn doTheTest() !void { | ||
| 195 | const inf = std.math.inf_f32; | ||
| 196 | var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 }; | ||
| 197 | try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731 | ||
| 198 | } | ||
| 199 | }; | ||
| 200 | try S.doTheTest(); | ||
| 201 | comptime try S.doTheTest(); | ||
| 202 | } | ||
| 203 | |||
| 204 | test "pointer to array at fixed address" { | ||
| 205 | const array = @intToPtr(*volatile [1]u32, 0x10); | ||
| 206 | // Silly check just to reference `array` | ||
| 207 | try expect(@ptrToInt(&array[0]) == 0x10); | ||
| 208 | } | ||
| 209 | |||
| 210 | test "pointer arithmetic affects the alignment" { | ||
| 211 | { | ||
| 212 | var ptr: [*]align(8) u32 = undefined; | ||
| 213 | var x: usize = 1; | ||
| 214 | |||
| 215 | try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); | ||
| 216 | const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 | ||
| 217 | try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); | ||
| 218 | const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 | ||
| 219 | try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); | ||
| 220 | const ptr3 = ptr + 0; // no-op | ||
| 221 | try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); | ||
| 222 | const ptr4 = ptr + x; // runtime-known addend | ||
| 223 | try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); | ||
| 224 | } | ||
| 225 | { | ||
| 226 | var ptr: [*]align(8) [3]u8 = undefined; | ||
| 227 | var x: usize = 1; | ||
| 228 | |||
| 229 | const ptr1 = ptr + 17; // 3 * 17 = 51 | ||
| 230 | try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); | ||
| 231 | const ptr2 = ptr + x; // runtime-known addend | ||
| 232 | try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); | ||
| 233 | const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 | ||
| 234 | try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); | ||
| 235 | const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 | ||
| 236 | try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | test "@ptrToInt on null optional at comptime" { | ||
| 241 | { | ||
| 242 | const pointer = @intToPtr(?*u8, 0x000); | ||
| 243 | const x = @ptrToInt(pointer); | ||
| 244 | _ = x; | ||
| 245 | comptime try expect(0 == @ptrToInt(pointer)); | ||
| 246 | } | ||
| 247 | { | ||
| 248 | const pointer = @intToPtr(?*u8, 0xf00); | ||
| 249 | comptime try expect(0xf00 == @ptrToInt(pointer)); | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | test "indexing array with sentinel returns correct type" { | ||
| 254 | var s: [:0]const u8 = "abc"; | ||
| 255 | try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); | ||
| 256 | } | ||
test/behavior/ptrcast.zig+77| ... | @@ -2,3 +2,80 @@ const std = @import("std"); | ... | @@ -2,3 +2,80 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const native_endian = builtin.target.cpu.arch.endian(); | 4 | const native_endian = builtin.target.cpu.arch.endian(); |
| 5 | |||
| 6 | test "reinterpret bytes as integer with nonzero offset" { | ||
| 7 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 8 | |||
| 9 | try testReinterpretBytesAsInteger(); | ||
| 10 | comptime try testReinterpretBytesAsInteger(); | ||
| 11 | } | ||
| 12 | |||
| 13 | fn testReinterpretBytesAsInteger() !void { | ||
| 14 | const bytes = "\x12\x34\x56\x78\xab"; | ||
| 15 | const expected = switch (native_endian) { | ||
| 16 | .Little => 0xab785634, | ||
| 17 | .Big => 0x345678ab, | ||
| 18 | }; | ||
| 19 | try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); | ||
| 20 | } | ||
| 21 | |||
| 22 | test "reinterpret bytes of an array into an extern struct" { | ||
| 23 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 24 | |||
| 25 | try testReinterpretBytesAsExternStruct(); | ||
| 26 | comptime try testReinterpretBytesAsExternStruct(); | ||
| 27 | } | ||
| 28 | |||
| 29 | fn testReinterpretBytesAsExternStruct() !void { | ||
| 30 | var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; | ||
| 31 | |||
| 32 | const S = extern struct { | ||
| 33 | a: u8, | ||
| 34 | b: u16, | ||
| 35 | c: u8, | ||
| 36 | }; | ||
| 37 | |||
| 38 | var ptr = @ptrCast(*const S, &bytes); | ||
| 39 | var val = ptr.c; | ||
| 40 | try expect(val == 5); | ||
| 41 | } | ||
| 42 | |||
| 43 | test "reinterpret struct field at comptime" { | ||
| 44 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 45 | |||
| 46 | const numNative = comptime Bytes.init(0x12345678); | ||
| 47 | if (native_endian != .Little) { | ||
| 48 | try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes)); | ||
| 49 | } else { | ||
| 50 | try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes)); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | const Bytes = struct { | ||
| 55 | bytes: [4]u8, | ||
| 56 | |||
| 57 | pub fn init(v: u32) Bytes { | ||
| 58 | var res: Bytes = undefined; | ||
| 59 | @ptrCast(*align(1) u32, &res.bytes).* = v; | ||
| 60 | |||
| 61 | return res; | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | test "comptime ptrcast keeps larger alignment" { | ||
| 66 | comptime { | ||
| 67 | const a: u32 = 1234; | ||
| 68 | const p = @ptrCast([*]const u8, &a); | ||
| 69 | try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | test "implicit optional pointer to optional anyopaque pointer" { | ||
| 74 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 75 | |||
| 76 | var buf: [4]u8 = "aoeu".*; | ||
| 77 | var x: ?[*]u8 = &buf; | ||
| 78 | var y: ?*anyopaque = x; | ||
| 79 | var z = @ptrCast(*[4]u8, y); | ||
| 80 | try expect(std.mem.eql(u8, z, "aoeu")); | ||
| 81 | } |
test/behavior/ptrcast_stage1.zig deleted-73| ... | @@ -1,73 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 5 | |||
| 6 | test "reinterpret bytes as integer with nonzero offset" { | ||
| 7 | try testReinterpretBytesAsInteger(); | ||
| 8 | comptime try testReinterpretBytesAsInteger(); | ||
| 9 | } | ||
| 10 | |||
| 11 | fn testReinterpretBytesAsInteger() !void { | ||
| 12 | const bytes = "\x12\x34\x56\x78\xab"; | ||
| 13 | const expected = switch (native_endian) { | ||
| 14 | .Little => 0xab785634, | ||
| 15 | .Big => 0x345678ab, | ||
| 16 | }; | ||
| 17 | try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); | ||
| 18 | } | ||
| 19 | |||
| 20 | test "reinterpret bytes of an array into an extern struct" { | ||
| 21 | try testReinterpretBytesAsExternStruct(); | ||
| 22 | comptime try testReinterpretBytesAsExternStruct(); | ||
| 23 | } | ||
| 24 | |||
| 25 | fn testReinterpretBytesAsExternStruct() !void { | ||
| 26 | var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; | ||
| 27 | |||
| 28 | const S = extern struct { | ||
| 29 | a: u8, | ||
| 30 | b: u16, | ||
| 31 | c: u8, | ||
| 32 | }; | ||
| 33 | |||
| 34 | var ptr = @ptrCast(*const S, &bytes); | ||
| 35 | var val = ptr.c; | ||
| 36 | try expect(val == 5); | ||
| 37 | } | ||
| 38 | |||
| 39 | test "reinterpret struct field at comptime" { | ||
| 40 | const numNative = comptime Bytes.init(0x12345678); | ||
| 41 | if (native_endian != .Little) { | ||
| 42 | try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes)); | ||
| 43 | } else { | ||
| 44 | try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes)); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | const Bytes = struct { | ||
| 49 | bytes: [4]u8, | ||
| 50 | |||
| 51 | pub fn init(v: u32) Bytes { | ||
| 52 | var res: Bytes = undefined; | ||
| 53 | @ptrCast(*align(1) u32, &res.bytes).* = v; | ||
| 54 | |||
| 55 | return res; | ||
| 56 | } | ||
| 57 | }; | ||
| 58 | |||
| 59 | test "comptime ptrcast keeps larger alignment" { | ||
| 60 | comptime { | ||
| 61 | const a: u32 = 1234; | ||
| 62 | const p = @ptrCast([*]const u8, &a); | ||
| 63 | try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | test "implicit optional pointer to optional anyopaque pointer" { | ||
| 68 | var buf: [4]u8 = "aoeu".*; | ||
| 69 | var x: ?[*]u8 = &buf; | ||
| 70 | var y: ?*anyopaque = x; | ||
| 71 | var z = @ptrCast(*[4]u8, y); | ||
| 72 | try expect(std.mem.eql(u8, z, "aoeu")); | ||
| 73 | } | ||