test: add tests for @memmove

This commit is contained in:
dweiller 2025-01-22 18:44:27 +11:00
parent 898ca82458
commit 4e78836d29
8 changed files with 478 additions and 1 deletions

View file

@ -55,6 +55,7 @@ test {
_ = @import("behavior/member_func.zig"); _ = @import("behavior/member_func.zig");
_ = @import("behavior/memcpy.zig"); _ = @import("behavior/memcpy.zig");
_ = @import("behavior/memset.zig"); _ = @import("behavior/memset.zig");
_ = @import("behavior/memmove.zig");
_ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/merge_error_sets.zig");
_ = @import("behavior/muladd.zig"); _ = @import("behavior/muladd.zig");
_ = @import("behavior/multiple_externs_with_conflicting_types.zig"); _ = @import("behavior/multiple_externs_with_conflicting_types.zig");

View file

@ -6,6 +6,7 @@ var x: u8 = 1;
// This excludes builtin functions that return void or noreturn that cannot be tested. // This excludes builtin functions that return void or noreturn that cannot be tested.
test { test {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@ -17,6 +18,7 @@ test {
try testing.expectEqual(void, @TypeOf(@breakpoint())); try testing.expectEqual(void, @TypeOf(@breakpoint()));
try testing.expectEqual({}, @export(&x, .{ .name = "x" })); try testing.expectEqual({}, @export(&x, .{ .name = "x" }));
try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0])); try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));
try testing.expectEqual({}, @memmove(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0]));
try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined)); try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined));
try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {}));
try testing.expectEqual({}, @prefetch(&val, .{})); try testing.expectEqual({}, @prefetch(&val, .{}));

183
test/behavior/memmove.zig Normal file
View file

@ -0,0 +1,183 @@
const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
test "memmove and memset intrinsics" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemmoveMemset();
try comptime testMemmoveMemset();
}
fn testMemmoveMemset() !void {
var foo: [20]u8 = undefined;
@memset(foo[0..10], 'A');
@memset(foo[10..20], 'B');
try expect(foo[0] == 'A');
try expect(foo[11] == 'B');
try expect(foo[19] == 'B');
@memmove(foo[10..20], foo[0..10]);
try expect(foo[0] == 'A');
try expect(foo[11] == 'A');
try expect(foo[19] == 'A');
}
test "@memmove with both operands single-ptr-to-array, one is null-terminated" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemmoveBothSinglePtrArrayOneIsNullTerminated();
try comptime testMemmoveBothSinglePtrArrayOneIsNullTerminated();
}
fn testMemmoveBothSinglePtrArrayOneIsNullTerminated() !void {
var buf: [100]u8 = undefined;
const suffix = "hello";
@memmove(buf[buf.len - suffix.len ..], suffix);
try expect(buf[95] == 'h');
try expect(buf[96] == 'e');
try expect(buf[97] == 'l');
try expect(buf[98] == 'l');
try expect(buf[99] == 'o');
const start = buf.len - suffix.len - 3;
const end = start + suffix.len;
@memmove(buf[start..end], buf[buf.len - suffix.len ..]);
try expect(buf[92] == 'h');
try expect(buf[93] == 'e');
try expect(buf[94] == 'l');
try expect(buf[95] == 'l');
try expect(buf[96] == 'o');
try expect(buf[97] == 'l');
try expect(buf[98] == 'l');
try expect(buf[99] == 'o');
@memmove(buf[start + 2 .. end + 2], buf[start..end]);
try expect(buf[92] == 'h');
try expect(buf[93] == 'e');
try expect(buf[94] == 'h');
try expect(buf[95] == 'e');
try expect(buf[96] == 'l');
try expect(buf[97] == 'l');
try expect(buf[98] == 'o');
try expect(buf[99] == 'o');
}
test "@memmove dest many pointer" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemmoveDestManyPtr();
try comptime testMemmoveDestManyPtr();
}
fn testMemmoveDestManyPtr() !void {
var str = "hello".*;
var buf: [8]u8 = undefined;
var len: usize = 5;
_ = &len;
@memmove(@as([*]u8, @ptrCast(&buf)), @as([*]const u8, @ptrCast(&str))[0..len]);
try expect(buf[0] == 'h');
try expect(buf[1] == 'e');
try expect(buf[2] == 'l');
try expect(buf[3] == 'l');
try expect(buf[4] == 'o');
@memmove(buf[3..].ptr, buf[0..len]);
try expect(buf[0] == 'h');
try expect(buf[1] == 'e');
try expect(buf[2] == 'l');
try expect(buf[3] == 'h');
try expect(buf[4] == 'e');
try expect(buf[5] == 'l');
try expect(buf[6] == 'l');
try expect(buf[7] == 'o');
@memmove(buf[2..7].ptr, buf[3 .. len + 3]);
try expect(buf[0] == 'h');
try expect(buf[1] == 'e');
try expect(buf[2] == 'h');
try expect(buf[3] == 'e');
try expect(buf[4] == 'l');
try expect(buf[5] == 'l');
try expect(buf[6] == 'o');
try expect(buf[7] == 'o');
}
test "@memmove slice" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
try testMemmoveSlice();
try comptime testMemmoveSlice();
}
fn testMemmoveSlice() !void {
var buf: [8]u8 = undefined;
const dst1: []u8 = buf[0..5];
const dst2: []u8 = buf[3..8];
const dst3: []u8 = buf[2..7];
const src: []const u8 = "hello";
@memmove(dst1, src);
try expect(buf[0] == 'h');
try expect(buf[1] == 'e');
try expect(buf[2] == 'l');
try expect(buf[3] == 'l');
try expect(buf[4] == 'o');
@memmove(dst2, dst1);
try expect(buf[0] == 'h');
try expect(buf[1] == 'e');
try expect(buf[2] == 'l');
try expect(buf[3] == 'h');
try expect(buf[4] == 'e');
try expect(buf[5] == 'l');
try expect(buf[6] == 'l');
try expect(buf[7] == 'o');
@memmove(dst3, dst2);
try expect(buf[0] == 'h');
try expect(buf[1] == 'e');
try expect(buf[2] == 'h');
try expect(buf[3] == 'e');
try expect(buf[4] == 'l');
try expect(buf[5] == 'l');
try expect(buf[6] == 'o');
try expect(buf[7] == 'o');
}
comptime {
const S = struct {
buffer: [8]u8 = undefined,
fn set(self: *@This(), items: []const u8) void {
@memmove(self.buffer[0..items.len], items);
@memmove(self.buffer[3..], self.buffer[0..items.len]);
@memmove(self.buffer[2 .. 2 + items.len], self.buffer[3..]);
}
};
var s = S{};
s.set("hello");
if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad");
}

View file

@ -0,0 +1,218 @@
export fn foo() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []u8 = &buf;
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
@memmove(dest, src);
}
export fn bar() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []u8 = &buf;
const src: *align(1) [8]u16 = @ptrCast(&buf);
@memmove(dest, src);
}
export fn baz() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []u8 = &buf;
const src: [*]align(1) u16 = @ptrCast(&buf);
@memmove(dest, src);
}
export fn qux() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *[8]u8 = &buf;
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
@memmove(dest, src);
}
export fn quux() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *[8]u8 = &buf;
const src: *align(1) [8]u16 = @ptrCast(&buf);
@memmove(dest, src);
}
export fn quuux() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *[8]u8 = &buf;
const src: [*]align(1) u16 = @ptrCast(&buf);
@memmove(dest, src);
}
export fn foo2() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
const src: []u8 = &buf;
@memmove(dest, src);
}
export fn bar2() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *align(1) [8]u16 = @ptrCast(&buf);
const src: []u8 = &buf;
@memmove(dest, src);
}
export fn baz2() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: [*]align(1) u16 = @ptrCast(&buf);
const src: []u8 = &buf;
@memmove(dest, src);
}
export fn qux2() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
const src: *[8]u8 = &buf;
@memmove(dest, src);
}
export fn quux2() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *align(1) [8]u16 = @ptrCast(&buf);
const src: *[8]u8 = &buf;
@memmove(dest, src);
}
export fn quuux2() void {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: [*]align(1) u16 = @ptrCast(&buf);
const src: *[8]u8 = &buf;
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []u8 = &buf;
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []u8 = &buf;
const src: *align(1) [8]u16 = @ptrCast(&buf);
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []u8 = &buf;
const src: [*]align(1) u16 = @ptrCast(&buf);
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *[8]u8 = &buf;
const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *[8]u8 = &buf;
const src: *align(1) [8]u16 = @ptrCast(&buf);
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *[8]u8 = &buf;
const src: [*]align(1) u16 = @ptrCast(&buf);
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
const src: []u8 = &buf;
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *align(1) [8]u16 = @ptrCast(&buf);
const src: []u8 = &buf;
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: [*]align(1) u16 = @ptrCast(&buf);
const src: []u8 = &buf;
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4];
const src: *[8]u8 = &buf;
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: *align(1) [8]u16 = @ptrCast(&buf);
const src: *[8]u8 = &buf;
@memmove(dest, src);
}
comptime {
var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 };
const dest: [*]align(1) u16 = @ptrCast(&buf);
const src: *[8]u8 = &buf;
@memmove(dest, src);
}
// error
//
// :6:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :6:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :14:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :14:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :22:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :22:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :30:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :30:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :38:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :38:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :46:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :46:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :54:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :62:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :70:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :78:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :86:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :94:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :101:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :101:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :108:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :108:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :115:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :115:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :122:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :122:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :129:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :129:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :136:5: error: pointer element type 'u16' cannot coerce into element type 'u8'
// :136:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values
// :143:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :150:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :157:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :164:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :171:5: error: pointer element type 'u8' cannot coerce into element type 'u16'
// :178:5: error: pointer element type 'u8' cannot coerce into element type 'u16'

View file

@ -63,6 +63,14 @@ export fn far() void {
@memset(&rt, elem); @memset(&rt, elem);
} }
export fn bax() void {
comptime var x: [2]u32 = undefined;
x = .{ 1, 2 };
var rt: [2]u32 = undefined;
@memmove(&rt, &x);
}
// error // error
// //
// :5:19: error: runtime value contains reference to comptime var // :5:19: error: runtime value contains reference to comptime var
@ -92,3 +100,6 @@ export fn far() void {
// :63:18: error: runtime value contains reference to comptime var // :63:18: error: runtime value contains reference to comptime var
// :63:18: note: comptime var pointers are not available at runtime // :63:18: note: comptime var pointers are not available at runtime
// :59:27: note: 'runtime_value' points to comptime var declared here // :59:27: note: 'runtime_value' points to comptime var declared here
// :71:19: error: runtime value contains reference to comptime var
// :71:19: note: comptime var pointers are not available at runtime
// :67:30: note: 'runtime_value' points to comptime var declared here

View file

@ -28,10 +28,39 @@ pub export fn memcpy_const_dest_ptr() void {
var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 }; var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };
@memcpy(&buf1, &buf2); @memcpy(&buf1, &buf2);
} }
pub export fn memset_array() void { pub export fn memcpy_array() void {
const buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
@memcpy(buf, 1); @memcpy(buf, 1);
} }
pub export fn entry_memmove() void {
var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
const slice: []u8 = &buf;
const a: u32 = 1234;
@memmove(slice.ptr, @as([*]const u8, @ptrCast(&a)));
}
pub export fn entry1_memmove() void {
var buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
const ptr: *u8 = &buf[0];
@memmove(ptr, 0);
}
pub export fn non_matching_lengths_memmove() void {
var buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
var buf2: [6]u8 = .{ 1, 2, 3, 4, 5, 6 };
@memmove(&buf2, &buf1);
}
pub export fn memcpy_const_dest_ptr_memmove() void {
const buf1: [5]u8 = .{ 1, 2, 3, 4, 5 };
var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 };
@memmove(&buf1, &buf2);
}
pub export fn memmove_array() void {
const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
@memmove(buf, 1);
}
pub export fn memset_array() void {
const buf: [5]u8 = .{ 1, 2, 3, 4, 5 };
@memset(buf, 1);
}
// error // error
// backend=stage2 // backend=stage2
@ -51,3 +80,16 @@ pub export fn memset_array() void {
// :29:13: error: cannot memcpy to constant pointer // :29:13: error: cannot memcpy to constant pointer
// :33:13: error: type '[5]u8' is not an indexable pointer // :33:13: error: type '[5]u8' is not an indexable pointer
// :33:13: note: operand must be a slice, a many pointer or a pointer to an array // :33:13: note: operand must be a slice, a many pointer or a pointer to an array
// :39:5: error: unknown @memmove length
// :39:19: note: destination type '[*]u8' provides no length
// :39:25: note: source type '[*]const u8' provides no length
// :44:14: error: type '*u8' is not an indexable pointer
// :44:14: note: operand must be a slice, a many pointer or a pointer to an array
// :49:5: error: non-matching @memmove lengths
// :49:14: note: length 6 here
// :49:21: note: length 5 here
// :54:14: error: cannot memmove to constant pointer
// :58:14: error: type '[5]u8' is not an indexable pointer
// :58:14: note: operand must be a slice, a many pointer or a pointer to an array
// :62:13: error: type '[5]u8' is not an indexable pointer
// :62:13: note: operand must be a slice, a many pointer or a pointer to an array

View file

@ -0,0 +1,19 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = stack_trace;
if (std.mem.eql(u8, message, "@memmove arguments have non-equal lengths")) {
std.process.exit(0);
}
std.process.exit(1);
}
pub fn main() !void {
var buffer = [2]u8{ 1, 2 } ** 5;
var len: usize = 5;
_ = &len;
@memmove(buffer[0..len], buffer[len .. len + 4]);
return error.TestFailed;
}
// run
// backend=llvm
// target=native

View file

@ -5,6 +5,7 @@ test {
const source = foo(); const source = foo();
@memcpy(dest, source); @memcpy(dest, source);
@memmove(dest, source);
@memset(dest, 4); @memset(dest, 4);
@memset(dest, undefined); @memset(dest, undefined);