zig/test/behavior/ptrcast.zig
Andrew Kelley 157f66ec07 Sema: fix pointer type hash and equality functions
Several issues with pointer types are fixed:

Prior to this commit, Zig would not canonicalize a pointer type with
an explicit alignment to alignment=0 if it matched the pointee ABI
alignment. In order to fix this, `Type.ptr` now takes a Target
parameter. I also moved the host_size canonicalization to `Type.ptr`
since target is now available. Similarly, is_allowzero in the case of
C pointers is now treated as a canonicalization done by the function
rather than a precondition.

in-memory coercion for pointers now properly checks ABI alignment
of pointee types instead of incorrectly treating the 0 value as an
alignment.

Type equality is completely reworked based on the tag() rather than the
zigTypeTag(). It's still semantically based on zigTypeTag() but that
knowledge is implied rather than dictating the control flow of the
logic. Importantly, this fixes cases for opaques, structs, tuples,
enums, and unions, where type equality was incorrectly returning based
on whether the tag() values were equal.

Additionally, pointer type equality now takes into account alignment.
Because we canonicalize non-zero alignment which equals pointee type ABI
alignment to alignment=0, this now can be a simple integer comparison.

Type hashing is implemented for pointers and floats. Array types now
additionally hash their sentinels.

This regressed some behavior tests that were passing but only because
of bugs regarding type equality.

The C backend has a noticeable problem with lowering differently-aligned
pointers (particularly slices) as the same type, causing C compilation
errors due to duplicate declarations.
2022-02-28 19:22:16 -07:00

84 lines
2.3 KiB
Zig

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" {
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
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"));
}