From 82bd0ac572f14d1e3a13737f4daf00a1ee8041a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 26 Jan 2022 19:59:39 -0700 Subject: [PATCH] Sema: implement struct init is_ref=true 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. --- src/Sema.zig | 150 ++++++++++------ src/value.zig | 230 ++++++++++++++---------- test/behavior.zig | 3 - test/behavior/enum.zig | 113 ++++++++++++ test/behavior/enum_llvm.zig | 105 ----------- test/behavior/pointers.zig | 282 ++++++++++++++++++++++++++++++ test/behavior/pointers_stage1.zig | 256 --------------------------- test/behavior/ptrcast.zig | 77 ++++++++ test/behavior/ptrcast_stage1.zig | 73 -------- 9 files changed, 702 insertions(+), 587 deletions(-) delete mode 100644 test/behavior/enum_llvm.zig delete mode 100644 test/behavior/pointers_stage1.zig delete mode 100644 test/behavior/ptrcast_stage1.zig diff --git a/src/Sema.zig b/src/Sema.zig index 6fc4f85174..84907a2044 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10433,7 +10433,13 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } } -fn structInitEmpty(sema: *Sema, block: *Block, obj_ty: Type, dest_src: LazySrcLoc, init_src: LazySrcLoc) CompileError!Air.Inst.Ref { +fn structInitEmpty( + sema: *Sema, + block: *Block, + obj_ty: Type, + dest_src: LazySrcLoc, + init_src: LazySrcLoc, +) CompileError!Air.Inst.Ref { const gpa = sema.gpa; // This logic must be synchronized with that in `zirStructInit`. 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 return sema.fail(block, src, "TODO: Sema.zirUnionInitPtr", .{}); } -fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { +fn zirStructInit( + sema: *Sema, + block: *Block, + inst: Zir.Inst.Index, + is_ref: bool, +) CompileError!Air.Inst.Ref { const gpa = sema.gpa; const zir_datas = sema.code.instructions.items(.data); const inst_data = zir_datas[inst].pl_node; @@ -10612,10 +10623,6 @@ fn finishStructInit( return sema.failWithOwnedErrorMsg(msg); } - if (is_ref) { - return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true", .{}); - } - const is_comptime = for (field_inits) |field_init| { if (!(try sema.isComptimeKnown(block, src, field_init))) { break false; @@ -10627,10 +10634,24 @@ fn finishStructInit( for (field_inits) |field_init, i| { values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; } - return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values)); + const struct_val = try Value.Tag.@"struct".create(sema.arena, values); + return sema.addConstantMaybeRef(block, src, struct_ty, struct_val, is_ref); } - return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); + if (is_ref) { + const alloc = try block.addTy(.alloc, struct_ty); + for (field_inits) |field_init, i_usize| { + const i = @intCast(u32, i_usize); + const field_src = src; + const field_ptr = try sema.structFieldPtrByIndex(block, src, alloc, i, struct_obj, field_src); + try sema.storePtr(block, src, field_ptr, field_init); + } + + return alloc; + } + + try sema.requireRuntimeBlock(block, src); + return block.addVectorInit(struct_ty, field_inits); } fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { @@ -10674,51 +10695,43 @@ fn zirArrayInit( } else null; const runtime_src = opt_runtime_src orelse { - var anon_decl = try block.startAnonDecl(src); - defer anon_decl.deinit(); + const elem_vals = try sema.arena.alloc(Value, resolved_args.len); - const elem_vals = try anon_decl.arena().alloc(Value, resolved_args.len); for (resolved_args) |arg, i| { // We checked that all args are comptime above. - const arg_val = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?; - elem_vals[i] = try arg_val.copy(anon_decl.arena()); + elem_vals[i] = (sema.resolveMaybeUndefVal(block, src, arg) catch unreachable).?; } - const val = try Value.Tag.array.create(anon_decl.arena(), elem_vals); - const decl = try anon_decl.finish(try array_ty.copy(anon_decl.arena()), val); - if (is_ref) { - return sema.analyzeDeclRef(decl); - } else { - return sema.analyzeDeclVal(block, .unneeded, decl); - } + const array_val = try Value.Tag.array.create(sema.arena, elem_vals); + return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref); }; try sema.requireRuntimeBlock(block, runtime_src); try sema.resolveTypeLayout(block, src, elem_ty); - const alloc_ty = try Type.ptr(sema.arena, .{ - .pointee_type = array_ty, - .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), - }); - const alloc = try block.addTy(.alloc, alloc_ty); - - const elem_ptr_ty = try Type.ptr(sema.arena, .{ - .mutable = true, - .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), - .pointee_type = elem_ty, - }); - const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); - - for (resolved_args) |arg, i| { - const index = try sema.addIntUnsigned(Type.u64, i); - const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); - _ = try block.addBinOp(.store, elem_ptr, arg); - } if (is_ref) { + const alloc_ty = try Type.ptr(sema.arena, .{ + .pointee_type = array_ty, + .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), + }); + const alloc = try block.addTy(.alloc, alloc_ty); + + const elem_ptr_ty = try Type.ptr(sema.arena, .{ + .mutable = true, + .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local), + .pointee_type = elem_ty, + }); + const elem_ptr_ty_ref = try sema.addType(elem_ptr_ty); + + for (resolved_args) |arg, i| { + const index = try sema.addIntUnsigned(Type.u64, i); + const elem_ptr = try block.addPtrElemPtrTypeRef(alloc, index, elem_ptr_ty_ref); + _ = try block.addBinOp(.store, elem_ptr, arg); + } return alloc; - } else { - return sema.analyzeLoad(block, .unneeded, alloc, .unneeded); } + + return block.addVectorInit(array_ty, resolved_args); } fn zirArrayInitAnon( @@ -10758,17 +10771,11 @@ fn zirArrayInitAnon( const runtime_src = opt_runtime_src orelse { const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); - if (!is_ref) return sema.addConstant(tuple_ty, tuple_val); - - var anon_decl = try block.startAnonDecl(src); - defer anon_decl.deinit(); - const decl = try anon_decl.finish( - try tuple_ty.copy(anon_decl.arena()), - try tuple_val.copy(anon_decl.arena()), - ); - return sema.analyzeDeclRef(decl); + return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); }; + try sema.requireRuntimeBlock(block, runtime_src); + if (is_ref) { const alloc = try block.addTy(.alloc, tuple_ty); for (operands) |operand, i_usize| { @@ -10790,10 +10797,28 @@ fn zirArrayInitAnon( element_refs[i] = sema.resolveInst(operand); } - try sema.requireRuntimeBlock(block, runtime_src); return block.addVectorInit(tuple_ty, element_refs); } +fn addConstantMaybeRef( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + ty: Type, + val: Value, + is_ref: bool, +) !Air.Inst.Ref { + if (!is_ref) return sema.addConstant(ty, val); + + var anon_decl = try block.startAnonDecl(src); + defer anon_decl.deinit(); + const decl = try anon_decl.finish( + try ty.copy(anon_decl.arena()), + try val.copy(anon_decl.arena()), + ); + return sema.analyzeDeclRef(decl); +} + fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); @@ -13444,18 +13469,30 @@ fn structFieldPtr( field_name_src: LazySrcLoc, unresolved_struct_ty: Type, ) CompileError!Air.Inst.Ref { - const arena = sema.arena; assert(unresolved_struct_ty.zigTypeTag() == .Struct); - const struct_ptr_ty = sema.typeOf(struct_ptr); const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_ty); const struct_obj = struct_ty.castTag(.@"struct").?.data; const field_index_big = struct_obj.fields.getIndex(field_name) orelse return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name); const field_index = @intCast(u32, field_index_big); + + return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_obj, field_name_src); +} + +fn structFieldPtrByIndex( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + struct_ptr: Air.Inst.Ref, + field_index: u32, + struct_obj: *Module.Struct, + field_src: LazySrcLoc, +) CompileError!Air.Inst.Ref { const field = struct_obj.fields.values()[field_index]; + const struct_ptr_ty = sema.typeOf(struct_ptr); var ptr_ty_data: Type.Payload.Pointer.Data = .{ .pointee_type = field.ty, .mutable = struct_ptr_ty.ptrIsMutable(), @@ -13470,7 +13507,7 @@ fn structFieldPtr( var offset: u64 = 0; var running_bits: u16 = 0; for (struct_obj.fields.values()) |f, i| { - if (!(try sema.typeHasRuntimeBits(block, field_name_src, f.ty))) continue; + if (!(try sema.typeHasRuntimeBits(block, field_src, f.ty))) continue; const field_align = f.packedAlignment(); if (field_align == 0) { @@ -13509,12 +13546,12 @@ fn structFieldPtr( const int_ty: Type = .{ .ptr_otherwise = &int_payload.base }; ptr_ty_data.host_size = @intCast(u16, int_ty.abiSize(target)); } - const ptr_field_ty = try Type.ptr(arena, ptr_ty_data); + const ptr_field_ty = try Type.ptr(sema.arena, ptr_ty_data); if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { return sema.addConstant( ptr_field_ty, - try Value.Tag.field_ptr.create(arena, .{ + try Value.Tag.field_ptr.create(sema.arena, .{ .container_ptr = struct_ptr_val, .field_index = field_index, }), @@ -13546,6 +13583,9 @@ fn structFieldVal( if (try sema.resolveMaybeUndefVal(block, src, struct_byval)) |struct_val| { if (struct_val.isUndef()) return sema.addConstUndef(field.ty); + if ((try sema.typeHasOnePossibleValue(block, src, field.ty))) |opv| { + return sema.addConstant(field.ty, opv); + } const field_values = struct_val.castTag(.@"struct").?.data; return sema.addConstant(field.ty, field_values[field_index]); diff --git a/src/value.zig b/src/value.zig index 39cb1a4dbc..9d9895a6e0 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1530,60 +1530,69 @@ pub const Value = extern union { const b_tag = b.tag(); assert(a_tag != .undef); assert(b_tag != .undef); - if (a_tag == b_tag) { - switch (a_tag) { - .void_value, .null_value, .the_only_possible_value => return true, - .enum_literal => { - const a_name = a.castTag(.enum_literal).?.data; - const b_name = b.castTag(.enum_literal).?.data; - return std.mem.eql(u8, a_name, b_name); - }, - .enum_field_index => { - const a_field_index = a.castTag(.enum_field_index).?.data; - const b_field_index = b.castTag(.enum_field_index).?.data; - return a_field_index == b_field_index; - }, - .opt_payload => { - const a_payload = a.castTag(.opt_payload).?.data; - const b_payload = b.castTag(.opt_payload).?.data; - var buffer: Type.Payload.ElemType = undefined; - return eql(a_payload, b_payload, ty.optionalChild(&buffer)); - }, - .slice => { - const a_payload = a.castTag(.slice).?.data; - const b_payload = b.castTag(.slice).?.data; - if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; + if (a_tag == b_tag) switch (a_tag) { + .void_value, .null_value, .the_only_possible_value => return true, + .enum_literal => { + const a_name = a.castTag(.enum_literal).?.data; + const b_name = b.castTag(.enum_literal).?.data; + return std.mem.eql(u8, a_name, b_name); + }, + .enum_field_index => { + const a_field_index = a.castTag(.enum_field_index).?.data; + const b_field_index = b.castTag(.enum_field_index).?.data; + return a_field_index == b_field_index; + }, + .opt_payload => { + const a_payload = a.castTag(.opt_payload).?.data; + const b_payload = b.castTag(.opt_payload).?.data; + var buffer: Type.Payload.ElemType = undefined; + return eql(a_payload, b_payload, ty.optionalChild(&buffer)); + }, + .slice => { + const a_payload = a.castTag(.slice).?.data; + const b_payload = b.castTag(.slice).?.data; + if (!eql(a_payload.len, b_payload.len, Type.usize)) return false; - var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; - const ptr_ty = ty.slicePtrFieldType(&ptr_buf); + var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; + const ptr_ty = ty.slicePtrFieldType(&ptr_buf); - return eql(a_payload.ptr, b_payload.ptr, ptr_ty); - }, - .elem_ptr => @panic("TODO: Implement more pointer eql cases"), - .field_ptr => @panic("TODO: Implement more pointer eql cases"), - .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), - .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), - .array => { - const a_array = a.castTag(.array).?.data; - const b_array = b.castTag(.array).?.data; + return eql(a_payload.ptr, b_payload.ptr, ptr_ty); + }, + .elem_ptr => @panic("TODO: Implement more pointer eql cases"), + .field_ptr => @panic("TODO: Implement more pointer eql cases"), + .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"), + .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"), + .array => { + const a_array = a.castTag(.array).?.data; + const b_array = b.castTag(.array).?.data; - if (a_array.len != b_array.len) return false; + if (a_array.len != b_array.len) return false; - const elem_ty = ty.childType(); - for (a_array) |a_elem, i| { - const b_elem = b_array[i]; + const elem_ty = ty.childType(); + for (a_array) |a_elem, i| { + const b_elem = b_array[i]; - if (!eql(a_elem, b_elem, elem_ty)) return false; - } - return true; - }, - .function => { - const a_payload = a.castTag(.function).?.data; - const b_payload = b.castTag(.function).?.data; - return a_payload == b_payload; - }, - else => {}, - } + if (!eql(a_elem, b_elem, elem_ty)) return false; + } + return true; + }, + .function => { + const a_payload = a.castTag(.function).?.data; + const b_payload = b.castTag(.function).?.data; + return a_payload == b_payload; + }, + .@"struct" => { + const fields = ty.structFields().values(); + const a_field_vals = a.castTag(.@"struct").?.data; + const b_field_vals = b.castTag(.@"struct").?.data; + assert(a_field_vals.len == b_field_vals.len); + assert(fields.len == a_field_vals.len); + for (fields) |field, i| { + if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false; + } + return true; + }, + else => {}, } else if (a_tag == .null_value or b_tag == .null_value) { return false; } @@ -1628,6 +1637,13 @@ pub const Value = extern union { } return true; }, + .Struct => { + // must be a struct with no fields since we checked for if + // both have the struct tag above. + const fields = ty.structFields().values(); + assert(fields.len == 0); + return true; + }, else => return order(a, b).compare(.eq), } } @@ -1651,31 +1667,13 @@ pub const Value = extern union { var buf: ToTypeBuffer = undefined; return val.toType(&buf).hashWithHasher(hasher); }, - .Bool => { - std.hash.autoHash(hasher, val.toBool()); - }, - .Int, .ComptimeInt => { - var space: BigIntSpace = undefined; - const big = val.toBigInt(&space); - std.hash.autoHash(hasher, big.positive); - for (big.limbs) |limb| { - std.hash.autoHash(hasher, limb); - } - }, .Float, .ComptimeFloat => { // TODO double check the lang spec. should we to bitwise hashing here, // or a hash that normalizes the float value? const float = val.toFloat(f128); std.hash.autoHash(hasher, @bitCast(u128, float)); }, - .Pointer => switch (val.tag()) { - .decl_ref_mut, - .extern_fn, - .decl_ref, - .function, - .variable, - => std.hash.autoHash(hasher, val.pointerDecl().?), - + .Bool, .Int, .ComptimeInt, .Pointer => switch (val.tag()) { .slice => { const slice = val.castTag(.slice).?.data; var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined; @@ -1684,22 +1682,7 @@ pub const Value = extern union { hash(slice.len, Type.usize, hasher); }, - // For these, hash them as hash of a pointer to the decl, - // combined with a hash of the byte offset from the decl. - .elem_ptr => @panic("TODO: Implement more pointer hashing cases"), - .field_ptr => @panic("TODO: Implement more pointer hashing cases"), - .eu_payload_ptr => @panic("TODO: Implement more pointer hashing cases"), - .opt_payload_ptr => @panic("TODO: Implement more pointer hashing cases"), - - .zero, - .one, - .int_u64, - .int_i64, - .int_big_positive, - .int_big_negative, - => @panic("TODO: Implement pointer hashing for int pointers"), - - else => unreachable, + else => return hashPtr(val, hasher), }, .Array, .Vector => { const len = ty.arrayLen(); @@ -1739,14 +1722,7 @@ pub const Value = extern union { .Enum => { var enum_space: Payload.U64 = undefined; const int_val = val.enumToInt(ty, &enum_space); - - var space: BigIntSpace = undefined; - const big = int_val.toBigInt(&space); - - std.hash.autoHash(hasher, big.positive); - for (big.limbs) |limb| { - std.hash.autoHash(hasher, limb); - } + hashInt(int_val, hasher); }, .Union => { const union_obj = val.cast(Payload.Union).?.data; @@ -1757,8 +1733,12 @@ pub const Value = extern union { union_obj.val.hash(active_field_ty, hasher); }, .Fn => { - const func = val.castTag(.function).?.data; - return std.hash.autoHash(hasher, func.owner_decl); + const func: *Module.Fn = val.castTag(.function).?.data; + // Note that his hashes the *Fn rather than the *Decl. This is + // to differentiate function bodies from function pointers. + // This is currently redundant since we already hash the zig type tag + // at the top of this function. + std.hash.autoHash(hasher, func); }, .Frame => { @panic("TODO implement hashing frame values"); @@ -1824,6 +1804,65 @@ pub const Value = extern union { }; } + fn hashInt(int_val: Value, hasher: *std.hash.Wyhash) void { + var buffer: BigIntSpace = undefined; + const big = int_val.toBigInt(&buffer); + std.hash.autoHash(hasher, big.positive); + for (big.limbs) |limb| { + std.hash.autoHash(hasher, limb); + } + } + + fn hashPtr(ptr_val: Value, hasher: *std.hash.Wyhash) void { + switch (ptr_val.tag()) { + .decl_ref, + .decl_ref_mut, + .extern_fn, + .function, + .variable, + => { + const decl: *Module.Decl = ptr_val.pointerDecl().?; + std.hash.autoHash(hasher, decl); + }, + + .elem_ptr => { + const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; + hashPtr(elem_ptr.array_ptr, hasher); + std.hash.autoHash(hasher, Value.Tag.elem_ptr); + std.hash.autoHash(hasher, elem_ptr.index); + }, + .field_ptr => { + const field_ptr = ptr_val.castTag(.field_ptr).?.data; + std.hash.autoHash(hasher, Value.Tag.field_ptr); + hashPtr(field_ptr.container_ptr, hasher); + std.hash.autoHash(hasher, field_ptr.field_index); + }, + .eu_payload_ptr => { + const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data; + std.hash.autoHash(hasher, Value.Tag.eu_payload_ptr); + hashPtr(err_union_ptr, hasher); + }, + .opt_payload_ptr => { + const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data; + std.hash.autoHash(hasher, Value.Tag.opt_payload_ptr); + hashPtr(opt_ptr, hasher); + }, + + .zero, + .one, + .int_u64, + .int_i64, + .int_big_positive, + .int_big_negative, + .bool_false, + .bool_true, + .the_only_possible_value, + => return hashInt(ptr_val, hasher), + + else => unreachable, + } + } + pub fn markReferencedDeclsAlive(val: Value) void { switch (val.tag()) { .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(), @@ -1876,7 +1915,8 @@ pub const Value = extern union { pub fn slicePtr(val: Value) Value { return switch (val.tag()) { .slice => val.castTag(.slice).?.data.ptr, - .decl_ref, .decl_ref_mut => val, + // TODO this should require being a slice tag, and not allow decl_ref, field_ptr, etc. + .decl_ref, .decl_ref_mut, .field_ptr, .elem_ptr => val, else => unreachable, }; } diff --git a/test/behavior.zig b/test/behavior.zig index 7380baf262..5fa65fe475 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -98,7 +98,6 @@ test { _ = @import("behavior/bugs/3007.zig"); _ = @import("behavior/bugs/9584.zig"); _ = @import("behavior/cast_llvm.zig"); - _ = @import("behavior/enum_llvm.zig"); _ = @import("behavior/error_llvm.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); @@ -163,9 +162,7 @@ test { _ = @import("behavior/muladd.zig"); _ = @import("behavior/null_stage1.zig"); _ = @import("behavior/optional_stage1.zig"); - _ = @import("behavior/pointers_stage1.zig"); _ = @import("behavior/popcount_stage1.zig"); - _ = @import("behavior/ptrcast_stage1.zig"); _ = @import("behavior/reflection.zig"); _ = @import("behavior/saturating_arithmetic_stage1.zig"); _ = @import("behavior/select.zig"); diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 0a0d7e5a15..119a999366 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -972,3 +972,116 @@ fn test3_2(f: Test3Foo) !void { else => unreachable, } } + +test "@tagName" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); +} + +fn testEnumTagNameBare(n: anytype) []const u8 { + return @tagName(n); +} + +const BareNumber = enum { One, Two, Three }; + +test "@tagName non-exhaustive enum" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); + comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); +} +const NonExhaustive = enum(u8) { A, B, _ }; + +test "@tagName is null-terminated" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest(n: BareNumber) !void { + try expect(@tagName(n)[3] == 0); + } + }; + try S.doTheTest(.Two); + try comptime S.doTheTest(.Two); +} + +test "tag name with assigned enum values" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const LocalFoo = enum(u8) { + A = 1, + B = 0, + }; + var b = LocalFoo.B; + try expect(mem.eql(u8, @tagName(b), "B")); +} + +test "@tagName on enum literals" { + try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); + comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); +} + +test "enum literal casting to optional" { + var bar: ?Bar = undefined; + bar = .B; + + try expect(bar.? == Bar.B); +} + +const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 }; +const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 }; +const C = enum(u2) { One4, Two4, Three4, Four4 }; + +const BitFieldOfEnums = packed struct { + a: A, + b: B, + c: C, +}; + +const bit_field_1 = BitFieldOfEnums{ + .a = A.Two, + .b = B.Three3, + .c = C.Four4, +}; + +test "bit field access with enum fields" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + var data = bit_field_1; + try expect(getA(&data) == A.Two); + try expect(getB(&data) == B.Three3); + try expect(getC(&data) == C.Four4); + comptime try expect(@sizeOf(BitFieldOfEnums) == 1); + + data.b = B.Four3; + try expect(data.b == B.Four3); + + data.a = A.Three; + try expect(data.a == A.Three); + try expect(data.b == B.Four3); +} + +fn getA(data: *const BitFieldOfEnums) A { + return data.a; +} + +fn getB(data: *const BitFieldOfEnums) B { + return data.b; +} + +fn getC(data: *const BitFieldOfEnums) C { + return data.c; +} + +test "enum literal in array literal" { + const Items = enum { one, two }; + const array = [_]Items{ .one, .two }; + + try expect(array[0] == .one); + try expect(array[1] == .two); +} diff --git a/test/behavior/enum_llvm.zig b/test/behavior/enum_llvm.zig deleted file mode 100644 index 81a1c72e59..0000000000 --- a/test/behavior/enum_llvm.zig +++ /dev/null @@ -1,105 +0,0 @@ -const std = @import("std"); -const expect = std.testing.expect; -const mem = std.mem; -const Tag = std.meta.Tag; - -test "@tagName" { - try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime try expect(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); -} - -fn testEnumTagNameBare(n: anytype) []const u8 { - return @tagName(n); -} - -const BareNumber = enum { One, Two, Three }; - -test "@tagName non-exhaustive enum" { - try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); - comptime try expect(mem.eql(u8, testEnumTagNameBare(NonExhaustive.B), "B")); -} -const NonExhaustive = enum(u8) { A, B, _ }; - -test "@tagName is null-terminated" { - const S = struct { - fn doTheTest(n: BareNumber) !void { - try expect(@tagName(n)[3] == 0); - } - }; - try S.doTheTest(.Two); - try comptime S.doTheTest(.Two); -} - -test "tag name with assigned enum values" { - const LocalFoo = enum(u8) { - A = 1, - B = 0, - }; - var b = LocalFoo.B; - try expect(mem.eql(u8, @tagName(b), "B")); -} - -test "@tagName on enum literals" { - try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); - comptime try expect(mem.eql(u8, @tagName(.FooBar), "FooBar")); -} - -const Bar = enum { A, B, C, D }; - -test "enum literal casting to optional" { - var bar: ?Bar = undefined; - bar = .B; - - try expect(bar.? == Bar.B); -} - -const A = enum(u3) { One, Two, Three, Four, One2, Two2, Three2, Four2 }; -const B = enum(u3) { One3, Two3, Three3, Four3, One23, Two23, Three23, Four23 }; -const C = enum(u2) { One4, Two4, Three4, Four4 }; - -const BitFieldOfEnums = packed struct { - a: A, - b: B, - c: C, -}; - -const bit_field_1 = BitFieldOfEnums{ - .a = A.Two, - .b = B.Three3, - .c = C.Four4, -}; - -test "bit field access with enum fields" { - var data = bit_field_1; - try expect(getA(&data) == A.Two); - try expect(getB(&data) == B.Three3); - try expect(getC(&data) == C.Four4); - comptime try expect(@sizeOf(BitFieldOfEnums) == 1); - - data.b = B.Four3; - try expect(data.b == B.Four3); - - data.a = A.Three; - try expect(data.a == A.Three); - try expect(data.b == B.Four3); -} - -fn getA(data: *const BitFieldOfEnums) A { - return data.a; -} - -fn getB(data: *const BitFieldOfEnums) B { - return data.b; -} - -fn getC(data: *const BitFieldOfEnums) C { - return data.c; -} - -test "enum literal in array literal" { - const Items = enum { one, two }; - const array = [_]Items{ .one, .two }; - - try expect(array[0] == .one); - try expect(array[1] == .two); -} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 32b88a2522..a642da858b 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const expect = testing.expect; @@ -97,3 +98,284 @@ test "C pointer comparison and arithmetic" { try S.doTheTest(); comptime try S.doTheTest(); } + +test "dereference pointer again" { + try testDerefPtrOneVal(); + comptime try testDerefPtrOneVal(); +} + +const Foo1 = struct { + x: void, +}; + +fn testDerefPtrOneVal() !void { + // Foo1 satisfies the OnePossibleValueYes criteria + const x = &Foo1{ .x = {} }; + const y = x.*; + try expect(@TypeOf(y.x) == void); +} + +test "peer type resolution with C pointers" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + var ptr_one: *u8 = undefined; + var ptr_many: [*]u8 = undefined; + var ptr_c: [*c]u8 = undefined; + var t = true; + var x1 = if (t) ptr_one else ptr_c; + var x2 = if (t) ptr_many else ptr_c; + var x3 = if (t) ptr_c else ptr_one; + var x4 = if (t) ptr_c else ptr_many; + try expect(@TypeOf(x1) == [*c]u8); + try expect(@TypeOf(x2) == [*c]u8); + try expect(@TypeOf(x3) == [*c]u8); + try expect(@TypeOf(x4) == [*c]u8); +} + +test "implicit casting between C pointer and optional non-C pointer" { + var slice: []const u8 = "aoeu"; + const opt_many_ptr: ?[*]const u8 = slice.ptr; + var ptr_opt_many_ptr = &opt_many_ptr; + var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; + try expect(c_ptr.*.* == 'a'); + ptr_opt_many_ptr = c_ptr; + try expect(ptr_opt_many_ptr.*.?[1] == 'o'); +} + +test "implicit cast error unions with non-optional to optional pointer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + try expectError(error.Fail, foo()); + } + fn foo() anyerror!?*u8 { + return bar() orelse error.Fail; + } + fn bar() ?*u8 { + return null; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "compare equality of optional and non-optional pointer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const a = @intToPtr(*const usize, 0x12345678); + const b = @intToPtr(?*usize, 0x12345678); + try expect(a == b); + try expect(b == a); +} + +test "allowzero pointer and slice" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + var ptr = @intToPtr([*]allowzero i32, 0); + var opt_ptr: ?[*]allowzero i32 = ptr; + try expect(opt_ptr != null); + try expect(@ptrToInt(ptr) == 0); + var runtime_zero: usize = 0; + var slice = ptr[runtime_zero..10]; + comptime try expect(@TypeOf(slice) == []allowzero i32); + try expect(@ptrToInt(&slice[5]) == 20); + + comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); + comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); +} + +test "assign null directly to C pointer and test null equality" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + var x: [*c]i32 = null; + try expect(x == null); + try expect(null == x); + try expect(!(x != null)); + try expect(!(null != x)); + if (x) |same_x| { + _ = same_x; + @panic("fail"); + } + var otherx: i32 = undefined; + try expect((x orelse &otherx) == &otherx); + + const y: [*c]i32 = null; + comptime try expect(y == null); + comptime try expect(null == y); + comptime try expect(!(y != null)); + comptime try expect(!(null != y)); + if (y) |same_y| { + _ = same_y; + @panic("fail"); + } + const othery: i32 = undefined; + comptime try expect((y orelse &othery) == &othery); + + var n: i32 = 1234; + var x1: [*c]i32 = &n; + try expect(!(x1 == null)); + try expect(!(null == x1)); + try expect(x1 != null); + try expect(null != x1); + try expect(x1.?.* == 1234); + if (x1) |same_x1| { + try expect(same_x1.* == 1234); + } else { + @panic("fail"); + } + try expect((x1 orelse &otherx) == x1); + + const nc: i32 = 1234; + const y1: [*c]const i32 = &nc; + comptime try expect(!(y1 == null)); + comptime try expect(!(null == y1)); + comptime try expect(y1 != null); + comptime try expect(null != y1); + comptime try expect(y1.?.* == 1234); + if (y1) |same_y1| { + try expect(same_y1.* == 1234); + } else { + @compileError("fail"); + } + comptime try expect((y1 orelse &othery) == y1); +} + +test "null terminated pointer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' }; + var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero); + var no_zero_ptr: [*]const u8 = zero_ptr; + var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr); + try expect(std.mem.eql(u8, std.mem.sliceTo(zero_ptr_again, 0), "hello")); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "allow any sentinel" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 }; + var ptr: [*:std.math.minInt(i32)]i32 = &array; + try expect(ptr[4] == std.math.minInt(i32)); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "pointer sentinel with enums" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + const Number = enum { + one, + two, + sentinel, + }; + + fn doTheTest() !void { + var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one }; + try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731 + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "pointer sentinel with optional element" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 }; + try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731 + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "pointer sentinel with +inf" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const inf = std.math.inf_f32; + var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 }; + try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731 + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "pointer to array at fixed address" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const array = @intToPtr(*volatile [1]u32, 0x10); + // Silly check just to reference `array` + try expect(@ptrToInt(&array[0]) == 0x10); +} + +test "pointer arithmetic affects the alignment" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + { + var ptr: [*]align(8) u32 = undefined; + var x: usize = 1; + + try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); + const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 + try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); + const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 + try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); + const ptr3 = ptr + 0; // no-op + try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); + const ptr4 = ptr + x; // runtime-known addend + try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); + } + { + var ptr: [*]align(8) [3]u8 = undefined; + var x: usize = 1; + + const ptr1 = ptr + 17; // 3 * 17 = 51 + try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); + const ptr2 = ptr + x; // runtime-known addend + try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); + const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 + try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); + const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 + try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); + } +} + +test "@ptrToInt on null optional at comptime" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + { + const pointer = @intToPtr(?*u8, 0x000); + const x = @ptrToInt(pointer); + _ = x; + comptime try expect(0 == @ptrToInt(pointer)); + } + { + const pointer = @intToPtr(?*u8, 0xf00); + comptime try expect(0xf00 == @ptrToInt(pointer)); + } +} + +test "indexing array with sentinel returns correct type" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + var s: [:0]const u8 = "abc"; + try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); +} diff --git a/test/behavior/pointers_stage1.zig b/test/behavior/pointers_stage1.zig deleted file mode 100644 index 9d550db55b..0000000000 --- a/test/behavior/pointers_stage1.zig +++ /dev/null @@ -1,256 +0,0 @@ -const std = @import("std"); -const testing = std.testing; -const expect = testing.expect; -const expectError = testing.expectError; - -const Foo1 = struct { - x: void, -}; - -test "dereference pointer again" { - try testDerefPtrOneVal(); - comptime try testDerefPtrOneVal(); -} - -fn testDerefPtrOneVal() !void { - // Foo1 satisfies the OnePossibleValueYes criteria - const x = &Foo1{ .x = {} }; - const y = x.*; - try expect(@TypeOf(y.x) == void); -} - -test "peer type resolution with C pointers" { - var ptr_one: *u8 = undefined; - var ptr_many: [*]u8 = undefined; - var ptr_c: [*c]u8 = undefined; - var t = true; - var x1 = if (t) ptr_one else ptr_c; - var x2 = if (t) ptr_many else ptr_c; - var x3 = if (t) ptr_c else ptr_one; - var x4 = if (t) ptr_c else ptr_many; - try expect(@TypeOf(x1) == [*c]u8); - try expect(@TypeOf(x2) == [*c]u8); - try expect(@TypeOf(x3) == [*c]u8); - try expect(@TypeOf(x4) == [*c]u8); -} - -test "implicit casting between C pointer and optional non-C pointer" { - var slice: []const u8 = "aoeu"; - const opt_many_ptr: ?[*]const u8 = slice.ptr; - var ptr_opt_many_ptr = &opt_many_ptr; - var c_ptr: [*c]const [*c]const u8 = ptr_opt_many_ptr; - try expect(c_ptr.*.* == 'a'); - ptr_opt_many_ptr = c_ptr; - try expect(ptr_opt_many_ptr.*.?[1] == 'o'); -} - -test "implicit cast error unions with non-optional to optional pointer" { - const S = struct { - fn doTheTest() !void { - try expectError(error.Fail, foo()); - } - fn foo() anyerror!?*u8 { - return bar() orelse error.Fail; - } - fn bar() ?*u8 { - return null; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "compare equality of optional and non-optional pointer" { - const a = @intToPtr(*const usize, 0x12345678); - const b = @intToPtr(?*usize, 0x12345678); - try expect(a == b); - try expect(b == a); -} - -test "allowzero pointer and slice" { - var ptr = @intToPtr([*]allowzero i32, 0); - var opt_ptr: ?[*]allowzero i32 = ptr; - try expect(opt_ptr != null); - try expect(@ptrToInt(ptr) == 0); - var runtime_zero: usize = 0; - var slice = ptr[runtime_zero..10]; - comptime try expect(@TypeOf(slice) == []allowzero i32); - try expect(@ptrToInt(&slice[5]) == 20); - - comptime try expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); - comptime try expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); -} - -test "assign null directly to C pointer and test null equality" { - var x: [*c]i32 = null; - try expect(x == null); - try expect(null == x); - try expect(!(x != null)); - try expect(!(null != x)); - if (x) |same_x| { - _ = same_x; - @panic("fail"); - } - var otherx: i32 = undefined; - try expect((x orelse &otherx) == &otherx); - - const y: [*c]i32 = null; - comptime try expect(y == null); - comptime try expect(null == y); - comptime try expect(!(y != null)); - comptime try expect(!(null != y)); - if (y) |same_y| { - _ = same_y; - @panic("fail"); - } - const othery: i32 = undefined; - comptime try expect((y orelse &othery) == &othery); - - var n: i32 = 1234; - var x1: [*c]i32 = &n; - try expect(!(x1 == null)); - try expect(!(null == x1)); - try expect(x1 != null); - try expect(null != x1); - try expect(x1.?.* == 1234); - if (x1) |same_x1| { - try expect(same_x1.* == 1234); - } else { - @panic("fail"); - } - try expect((x1 orelse &otherx) == x1); - - const nc: i32 = 1234; - const y1: [*c]const i32 = &nc; - comptime try expect(!(y1 == null)); - comptime try expect(!(null == y1)); - comptime try expect(y1 != null); - comptime try expect(null != y1); - comptime try expect(y1.?.* == 1234); - if (y1) |same_y1| { - try expect(same_y1.* == 1234); - } else { - @compileError("fail"); - } - comptime try expect((y1 orelse &othery) == y1); -} - -test "null terminated pointer" { - const S = struct { - fn doTheTest() !void { - var array_with_zero = [_:0]u8{ 'h', 'e', 'l', 'l', 'o' }; - var zero_ptr: [*:0]const u8 = @ptrCast([*:0]const u8, &array_with_zero); - var no_zero_ptr: [*]const u8 = zero_ptr; - var zero_ptr_again = @ptrCast([*:0]const u8, no_zero_ptr); - try expect(std.mem.eql(u8, std.mem.sliceTo(zero_ptr_again, 0), "hello")); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "allow any sentinel" { - const S = struct { - fn doTheTest() !void { - var array = [_:std.math.minInt(i32)]i32{ 1, 2, 3, 4 }; - var ptr: [*:std.math.minInt(i32)]i32 = &array; - try expect(ptr[4] == std.math.minInt(i32)); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "pointer sentinel with enums" { - const S = struct { - const Number = enum { - one, - two, - sentinel, - }; - - fn doTheTest() !void { - var ptr: [*:.sentinel]const Number = &[_:.sentinel]Number{ .one, .two, .two, .one }; - try expect(ptr[4] == .sentinel); // TODO this should be comptime try expect, see #3731 - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "pointer sentinel with optional element" { - const S = struct { - fn doTheTest() !void { - var ptr: [*:null]const ?i32 = &[_:null]?i32{ 1, 2, 3, 4 }; - try expect(ptr[4] == null); // TODO this should be comptime try expect, see #3731 - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "pointer sentinel with +inf" { - const S = struct { - fn doTheTest() !void { - const inf = std.math.inf_f32; - var ptr: [*:inf]const f32 = &[_:inf]f32{ 1.1, 2.2, 3.3, 4.4 }; - try expect(ptr[4] == inf); // TODO this should be comptime try expect, see #3731 - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "pointer to array at fixed address" { - const array = @intToPtr(*volatile [1]u32, 0x10); - // Silly check just to reference `array` - try expect(@ptrToInt(&array[0]) == 0x10); -} - -test "pointer arithmetic affects the alignment" { - { - var ptr: [*]align(8) u32 = undefined; - var x: usize = 1; - - try expect(@typeInfo(@TypeOf(ptr)).Pointer.alignment == 8); - const ptr1 = ptr + 1; // 1 * 4 = 4 -> lcd(4,8) = 4 - try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 4); - const ptr2 = ptr + 4; // 4 * 4 = 16 -> lcd(16,8) = 8 - try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 8); - const ptr3 = ptr + 0; // no-op - try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); - const ptr4 = ptr + x; // runtime-known addend - try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); - } - { - var ptr: [*]align(8) [3]u8 = undefined; - var x: usize = 1; - - const ptr1 = ptr + 17; // 3 * 17 = 51 - try expect(@typeInfo(@TypeOf(ptr1)).Pointer.alignment == 1); - const ptr2 = ptr + x; // runtime-known addend - try expect(@typeInfo(@TypeOf(ptr2)).Pointer.alignment == 1); - const ptr3 = ptr + 8; // 3 * 8 = 24 -> lcd(8,24) = 8 - try expect(@typeInfo(@TypeOf(ptr3)).Pointer.alignment == 8); - const ptr4 = ptr + 4; // 3 * 4 = 12 -> lcd(8,12) = 4 - try expect(@typeInfo(@TypeOf(ptr4)).Pointer.alignment == 4); - } -} - -test "@ptrToInt on null optional at comptime" { - { - const pointer = @intToPtr(?*u8, 0x000); - const x = @ptrToInt(pointer); - _ = x; - comptime try expect(0 == @ptrToInt(pointer)); - } - { - const pointer = @intToPtr(?*u8, 0xf00); - comptime try expect(0xf00 == @ptrToInt(pointer)); - } -} - -test "indexing array with sentinel returns correct type" { - var s: [:0]const u8 = "abc"; - try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0]))); -} diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 1a03964179..02ae0fd0c6 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -2,3 +2,80 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; const native_endian = builtin.target.cpu.arch.endian(); + +test "reinterpret bytes as integer with nonzero offset" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testReinterpretBytesAsInteger(); + comptime try testReinterpretBytesAsInteger(); +} + +fn testReinterpretBytesAsInteger() !void { + const bytes = "\x12\x34\x56\x78\xab"; + const expected = switch (native_endian) { + .Little => 0xab785634, + .Big => 0x345678ab, + }; + try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); +} + +test "reinterpret bytes of an array into an extern struct" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + try testReinterpretBytesAsExternStruct(); + comptime try testReinterpretBytesAsExternStruct(); +} + +fn testReinterpretBytesAsExternStruct() !void { + var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; + + const S = extern struct { + a: u8, + b: u16, + c: u8, + }; + + var ptr = @ptrCast(*const S, &bytes); + var val = ptr.c; + try expect(val == 5); +} + +test "reinterpret struct field at comptime" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const numNative = comptime Bytes.init(0x12345678); + if (native_endian != .Little) { + try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes)); + } else { + try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes)); + } +} + +const Bytes = struct { + bytes: [4]u8, + + pub fn init(v: u32) Bytes { + var res: Bytes = undefined; + @ptrCast(*align(1) u32, &res.bytes).* = v; + + return res; + } +}; + +test "comptime ptrcast keeps larger alignment" { + comptime { + const a: u32 = 1234; + const p = @ptrCast([*]const u8, &a); + try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); + } +} + +test "implicit optional pointer to optional anyopaque pointer" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + var buf: [4]u8 = "aoeu".*; + var x: ?[*]u8 = &buf; + var y: ?*anyopaque = x; + var z = @ptrCast(*[4]u8, y); + try expect(std.mem.eql(u8, z, "aoeu")); +} diff --git a/test/behavior/ptrcast_stage1.zig b/test/behavior/ptrcast_stage1.zig deleted file mode 100644 index 3bf6181a19..0000000000 --- a/test/behavior/ptrcast_stage1.zig +++ /dev/null @@ -1,73 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); -const expect = std.testing.expect; -const native_endian = builtin.target.cpu.arch.endian(); - -test "reinterpret bytes as integer with nonzero offset" { - try testReinterpretBytesAsInteger(); - comptime try testReinterpretBytesAsInteger(); -} - -fn testReinterpretBytesAsInteger() !void { - const bytes = "\x12\x34\x56\x78\xab"; - const expected = switch (native_endian) { - .Little => 0xab785634, - .Big => 0x345678ab, - }; - try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); -} - -test "reinterpret bytes of an array into an extern struct" { - try testReinterpretBytesAsExternStruct(); - comptime try testReinterpretBytesAsExternStruct(); -} - -fn testReinterpretBytesAsExternStruct() !void { - var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 }; - - const S = extern struct { - a: u8, - b: u16, - c: u8, - }; - - var ptr = @ptrCast(*const S, &bytes); - var val = ptr.c; - try expect(val == 5); -} - -test "reinterpret struct field at comptime" { - const numNative = comptime Bytes.init(0x12345678); - if (native_endian != .Little) { - try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes)); - } else { - try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes)); - } -} - -const Bytes = struct { - bytes: [4]u8, - - pub fn init(v: u32) Bytes { - var res: Bytes = undefined; - @ptrCast(*align(1) u32, &res.bytes).* = v; - - return res; - } -}; - -test "comptime ptrcast keeps larger alignment" { - comptime { - const a: u32 = 1234; - const p = @ptrCast([*]const u8, &a); - try expect(@TypeOf(p) == [*]align(@alignOf(u32)) const u8); - } -} - -test "implicit optional pointer to optional anyopaque pointer" { - var buf: [4]u8 = "aoeu".*; - var x: ?[*]u8 = &buf; - var y: ?*anyopaque = x; - var z = @ptrCast(*[4]u8, y); - try expect(std.mem.eql(u8, z, "aoeu")); -}