| 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 | 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 | 10443 | const gpa = sema.gpa; |
| 10438 | 10444 | // This logic must be synchronized with that in `zirStructInit`. |
| 10439 | 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 | 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 | 10492 | const gpa = sema.gpa; |
| 10482 | 10493 | const zir_datas = sema.code.instructions.items(.data); |
| 10483 | 10494 | const inst_data = zir_datas[inst].pl_node; |
| ... | ... | @@ -10612,10 +10623,6 @@ fn finishStructInit( |
| 10612 | 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 | 10626 | const is_comptime = for (field_inits) |field_init| { |
| 10620 | 10627 | if (!(try sema.isComptimeKnown(block, src, field_init))) { |
| 10621 | 10628 | break false; |
| ... | ... | @@ -10627,10 +10634,24 @@ fn finishStructInit( |
| 10627 | 10634 | for (field_inits) |field_init, i| { |
| 10628 | 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 | 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 | 10695 | } else null; |
| 10675 | 10696 | |
| 10676 | 10697 | const runtime_src = opt_runtime_src orelse { |
| 10677 | var anon_decl = try block.startAnonDecl(src); | |
| 10678 | defer anon_decl.deinit(); | |
| 10698 | const elem_vals = try sema.arena.alloc(Value, resolved_args.len); | |
| 10679 | 10699 | |
| 10680 | const elem_vals = try anon_decl.arena().alloc(Value, resolved_args.len); | |
| 10681 | 10700 | for (resolved_args) |arg, i| { |
| 10682 | 10701 | // We checked that all args are comptime above. |
| 10683 | const arg_val = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?; | |
| 10684 | elem_vals[i] = try arg_val.copy(anon_decl.arena()); | |
| 10702 | elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?; | |
| 10685 | 10703 | } |
| 10686 | 10704 | |
| 10687 | const val = try Value.Tag.array.create(anon_decl.arena(), elem_vals); | |
| 10688 | const decl = try anon_decl.finish(try array_ty.copy(anon_decl.arena()), val); | |
| 10689 | if (is_ref) { | |
| 10690 | return sema.analyzeDeclRef(decl); | |
| 10691 | } else { | |
| 10692 | return sema.analyzeDeclVal(block, .unneeded, decl); | |
| 10693 | } | |
| 10705 | const array_val = try Value.Tag.array.create(sema.arena, elem_vals); | |
| 10706 | return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref); | |
| 10694 | 10707 | }; |
| 10695 | 10708 | |
| 10696 | 10709 | try sema.requireRuntimeBlock(block, runtime_src); |
| 10697 | 10710 | try sema.resolveTypeLayout(block, src, elem_ty); |
| 10698 | 10711 | |
| 10699 | const alloc_ty = try Type.ptr(sema.arena, .{ | |
| 10700 | .pointee_type = array_ty, | |
| 10701 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 10702 | }); | |
| 10703 | const alloc = try block.addTy(.alloc, alloc_ty); | |
| 10712 | if (is_ref) { | |
| 10713 | const alloc_ty = try Type.ptr(sema.arena, .{ | |
| 10714 | .pointee_type = array_ty, | |
| 10715 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 10716 | }); | |
| 10717 | const alloc = try block.addTy(.alloc, alloc_ty); | |
| 10704 | 10718 | |
| 10705 | const elem_ptr_ty = try Type.ptr(sema.arena, .{ | |
| 10706 | .mutable = true, | |
| 10707 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 10708 | .pointee_type = elem_ty, | |
| 10709 | }); | |
| 10710 | const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); | |
| 10719 | const elem_ptr_ty = try Type.ptr(sema.arena, .{ | |
| 10720 | .mutable = true, | |
| 10721 | .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), | |
| 10722 | .pointee_type = elem_ty, | |
| 10723 | }); | |
| 10724 | const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); | |
| 10711 | 10725 | |
| 10712 | for (resolved_args) |arg, i| { | |
| 10713 | const index = try sema.addIntUnsigned(Type.u64, i); | |
| 10714 | const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); | |
| 10715 | _ = try block.addBinOp(.store, elem_ptr, arg); | |
| 10716 | } | |
| 10717 | if (is_ref) { | |
| 10726 | for (resolved_args) |arg, i| { | |
| 10727 | const index = try sema.addIntUnsigned(Type.u64, i); | |
| 10728 | const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); | |
| 10729 | _ = try block.addBinOp(.store, elem_ptr, arg); | |
| 10730 | } | |
| 10718 | 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 | 10737 | fn zirArrayInitAnon( |
| ... | ... | @@ -10758,17 +10771,11 @@ fn zirArrayInitAnon( |
| 10758 | 10771 | |
| 10759 | 10772 | const runtime_src = opt_runtime_src orelse { |
| 10760 | 10773 | const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); |
| 10761 | if (!is_ref) return sema.addConstant(tuple_ty, tuple_val); | |
| 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); | |
| 10774 | return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); | |
| 10770 | 10775 | }; |
| 10771 | 10776 | |
| 10777 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 10778 | ||
| 10772 | 10779 | if (is_ref) { |
| 10773 | 10780 | const alloc = try block.addTy(.alloc, tuple_ty); |
| 10774 | 10781 | for (operands) |operand, i_usize| { |
| ... | ... | @@ -10790,10 +10797,28 @@ fn zirArrayInitAnon( |
| 10790 | 10797 | element_refs[i] = sema.resolveInst(operand); |
| 10791 | 10798 | } |
| 10792 | 10799 | |
| 10793 | try sema.requireRuntimeBlock(block, runtime_src); | |
| 10794 | 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 | 10822 | fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 10798 | 10823 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 10799 | 10824 | const src = inst_data.src(); |
| ... | ... | @@ -13444,18 +13469,30 @@ fn structFieldPtr( |
| 13444 | 13469 | field_name_src: LazySrcLoc, |
| 13445 | 13470 | unresolved_struct_ty: Type, |
| 13446 | 13471 | ) CompileError!Air.Inst.Ref { |
| 13447 | const arena = sema.arena; | |
| 13448 | 13472 | assert(unresolved_struct_ty.zigTypeTag() == .Struct); |
| 13449 | 13473 | |
| 13450 | const struct_ptr_ty = sema.typeOf(struct_ptr); | |
| 13451 | 13474 | const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); |
| 13452 | 13475 | const struct_obj = struct_ty.castTag(.@"struct").?.data; |
| 13453 | 13476 | |
| 13454 | 13477 | const field_index_big = struct_obj.fields.getIndex(field_name) orelse |
| 13455 | 13478 | return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); |
| 13456 | 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 | 13493 | const field = struct_obj.fields.values()[field_index]; |
| 13458 | 13494 | |
| 13495 | const struct_ptr_ty = sema.typeOf(struct_ptr); | |
| 13459 | 13496 | var ptr_ty_data: Type.Payload.Pointer.Data = .{ |
| 13460 | 13497 | .pointee_type = field.ty, |
| 13461 | 13498 | .mutable = struct_ptr_ty.ptrIsMutable(), |
| ... | ... | @@ -13470,7 +13507,7 @@ fn structFieldPtr( |
| 13470 | 13507 | var offset: u64 = 0; |
| 13471 | 13508 | var running_bits: u16 = 0; |
| 13472 | 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 | 13512 | const field_align = f.packedAlignment(); |
| 13476 | 13513 | if (field_align == 0) { |
| ... | ... | @@ -13509,12 +13546,12 @@ fn structFieldPtr( |
| 13509 | 13546 | const int_ty: Type = .{ .ptr_otherwise = &int_payload.base }; |
| 13510 | 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 | 13551 | if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { |
| 13515 | 13552 | return sema.addConstant( |
| 13516 | 13553 | ptr_field_ty, |
| 13517 | try Value.Tag.field_ptr.create(arena, .{ | |
| 13554 | try Value.Tag.field_ptr.create(sema.arena, .{ | |
| 13518 | 13555 | .container_ptr = struct_ptr_val, |
| 13519 | 13556 | .field_index = field_index, |
| 13520 | 13557 | }), |
| ... | ... | @@ -13546,6 +13583,9 @@ fn structFieldVal( |
| 13546 | 13583 | |
| 13547 | 13584 | if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { |
| 13548 | 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 | 13590 | const field_values = struct_val.castTag(.@"struct").?.data; |
| 13551 | 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 | 1530 | const b_tag = b.tag(); |
| 1531 | 1531 | assert(a_tag != .undef); |
| 1532 | 1532 | assert(b_tag != .undef); |
| 1533 | if (a_tag == b_tag) { | |
| 1534 | switch (a_tag) { | |
| 1535 | .void_value, .null_value, .the_only_possible_value => return true, | |
| 1536 | .enum_literal => { | |
| 1537 | const a_name = a.castTag(.enum_literal).?.data; | |
| 1538 | const b_name = b.castTag(.enum_literal).?.data; | |
| 1539 | return std.mem.eql(u8, a_name, b_name); | |
| 1540 | }, | |
| 1541 | .enum_field_index => { | |
| 1542 | const a_field_index = a.castTag(.enum_field_index).?.data; | |
| 1543 | const b_field_index = b.castTag(.enum_field_index).?.data; | |
| 1544 | return a_field_index == b_field_index; | |
| 1545 | }, | |
| 1546 | .opt_payload => { | |
| 1547 | const a_payload = a.castTag(.opt_payload).?.data; | |
| 1548 | const b_payload = b.castTag(.opt_payload).?.data; | |
| 1549 | var buffer: Type.Payload.ElemType = undefined; | |
| 1550 | return eql(a_payload, b_payload, ty.optionalChild(&buffer)); | |
| 1551 | }, | |
| 1552 | .slice => { | |
| 1553 | const a_payload = a.castTag(.slice).?.data; | |
| 1554 | const b_payload = b.castTag(.slice).?.data; | |
| 1555 | if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; | |
| 1533 | if (a_tag == b_tag) switch (a_tag) { | |
| 1534 | .void_value, .null_value, .the_only_possible_value => return true, | |
| 1535 | .enum_literal => { | |
| 1536 | const a_name = a.castTag(.enum_literal).?.data; | |
| 1537 | const b_name = b.castTag(.enum_literal).?.data; | |
| 1538 | return std.mem.eql(u8, a_name, b_name); | |
| 1539 | }, | |
| 1540 | .enum_field_index => { | |
| 1541 | const a_field_index = a.castTag(.enum_field_index).?.data; | |
| 1542 | const b_field_index = b.castTag(.enum_field_index).?.data; | |
| 1543 | return a_field_index == b_field_index; | |
| 1544 | }, | |
| 1545 | .opt_payload => { | |
| 1546 | const a_payload = a.castTag(.opt_payload).?.data; | |
| 1547 | const b_payload = b.castTag(.opt_payload).?.data; | |
| 1548 | var buffer: Type.Payload.ElemType = undefined; | |
| 1549 | return eql(a_payload, b_payload, ty.optionalChild(&buffer)); | |
| 1550 | }, | |
| 1551 | .slice => { | |
| 1552 | const a_payload = a.castTag(.slice).?.data; | |
| 1553 | const b_payload = b.castTag(.slice).?.data; | |
| 1554 | if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; | |
| 1556 | 1555 | |
| 1557 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 1558 | const ptr_ty = ty.slicePtrFieldType(&ptr_buf); | |
| 1556 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; | |
| 1557 | const ptr_ty = ty.slicePtrFieldType(&ptr_buf); | |
| 1559 | 1558 | |
| 1560 | return eql(a_payload.ptr, b_payload.ptr, ptr_ty); | |
| 1561 | }, | |
| 1562 | .elem_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1563 | .field_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1564 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1565 | .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1566 | .array => { | |
| 1567 | const a_array = a.castTag(.array).?.data; | |
| 1568 | const b_array = b.castTag(.array).?.data; | |
| 1569 | ||
| 1570 | if (a_array.len != b_array.len) return false; | |
| 1571 | ||
| 1572 | const elem_ty = ty.childType(); | |
| 1573 | for (a_array) |a_elem, i| { | |
| 1574 | const b_elem = b_array[i]; | |
| 1575 | ||
| 1576 | if (!eql(a_elem, b_elem, elem_ty)) return false; | |
| 1577 | } | |
| 1578 | return true; | |
| 1579 | }, | |
| 1580 | .function => { | |
| 1581 | const a_payload = a.castTag(.function).?.data; | |
| 1582 | const b_payload = b.castTag(.function).?.data; | |
| 1583 | return a_payload == b_payload; | |
| 1584 | }, | |
| 1585 | else => {}, | |
| 1586 | } | |
| 1559 | return eql(a_payload.ptr, b_payload.ptr, ptr_ty); | |
| 1560 | }, | |
| 1561 | .elem_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1562 | .field_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1563 | .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1564 | .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), | |
| 1565 | .array => { | |
| 1566 | const a_array = a.castTag(.array).?.data; | |
| 1567 | const b_array = b.castTag(.array).?.data; | |
| 1568 | ||
| 1569 | if (a_array.len != b_array.len) return false; | |
| 1570 | ||
| 1571 | const elem_ty = ty.childType(); | |
| 1572 | for (a_array) |a_elem, i| { | |
| 1573 | const b_elem = b_array[i]; | |
| 1574 | ||
| 1575 | if (!eql(a_elem, b_elem, elem_ty)) return false; | |
| 1576 | } | |
| 1577 | return true; | |
| 1578 | }, | |
| 1579 | .function => { | |
| 1580 | const a_payload = a.castTag(.function).?.data; | |
| 1581 | const b_payload = b.castTag(.function).?.data; | |
| 1582 | return a_payload == b_payload; | |
| 1583 | }, | |
| 1584 | .@"struct" => { | |
| 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 | 1596 | } else if (a_tag == .null_value or b_tag == .null_value) { |
| 1588 | 1597 | return false; |
| 1589 | 1598 | } |
| ... | ... | @@ -1628,6 +1637,13 @@ pub const Value = extern union { |
| 1628 | 1637 | } |
| 1629 | 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 | 1647 | else => return order(a, b).compare(.eq), |
| 1632 | 1648 | } |
| 1633 | 1649 | } |
| ... | ... | @@ -1651,31 +1667,13 @@ pub const Value = extern union { |
| 1651 | 1667 | var buf: ToTypeBuffer = undefined; |
| 1652 | 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 | 1670 | .Float, .ComptimeFloat => { |
| 1666 | 1671 | // TODO double check the lang spec. should we to bitwise hashing here, |
| 1667 | 1672 | // or a hash that normalizes the float value? |
| 1668 | 1673 | const float = val.toFloat(f128); |
| 1669 | 1674 | std.hash.autoHash(hasher, @bitCast(u128, float)); |
| 1670 | 1675 | }, |
| 1671 | .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 | ||
| 1676 | .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { | |
| 1679 | 1677 | .slice => { |
| 1680 | 1678 | const slice = val.castTag(.slice).?.data; |
| 1681 | 1679 | var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| ... | ... | @@ -1684,22 +1682,7 @@ pub const Value = extern union { |
| 1684 | 1682 | hash(slice.len, Type.usize, hasher); |
| 1685 | 1683 | }, |
| 1686 | 1684 | |
| 1687 | // For these, hash them as hash of a pointer to the decl, | |
| 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, | |
| 1685 | else => return hashPtr(val, hasher), | |
| 1703 | 1686 | }, |
| 1704 | 1687 | .Array, .Vector => { |
| 1705 | 1688 | const len = ty.arrayLen(); |
| ... | ... | @@ -1739,14 +1722,7 @@ pub const Value = extern union { |
| 1739 | 1722 | .Enum => { |
| 1740 | 1723 | var enum_space: Payload.U64 = undefined; |
| 1741 | 1724 | const int_val = val.enumToInt(ty, &enum_space); |
| 1742 | ||
| 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 | } | |
| 1725 | hashInt(int_val, hasher); | |
| 1750 | 1726 | }, |
| 1751 | 1727 | .Union => { |
| 1752 | 1728 | const union_obj = val.cast(Payload.Union).?.data; |
| ... | ... | @@ -1757,8 +1733,12 @@ pub const Value = extern union { |
| 1757 | 1733 | union_obj.val.hash(active_field_ty, hasher); |
| 1758 | 1734 | }, |
| 1759 | 1735 | .Fn => { |
| 1760 | const func = val.castTag(.function).?.data; | |
| 1761 | return std.hash.autoHash(hasher, func.owner_decl); | |
| 1736 | const func: *Module.Fn = val.castTag(.function).?.data; | |
| 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 | 1743 | .Frame => { |
| 1764 | 1744 | @panic("TODO implement hashing frame values"); |
| ... | ... | @@ -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 | 1866 | pub fn markReferencedDeclsAlive(val: Value) void { |
| 1828 | 1867 | switch (val.tag()) { |
| 1829 | 1868 | .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(), |
| ... | ... | @@ -1876,7 +1915,8 @@ pub const Value = extern union { |
| 1876 | 1915 | pub fn slicePtr(val: Value) Value { |
| 1877 | 1916 | return switch (val.tag()) { |
| 1878 | 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 | 1920 | else => unreachable, |
| 1881 | 1921 | }; |
| 1882 | 1922 | } |
test/behavior.zig-3| ... | ... | @@ -98,7 +98,6 @@ test { |
| 98 | 98 | _ = @import("behavior/bugs/3007.zig"); |
| 99 | 99 | _ = @import("behavior/bugs/9584.zig"); |
| 100 | 100 | _ = @import("behavior/cast_llvm.zig"); |
| 101 | _ = @import("behavior/enum_llvm.zig"); | |
| 102 | 101 | _ = @import("behavior/error_llvm.zig"); |
| 103 | 102 | _ = @import("behavior/eval.zig"); |
| 104 | 103 | _ = @import("behavior/floatop.zig"); |
| ... | ... | @@ -163,9 +162,7 @@ test { |
| 163 | 162 | _ = @import("behavior/muladd.zig"); |
| 164 | 163 | _ = @import("behavior/null_stage1.zig"); |
| 165 | 164 | _ = @import("behavior/optional_stage1.zig"); |
| 166 | _ = @import("behavior/pointers_stage1.zig"); | |
| 167 | 165 | _ = @import("behavior/popcount_stage1.zig"); |
| 168 | _ = @import("behavior/ptrcast_stage1.zig"); | |
| 169 | 166 | _ = @import("behavior/reflection.zig"); |
| 170 | 167 | _ = @import("behavior/saturating_arithmetic_stage1.zig"); |
| 171 | 168 | _ = @import("behavior/select.zig"); |
test/behavior/enum.zig+113| ... | ... | @@ -972,3 +972,116 @@ fn test3_2(f: Test3Foo) !void { |
| 972 | 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 | const builtin = @import("builtin"); | |
| 1 | 2 | const std = @import("std"); |
| 2 | 3 | const testing = std.testing; |
| 3 | 4 | const expect = testing.expect; |
| ... | ... | @@ -97,3 +98,284 @@ test "C pointer comparison and arithmetic" { |
| 97 | 98 | try S.doTheTest(); |
| 98 | 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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 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 | } |