| author | |
| committer | |
| log | 1b76d4c53adafebcbfcf0476276375353139dacb |
| tree | 5854ca686d0d016e2d109766fa17742fff27c6ac |
| parent | 227d2b15e449db1e84788d2c87e4f49100d316ca |
| parent | 2e9c1553ef40e9f21c2241294b8942369ef9007a |
| signature |
add `@memmove` builtin41 files changed, 714 insertions(+), 15 deletions(-)
doc/langref.html.in+17| ... | @@ -5149,6 +5149,23 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val | ... | @@ -5149,6 +5149,23 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val |
| 5149 | {#syntax#}std.crypto.secureZero{#endsyntax#}</p> | 5149 | {#syntax#}std.crypto.secureZero{#endsyntax#}</p> |
| 5150 | {#header_close#} | 5150 | {#header_close#} |
| 5151 | 5151 | ||
| 5152 | {#header_open|@memmove#} | ||
| 5153 | <pre>{#syntax#}@memmove(dest, source) void{#endsyntax#}</pre> | ||
| 5154 | <p>This function copies bytes from one region of memory to another, but unlike | ||
| 5155 | {#link|@memcpy#} the regions may overlap.</p> | ||
| 5156 | <p>{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or | ||
| 5157 | a mutable many-item {#link|pointer|Pointers#}. It may have any | ||
| 5158 | alignment, and it may have any element type.</p> | ||
| 5159 | <p>{#syntax#}source{#endsyntax#} must be a slice, a pointer to | ||
| 5160 | an array, or a many-item {#link|pointer|Pointers#}. It may | ||
| 5161 | have any alignment, and it may have any element type.</p> | ||
| 5162 | <p>The {#syntax#}source{#endsyntax#} element type must have the same in-memory | ||
| 5163 | representation as the {#syntax#}dest{#endsyntax#} element type.</p> | ||
| 5164 | <p>Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and | ||
| 5165 | {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided, | ||
| 5166 | they must be equal.</p> | ||
| 5167 | {#header_close#} | ||
| 5168 | |||
| 5152 | {#header_open|@min#} | 5169 | {#header_open|@min#} |
| 5153 | <pre>{#syntax#}@min(...) T{#endsyntax#}</pre> | 5170 | <pre>{#syntax#}@min(...) T{#endsyntax#}</pre> |
| 5154 | <p> | 5171 | <p> |
lib/std/debug.zig+4| ... | @@ -134,6 +134,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type { | ... | @@ -134,6 +134,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type { |
| 134 | @branchHint(.cold); | 134 | @branchHint(.cold); |
| 135 | call("@memcpy arguments alias", @returnAddress()); | 135 | call("@memcpy arguments alias", @returnAddress()); |
| 136 | } | 136 | } |
| 137 | pub fn memmoveLenMismatch() noreturn { | ||
| 138 | @branchHint(.cold); | ||
| 139 | call("@memmove arguments have non-equal lengths", @returnAddress()); | ||
| 140 | } | ||
| 137 | pub fn noreturnReturned() noreturn { | 141 | pub fn noreturnReturned() noreturn { |
| 138 | @branchHint(.cold); | 142 | @branchHint(.cold); |
| 139 | call("'noreturn' function returned", @returnAddress()); | 143 | call("'noreturn' function returned", @returnAddress()); |
lib/std/debug/no_panic.zig+5| ... | @@ -135,6 +135,11 @@ pub fn memcpyAlias() noreturn { | ... | @@ -135,6 +135,11 @@ pub fn memcpyAlias() noreturn { |
| 135 | @trap(); | 135 | @trap(); |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | pub fn memmoveLenMismatch() noreturn { | ||
| 139 | @branchHint(.cold); | ||
| 140 | @trap(); | ||
| 141 | } | ||
| 142 | |||
| 138 | pub fn noreturnReturned() noreturn { | 143 | pub fn noreturnReturned() noreturn { |
| 139 | @branchHint(.cold); | 144 | @branchHint(.cold); |
| 140 | @trap(); | 145 | @trap(); |
lib/std/debug/simple_panic.zig+4| ... | @@ -128,6 +128,10 @@ pub fn memcpyAlias() noreturn { | ... | @@ -128,6 +128,10 @@ pub fn memcpyAlias() noreturn { |
| 128 | call("@memcpy arguments alias", null); | 128 | call("@memcpy arguments alias", null); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | pub fn memmoveLenMismatch() noreturn { | ||
| 132 | call("@memmove arguments have non-equal lengths", null); | ||
| 133 | } | ||
| 134 | |||
| 131 | pub fn noreturnReturned() noreturn { | 135 | pub fn noreturnReturned() noreturn { |
| 132 | call("'noreturn' function returned", null); | 136 | call("'noreturn' function returned", null); |
| 133 | } | 137 | } |
lib/std/mem.zig+2| ... | @@ -232,6 +232,7 @@ test "Allocator alloc and remap with zero-bit type" { | ... | @@ -232,6 +232,7 @@ test "Allocator alloc and remap with zero-bit type" { |
| 232 | /// Copy all of source into dest at position 0. | 232 | /// Copy all of source into dest at position 0. |
| 233 | /// dest.len must be >= source.len. | 233 | /// dest.len must be >= source.len. |
| 234 | /// If the slices overlap, dest.ptr must be <= src.ptr. | 234 | /// If the slices overlap, dest.ptr must be <= src.ptr. |
| 235 | /// This function is deprecated; use @memmove instead. | ||
| 235 | pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void { | 236 | pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void { |
| 236 | for (dest[0..source.len], source) |*d, s| d.* = s; | 237 | for (dest[0..source.len], source) |*d, s| d.* = s; |
| 237 | } | 238 | } |
| ... | @@ -239,6 +240,7 @@ pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void { | ... | @@ -239,6 +240,7 @@ pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void { |
| 239 | /// Copy all of source into dest at position 0. | 240 | /// Copy all of source into dest at position 0. |
| 240 | /// dest.len must be >= source.len. | 241 | /// dest.len must be >= source.len. |
| 241 | /// If the slices overlap, dest.ptr must be >= src.ptr. | 242 | /// If the slices overlap, dest.ptr must be >= src.ptr. |
| 243 | /// This function is deprecated; use @memmove instead. | ||
| 242 | pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { | 244 | pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { |
| 243 | // TODO instead of manually doing this check for the whole array | 245 | // TODO instead of manually doing this check for the whole array |
| 244 | // and turning off runtime safety, the compiler should detect loops like | 246 | // and turning off runtime safety, the compiler should detect loops like |
lib/std/zig/AstGen.zig+8| ... | @@ -2919,6 +2919,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2919,6 +2919,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2919 | .set_runtime_safety, | 2919 | .set_runtime_safety, |
| 2920 | .memcpy, | 2920 | .memcpy, |
| 2921 | .memset, | 2921 | .memset, |
| 2922 | .memmove, | ||
| 2922 | .validate_deref, | 2923 | .validate_deref, |
| 2923 | .validate_destructure, | 2924 | .validate_destructure, |
| 2924 | .save_err_ret_index, | 2925 | .save_err_ret_index, |
| ... | @@ -9717,6 +9718,13 @@ fn builtinCall( | ... | @@ -9717,6 +9718,13 @@ fn builtinCall( |
| 9717 | }); | 9718 | }); |
| 9718 | return rvalue(gz, ri, .void_value, node); | 9719 | return rvalue(gz, ri, .void_value, node); |
| 9719 | }, | 9720 | }, |
| 9721 | .memmove => { | ||
| 9722 | _ = try gz.addPlNode(.memmove, node, Zir.Inst.Bin{ | ||
| 9723 | .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), | ||
| 9724 | .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), | ||
| 9725 | }); | ||
| 9726 | return rvalue(gz, ri, .void_value, node); | ||
| 9727 | }, | ||
| 9720 | .shuffle => { | 9728 | .shuffle => { |
| 9721 | const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ | 9729 | const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ |
| 9722 | .elem_type = try typeExpr(gz, scope, params[0]), | 9730 | .elem_type = try typeExpr(gz, scope, params[0]), |
lib/std/zig/AstRlAnnotate.zig+1-1| ... | @@ -1055,7 +1055,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. | ... | @@ -1055,7 +1055,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. |
| 1055 | _ = try astrl.expr(args[2], block, ResultInfo.none); | 1055 | _ = try astrl.expr(args[2], block, ResultInfo.none); |
| 1056 | return false; | 1056 | return false; |
| 1057 | }, | 1057 | }, |
| 1058 | .memcpy => { | 1058 | .memcpy, .memmove => { |
| 1059 | _ = try astrl.expr(args[0], block, ResultInfo.none); | 1059 | _ = try astrl.expr(args[0], block, ResultInfo.none); |
| 1060 | _ = try astrl.expr(args[1], block, ResultInfo.none); | 1060 | _ = try astrl.expr(args[1], block, ResultInfo.none); |
| 1061 | return false; | 1061 | return false; |
lib/std/zig/BuiltinFn.zig+8| ... | @@ -68,6 +68,7 @@ pub const Tag = enum { | ... | @@ -68,6 +68,7 @@ pub const Tag = enum { |
| 68 | max, | 68 | max, |
| 69 | memcpy, | 69 | memcpy, |
| 70 | memset, | 70 | memset, |
| 71 | memmove, | ||
| 71 | min, | 72 | min, |
| 72 | wasm_memory_size, | 73 | wasm_memory_size, |
| 73 | wasm_memory_grow, | 74 | wasm_memory_grow, |
| ... | @@ -641,6 +642,13 @@ pub const list = list: { | ... | @@ -641,6 +642,13 @@ pub const list = list: { |
| 641 | .param_count = 2, | 642 | .param_count = 2, |
| 642 | }, | 643 | }, |
| 643 | }, | 644 | }, |
| 645 | .{ | ||
| 646 | "@memmove", | ||
| 647 | .{ | ||
| 648 | .tag = .memmove, | ||
| 649 | .param_count = 2, | ||
| 650 | }, | ||
| 651 | }, | ||
| 644 | .{ | 652 | .{ |
| 645 | "@min", | 653 | "@min", |
| 646 | .{ | 654 | .{ |
lib/std/zig/Zir.zig+7| ... | @@ -986,6 +986,9 @@ pub const Inst = struct { | ... | @@ -986,6 +986,9 @@ pub const Inst = struct { |
| 986 | /// Implements the `@memset` builtin. | 986 | /// Implements the `@memset` builtin. |
| 987 | /// Uses the `pl_node` union field with payload `Bin`. | 987 | /// Uses the `pl_node` union field with payload `Bin`. |
| 988 | memset, | 988 | memset, |
| 989 | /// Implements the `@memmove` builtin. | ||
| 990 | /// Uses the `pl_node` union field with payload `Bin`. | ||
| 991 | memmove, | ||
| 989 | /// Implements the `@min` builtin for 2 args. | 992 | /// Implements the `@min` builtin for 2 args. |
| 990 | /// Uses the `pl_node` union field with payload `Bin` | 993 | /// Uses the `pl_node` union field with payload `Bin` |
| 991 | min, | 994 | min, |
| ... | @@ -1272,6 +1275,7 @@ pub const Inst = struct { | ... | @@ -1272,6 +1275,7 @@ pub const Inst = struct { |
| 1272 | .max, | 1275 | .max, |
| 1273 | .memcpy, | 1276 | .memcpy, |
| 1274 | .memset, | 1277 | .memset, |
| 1278 | .memmove, | ||
| 1275 | .min, | 1279 | .min, |
| 1276 | .c_import, | 1280 | .c_import, |
| 1277 | .@"resume", | 1281 | .@"resume", |
| ... | @@ -1355,6 +1359,7 @@ pub const Inst = struct { | ... | @@ -1355,6 +1359,7 @@ pub const Inst = struct { |
| 1355 | .set_runtime_safety, | 1359 | .set_runtime_safety, |
| 1356 | .memcpy, | 1360 | .memcpy, |
| 1357 | .memset, | 1361 | .memset, |
| 1362 | .memmove, | ||
| 1358 | .check_comptime_control_flow, | 1363 | .check_comptime_control_flow, |
| 1359 | .@"defer", | 1364 | .@"defer", |
| 1360 | .defer_err_code, | 1365 | .defer_err_code, |
| ... | @@ -1832,6 +1837,7 @@ pub const Inst = struct { | ... | @@ -1832,6 +1837,7 @@ pub const Inst = struct { |
| 1832 | .max = .pl_node, | 1837 | .max = .pl_node, |
| 1833 | .memcpy = .pl_node, | 1838 | .memcpy = .pl_node, |
| 1834 | .memset = .pl_node, | 1839 | .memset = .pl_node, |
| 1840 | .memmove = .pl_node, | ||
| 1835 | .min = .pl_node, | 1841 | .min = .pl_node, |
| 1836 | .c_import = .pl_node, | 1842 | .c_import = .pl_node, |
| 1837 | 1843 | ||
| ... | @@ -4291,6 +4297,7 @@ fn findTrackableInner( | ... | @@ -4291,6 +4297,7 @@ fn findTrackableInner( |
| 4291 | .mul_add, | 4297 | .mul_add, |
| 4292 | .memcpy, | 4298 | .memcpy, |
| 4293 | .memset, | 4299 | .memset, |
| 4300 | .memmove, | ||
| 4294 | .min, | 4301 | .min, |
| 4295 | .max, | 4302 | .max, |
| 4296 | .alloc, | 4303 | .alloc, |
lib/std/zig/llvm/Builder.zig+30| ... | @@ -6125,6 +6125,36 @@ pub const WipFunction = struct { | ... | @@ -6125,6 +6125,36 @@ pub const WipFunction = struct { |
| 6125 | return value.unwrap().instruction; | 6125 | return value.unwrap().instruction; |
| 6126 | } | 6126 | } |
| 6127 | 6127 | ||
| 6128 | pub fn callMemMove( | ||
| 6129 | self: *WipFunction, | ||
| 6130 | dst: Value, | ||
| 6131 | dst_align: Alignment, | ||
| 6132 | src: Value, | ||
| 6133 | src_align: Alignment, | ||
| 6134 | len: Value, | ||
| 6135 | kind: MemoryAccessKind, | ||
| 6136 | ) Allocator.Error!Instruction.Index { | ||
| 6137 | var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; | ||
| 6138 | var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; | ||
| 6139 | const value = try self.callIntrinsic( | ||
| 6140 | .normal, | ||
| 6141 | try self.builder.fnAttrs(&.{ | ||
| 6142 | .none, | ||
| 6143 | .none, | ||
| 6144 | try self.builder.attrs(&dst_attrs), | ||
| 6145 | try self.builder.attrs(&src_attrs), | ||
| 6146 | }), | ||
| 6147 | .memmove, | ||
| 6148 | &.{ dst.typeOfWip(self), src.typeOfWip(self), len.typeOfWip(self) }, | ||
| 6149 | &.{ dst, src, len, switch (kind) { | ||
| 6150 | .normal => Value.false, | ||
| 6151 | .@"volatile" => Value.true, | ||
| 6152 | } }, | ||
| 6153 | undefined, | ||
| 6154 | ); | ||
| 6155 | return value.unwrap().instruction; | ||
| 6156 | } | ||
| 6157 | |||
| 6128 | pub fn callMemSet( | 6158 | pub fn callMemSet( |
| 6129 | self: *WipFunction, | 6159 | self: *WipFunction, |
| 6130 | dst: Value, | 6160 | dst: Value, |
lib/zig.h+1| ... | @@ -481,6 +481,7 @@ | ... | @@ -481,6 +481,7 @@ |
| 481 | 481 | ||
| 482 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); | 482 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); |
| 483 | zig_extern void *memset (void *, int, size_t); | 483 | zig_extern void *memset (void *, int, size_t); |
| 484 | zig_extern void *memmove (void *, void const *, size_t); | ||
| 484 | 485 | ||
| 485 | /* ================ Bool and 8/16/32/64-bit Integer Support ================= */ | 486 | /* ================ Bool and 8/16/32/64-bit Integer Support ================= */ |
| 486 | 487 |
src/Air.zig+14| ... | @@ -730,6 +730,18 @@ pub const Inst = struct { | ... | @@ -730,6 +730,18 @@ pub const Inst = struct { |
| 730 | /// source being a pointer-to-array), then it is guaranteed to be | 730 | /// source being a pointer-to-array), then it is guaranteed to be |
| 731 | /// greater than zero. | 731 | /// greater than zero. |
| 732 | memcpy, | 732 | memcpy, |
| 733 | /// Given dest pointer and source pointer, copy elements from source to dest. | ||
| 734 | /// Dest pointer is either a slice or a pointer to array. | ||
| 735 | /// The dest element type may be any type. | ||
| 736 | /// Source pointer must have same element type as dest element type. | ||
| 737 | /// Dest slice may have any alignment; source pointer may have any alignment. | ||
| 738 | /// The two memory regions may overlap. | ||
| 739 | /// Result type is always void. | ||
| 740 | /// Uses the `bin_op` field. LHS is the dest slice. RHS is the source pointer. | ||
| 741 | /// If the length is compile-time known (due to the destination or | ||
| 742 | /// source being a pointer-to-array), then it is guaranteed to be | ||
| 743 | /// greater than zero. | ||
| 744 | memmove, | ||
| 733 | 745 | ||
| 734 | /// Uses the `ty_pl` field with payload `Cmpxchg`. | 746 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| 735 | cmpxchg_weak, | 747 | cmpxchg_weak, |
| ... | @@ -1533,6 +1545,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) | ... | @@ -1533,6 +1545,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1533 | .memset, | 1545 | .memset, |
| 1534 | .memset_safe, | 1546 | .memset_safe, |
| 1535 | .memcpy, | 1547 | .memcpy, |
| 1548 | .memmove, | ||
| 1536 | .set_union_tag, | 1549 | .set_union_tag, |
| 1537 | .prefetch, | 1550 | .prefetch, |
| 1538 | .set_err_return_trace, | 1551 | .set_err_return_trace, |
| ... | @@ -1696,6 +1709,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { | ... | @@ -1696,6 +1709,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1696 | .memset, | 1709 | .memset, |
| 1697 | .memset_safe, | 1710 | .memset_safe, |
| 1698 | .memcpy, | 1711 | .memcpy, |
| 1712 | .memmove, | ||
| 1699 | .cmpxchg_weak, | 1713 | .cmpxchg_weak, |
| 1700 | .cmpxchg_strong, | 1714 | .cmpxchg_strong, |
| 1701 | .atomic_store_unordered, | 1715 | .atomic_store_unordered, |
src/Air/types_resolved.zig+1| ... | @@ -83,6 +83,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { | ... | @@ -83,6 +83,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool { |
| 83 | .memset, | 83 | .memset, |
| 84 | .memset_safe, | 84 | .memset_safe, |
| 85 | .memcpy, | 85 | .memcpy, |
| 86 | .memmove, | ||
| 86 | .atomic_store_unordered, | 87 | .atomic_store_unordered, |
| 87 | .atomic_store_monotonic, | 88 | .atomic_store_monotonic, |
| 88 | .atomic_store_release, | 89 | .atomic_store_release, |
src/Liveness.zig+2| ... | @@ -300,6 +300,7 @@ pub fn categorizeOperand( | ... | @@ -300,6 +300,7 @@ pub fn categorizeOperand( |
| 300 | .memset, | 300 | .memset, |
| 301 | .memset_safe, | 301 | .memset_safe, |
| 302 | .memcpy, | 302 | .memcpy, |
| 303 | .memmove, | ||
| 303 | => { | 304 | => { |
| 304 | const o = air_datas[@intFromEnum(inst)].bin_op; | 305 | const o = air_datas[@intFromEnum(inst)].bin_op; |
| 305 | if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); | 306 | if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| ... | @@ -936,6 +937,7 @@ fn analyzeInst( | ... | @@ -936,6 +937,7 @@ fn analyzeInst( |
| 936 | .memset, | 937 | .memset, |
| 937 | .memset_safe, | 938 | .memset_safe, |
| 938 | .memcpy, | 939 | .memcpy, |
| 940 | .memmove, | ||
| 939 | => { | 941 | => { |
| 940 | const o = inst_datas[@intFromEnum(inst)].bin_op; | 942 | const o = inst_datas[@intFromEnum(inst)].bin_op; |
| 941 | return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none }); | 943 | return analyzeOperands(a, pass, data, inst, .{ o.lhs, o.rhs, .none }); |
src/Liveness/Verify.zig+1| ... | @@ -267,6 +267,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { | ... | @@ -267,6 +267,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { |
| 267 | .memset, | 267 | .memset, |
| 268 | .memset_safe, | 268 | .memset_safe, |
| 269 | .memcpy, | 269 | .memcpy, |
| 270 | .memmove, | ||
| 270 | => { | 271 | => { |
| 271 | const bin_op = data[@intFromEnum(inst)].bin_op; | 272 | const bin_op = data[@intFromEnum(inst)].bin_op; |
| 272 | try self.verifyInstOperands(inst, .{ bin_op.lhs, bin_op.rhs, .none }); | 273 | try self.verifyInstOperands(inst, .{ bin_op.lhs, bin_op.rhs, .none }); |
src/Sema.zig+45-12| ... | @@ -1583,6 +1583,11 @@ fn analyzeBodyInner( | ... | @@ -1583,6 +1583,11 @@ fn analyzeBodyInner( |
| 1583 | i += 1; | 1583 | i += 1; |
| 1584 | continue; | 1584 | continue; |
| 1585 | }, | 1585 | }, |
| 1586 | .memmove => { | ||
| 1587 | try sema.zirMemmove(block, inst); | ||
| 1588 | i += 1; | ||
| 1589 | continue; | ||
| 1590 | }, | ||
| 1586 | .check_comptime_control_flow => { | 1591 | .check_comptime_control_flow => { |
| 1587 | if (!block.isComptime()) { | 1592 | if (!block.isComptime()) { |
| 1588 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | 1593 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| ... | @@ -25610,6 +25615,19 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A | ... | @@ -25610,6 +25615,19 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A |
| 25610 | } | 25615 | } |
| 25611 | 25616 | ||
| 25612 | fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | 25617 | fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 25618 | return sema.analyzeCopy(block, inst, .memcpy); | ||
| 25619 | } | ||
| 25620 | |||
| 25621 | fn zirMemmove(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | ||
| 25622 | return sema.analyzeCopy(block, inst, .memmove); | ||
| 25623 | } | ||
| 25624 | |||
| 25625 | fn analyzeCopy( | ||
| 25626 | sema: *Sema, | ||
| 25627 | block: *Block, | ||
| 25628 | inst: Zir.Inst.Index, | ||
| 25629 | op: enum { memcpy, memmove }, | ||
| 25630 | ) CompileError!void { | ||
| 25613 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; | 25631 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 25614 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | 25632 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 25615 | const src = block.nodeOffset(inst_data.src_node); | 25633 | const src = block.nodeOffset(inst_data.src_node); |
| ... | @@ -25625,12 +25643,12 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25625,12 +25643,12 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25625 | const zcu = pt.zcu; | 25643 | const zcu = pt.zcu; |
| 25626 | 25644 | ||
| 25627 | if (dest_ty.isConstPtr(zcu)) { | 25645 | if (dest_ty.isConstPtr(zcu)) { |
| 25628 | return sema.fail(block, dest_src, "cannot memcpy to constant pointer", .{}); | 25646 | return sema.fail(block, dest_src, "cannot {s} to constant pointer", .{@tagName(op)}); |
| 25629 | } | 25647 | } |
| 25630 | 25648 | ||
| 25631 | if (dest_len == .none and src_len == .none) { | 25649 | if (dest_len == .none and src_len == .none) { |
| 25632 | const msg = msg: { | 25650 | const msg = msg: { |
| 25633 | const msg = try sema.errMsg(src, "unknown @memcpy length", .{}); | 25651 | const msg = try sema.errMsg(src, "unknown @{s} length", .{@tagName(op)}); |
| 25634 | errdefer msg.destroy(sema.gpa); | 25652 | errdefer msg.destroy(sema.gpa); |
| 25635 | try sema.errNote(dest_src, msg, "destination type '{}' provides no length", .{ | 25653 | try sema.errNote(dest_src, msg, "destination type '{}' provides no length", .{ |
| 25636 | dest_ty.fmt(pt), | 25654 | dest_ty.fmt(pt), |
| ... | @@ -25676,7 +25694,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25676,7 +25694,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25676 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { | 25694 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { |
| 25677 | if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { | 25695 | if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { |
| 25678 | const msg = msg: { | 25696 | const msg = msg: { |
| 25679 | const msg = try sema.errMsg(src, "non-matching @memcpy lengths", .{}); | 25697 | const msg = try sema.errMsg(src, "non-matching @{s} lengths", .{@tagName(op)}); |
| 25680 | errdefer msg.destroy(sema.gpa); | 25698 | errdefer msg.destroy(sema.gpa); |
| 25681 | try sema.errNote(dest_src, msg, "length {} here", .{ | 25699 | try sema.errNote(dest_src, msg, "length {} here", .{ |
| 25682 | dest_len_val.fmtValueSema(pt, sema), | 25700 | dest_len_val.fmtValueSema(pt, sema), |
| ... | @@ -25696,7 +25714,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25696,7 +25714,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25696 | 25714 | ||
| 25697 | if (block.wantSafety()) { | 25715 | if (block.wantSafety()) { |
| 25698 | const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); | 25716 | const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); |
| 25699 | try sema.addSafetyCheck(block, src, ok, .memcpy_len_mismatch); | 25717 | const panic_id: Zcu.SimplePanicId = switch (op) { |
| 25718 | .memcpy => .memcpy_len_mismatch, | ||
| 25719 | .memmove => .memmove_len_mismatch, | ||
| 25720 | }; | ||
| 25721 | try sema.addSafetyCheck(block, src, ok, panic_id); | ||
| 25700 | } | 25722 | } |
| 25701 | } else if (dest_len != .none) { | 25723 | } else if (dest_len != .none) { |
| 25702 | if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { | 25724 | if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { |
| ... | @@ -25724,6 +25746,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25724,6 +25746,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25724 | return; | 25746 | return; |
| 25725 | } | 25747 | } |
| 25726 | 25748 | ||
| 25749 | const check_aliasing = switch (op) { | ||
| 25750 | .memcpy => true, | ||
| 25751 | .memmove => false, | ||
| 25752 | }; | ||
| 25753 | |||
| 25727 | const runtime_src = rs: { | 25754 | const runtime_src = rs: { |
| 25728 | const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; | 25755 | const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; |
| 25729 | const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; | 25756 | const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; |
| ... | @@ -25733,12 +25760,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25733,12 +25760,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25733 | 25760 | ||
| 25734 | const len_u64 = try len_val.?.toUnsignedIntSema(pt); | 25761 | const len_u64 = try len_val.?.toUnsignedIntSema(pt); |
| 25735 | 25762 | ||
| 25736 | if (Value.doPointersOverlap( | 25763 | if (check_aliasing) { |
| 25737 | raw_src_ptr, | 25764 | if (Value.doPointersOverlap( |
| 25738 | raw_dest_ptr, | 25765 | raw_src_ptr, |
| 25739 | len_u64, | 25766 | raw_dest_ptr, |
| 25740 | zcu, | 25767 | len_u64, |
| 25741 | )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); | 25768 | zcu, |
| 25769 | )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); | ||
| 25770 | } | ||
| 25742 | 25771 | ||
| 25743 | if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src; | 25772 | if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src; |
| 25744 | 25773 | ||
| ... | @@ -25810,7 +25839,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25810,7 +25839,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25810 | try sema.validateRuntimeValue(block, src_src, src_ptr); | 25839 | try sema.validateRuntimeValue(block, src_src, src_ptr); |
| 25811 | 25840 | ||
| 25812 | // Aliasing safety check. | 25841 | // Aliasing safety check. |
| 25813 | if (block.wantSafety()) { | 25842 | if (check_aliasing and block.wantSafety()) { |
| 25814 | const len = if (len_val) |v| | 25843 | const len = if (len_val) |v| |
| 25815 | Air.internedToRef(v.toIntern()) | 25844 | Air.internedToRef(v.toIntern()) |
| 25816 | else if (dest_len != .none) | 25845 | else if (dest_len != .none) |
| ... | @@ -25853,7 +25882,10 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -25853,7 +25882,10 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25853 | } | 25882 | } |
| 25854 | 25883 | ||
| 25855 | _ = try block.addInst(.{ | 25884 | _ = try block.addInst(.{ |
| 25856 | .tag = .memcpy, | 25885 | .tag = switch (op) { |
| 25886 | .memcpy => .memcpy, | ||
| 25887 | .memmove => .memmove, | ||
| 25888 | }, | ||
| 25857 | .data = .{ .bin_op = .{ | 25889 | .data = .{ .bin_op = .{ |
| 25858 | .lhs = new_dest_ptr, | 25890 | .lhs = new_dest_ptr, |
| 25859 | .rhs = new_src_ptr, | 25891 | .rhs = new_src_ptr, |
| ... | @@ -38078,6 +38110,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ | ... | @@ -38078,6 +38110,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ |
| 38078 | .@"panic.forLenMismatch", | 38110 | .@"panic.forLenMismatch", |
| 38079 | .@"panic.memcpyLenMismatch", | 38111 | .@"panic.memcpyLenMismatch", |
| 38080 | .@"panic.memcpyAlias", | 38112 | .@"panic.memcpyAlias", |
| 38113 | .@"panic.memmoveLenMismatch", | ||
| 38081 | .@"panic.noreturnReturned", | 38114 | .@"panic.noreturnReturned", |
| 38082 | => try pt.funcType(.{ | 38115 | => try pt.funcType(.{ |
| 38083 | .param_types = &.{}, | 38116 | .param_types = &.{}, |
src/Zcu.zig+4| ... | @@ -302,6 +302,7 @@ pub const BuiltinDecl = enum { | ... | @@ -302,6 +302,7 @@ pub const BuiltinDecl = enum { |
| 302 | @"panic.forLenMismatch", | 302 | @"panic.forLenMismatch", |
| 303 | @"panic.memcpyLenMismatch", | 303 | @"panic.memcpyLenMismatch", |
| 304 | @"panic.memcpyAlias", | 304 | @"panic.memcpyAlias", |
| 305 | @"panic.memmoveLenMismatch", | ||
| 305 | @"panic.noreturnReturned", | 306 | @"panic.noreturnReturned", |
| 306 | 307 | ||
| 307 | VaList, | 308 | VaList, |
| ... | @@ -379,6 +380,7 @@ pub const BuiltinDecl = enum { | ... | @@ -379,6 +380,7 @@ pub const BuiltinDecl = enum { |
| 379 | .@"panic.forLenMismatch", | 380 | .@"panic.forLenMismatch", |
| 380 | .@"panic.memcpyLenMismatch", | 381 | .@"panic.memcpyLenMismatch", |
| 381 | .@"panic.memcpyAlias", | 382 | .@"panic.memcpyAlias", |
| 383 | .@"panic.memmoveLenMismatch", | ||
| 382 | .@"panic.noreturnReturned", | 384 | .@"panic.noreturnReturned", |
| 383 | => .func, | 385 | => .func, |
| 384 | }; | 386 | }; |
| ... | @@ -446,6 +448,7 @@ pub const SimplePanicId = enum { | ... | @@ -446,6 +448,7 @@ pub const SimplePanicId = enum { |
| 446 | for_len_mismatch, | 448 | for_len_mismatch, |
| 447 | memcpy_len_mismatch, | 449 | memcpy_len_mismatch, |
| 448 | memcpy_alias, | 450 | memcpy_alias, |
| 451 | memmove_len_mismatch, | ||
| 449 | noreturn_returned, | 452 | noreturn_returned, |
| 450 | 453 | ||
| 451 | pub fn toBuiltin(id: SimplePanicId) BuiltinDecl { | 454 | pub fn toBuiltin(id: SimplePanicId) BuiltinDecl { |
| ... | @@ -470,6 +473,7 @@ pub const SimplePanicId = enum { | ... | @@ -470,6 +473,7 @@ pub const SimplePanicId = enum { |
| 470 | .for_len_mismatch => .@"panic.forLenMismatch", | 473 | .for_len_mismatch => .@"panic.forLenMismatch", |
| 471 | .memcpy_len_mismatch => .@"panic.memcpyLenMismatch", | 474 | .memcpy_len_mismatch => .@"panic.memcpyLenMismatch", |
| 472 | .memcpy_alias => .@"panic.memcpyAlias", | 475 | .memcpy_alias => .@"panic.memcpyAlias", |
| 476 | .memmove_len_mismatch => .@"panic.memmoveLenMismatch", | ||
| 473 | .noreturn_returned => .@"panic.noreturnReturned", | 477 | .noreturn_returned => .@"panic.noreturnReturned", |
| 474 | // zig fmt: on | 478 | // zig fmt: on |
| 475 | }; | 479 | }; |
src/arch/aarch64/CodeGen.zig+6| ... | @@ -760,6 +760,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -760,6 +760,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 760 | .atomic_rmw => try self.airAtomicRmw(inst), | 760 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 761 | .atomic_load => try self.airAtomicLoad(inst), | 761 | .atomic_load => try self.airAtomicLoad(inst), |
| 762 | .memcpy => try self.airMemcpy(inst), | 762 | .memcpy => try self.airMemcpy(inst), |
| 763 | .memmove => try self.airMemmove(inst), | ||
| 763 | .memset => try self.airMemset(inst, false), | 764 | .memset => try self.airMemset(inst, false), |
| 764 | .memset_safe => try self.airMemset(inst, true), | 765 | .memset_safe => try self.airMemset(inst, true), |
| 765 | .set_union_tag => try self.airSetUnionTag(inst), | 766 | .set_union_tag => try self.airSetUnionTag(inst), |
| ... | @@ -5993,6 +5994,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5993,6 +5994,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5993 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); | 5994 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); |
| 5994 | } | 5995 | } |
| 5995 | 5996 | ||
| 5997 | fn airMemmove(self: *Self, inst: Air.Inst.Index) InnerError!void { | ||
| 5998 | _ = inst; | ||
| 5999 | return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch}); | ||
| 6000 | } | ||
| 6001 | |||
| 5996 | fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void { | 6002 | fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5997 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 6003 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5998 | const operand = try self.resolveInst(un_op); | 6004 | const operand = try self.resolveInst(un_op); |
src/arch/arm/CodeGen.zig+6| ... | @@ -749,6 +749,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -749,6 +749,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 749 | .atomic_rmw => try self.airAtomicRmw(inst), | 749 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 750 | .atomic_load => try self.airAtomicLoad(inst), | 750 | .atomic_load => try self.airAtomicLoad(inst), |
| 751 | .memcpy => try self.airMemcpy(inst), | 751 | .memcpy => try self.airMemcpy(inst), |
| 752 | .memmove => try self.airMemmove(inst), | ||
| 752 | .memset => try self.airMemset(inst, false), | 753 | .memset => try self.airMemset(inst, false), |
| 753 | .memset_safe => try self.airMemset(inst, true), | 754 | .memset_safe => try self.airMemset(inst, true), |
| 754 | .set_union_tag => try self.airSetUnionTag(inst), | 755 | .set_union_tag => try self.airSetUnionTag(inst), |
| ... | @@ -5963,6 +5964,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5963,6 +5964,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 5963 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); | 5964 | return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); |
| 5964 | } | 5965 | } |
| 5965 | 5966 | ||
| 5967 | fn airMemmove(self: *Self, inst: Air.Inst.Index) !void { | ||
| 5968 | _ = inst; | ||
| 5969 | return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch}); | ||
| 5970 | } | ||
| 5971 | |||
| 5966 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { | 5972 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 5967 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 5973 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5968 | const operand = try self.resolveInst(un_op); | 5974 | const operand = try self.resolveInst(un_op); |
src/arch/riscv64/CodeGen.zig+6| ... | @@ -1581,6 +1581,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -1581,6 +1581,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1581 | .atomic_rmw => try func.airAtomicRmw(inst), | 1581 | .atomic_rmw => try func.airAtomicRmw(inst), |
| 1582 | .atomic_load => try func.airAtomicLoad(inst), | 1582 | .atomic_load => try func.airAtomicLoad(inst), |
| 1583 | .memcpy => try func.airMemcpy(inst), | 1583 | .memcpy => try func.airMemcpy(inst), |
| 1584 | .memmove => try func.airMemmove(inst), | ||
| 1584 | .memset => try func.airMemset(inst, false), | 1585 | .memset => try func.airMemset(inst, false), |
| 1585 | .memset_safe => try func.airMemset(inst, true), | 1586 | .memset_safe => try func.airMemset(inst, true), |
| 1586 | .set_union_tag => try func.airSetUnionTag(inst), | 1587 | .set_union_tag => try func.airSetUnionTag(inst), |
| ... | @@ -7919,6 +7920,11 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void { | ... | @@ -7919,6 +7920,11 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void { |
| 7919 | return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); | 7920 | return func.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 7920 | } | 7921 | } |
| 7921 | 7922 | ||
| 7923 | fn airMemmove(func: *Func, inst: Air.Inst.Index) !void { | ||
| 7924 | _ = inst; | ||
| 7925 | return func.fail("TODO implement airMemmove for riscv64", .{}); | ||
| 7926 | } | ||
| 7927 | |||
| 7922 | fn airTagName(func: *Func, inst: Air.Inst.Index) !void { | 7928 | fn airTagName(func: *Func, inst: Air.Inst.Index) !void { |
| 7923 | const pt = func.pt; | 7929 | const pt = func.pt; |
| 7924 | 7930 |
src/arch/sparc64/CodeGen.zig+1| ... | @@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -604,6 +604,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 604 | .atomic_rmw => try self.airAtomicRmw(inst), | 604 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 605 | .atomic_load => try self.airAtomicLoad(inst), | 605 | .atomic_load => try self.airAtomicLoad(inst), |
| 606 | .memcpy => @panic("TODO try self.airMemcpy(inst)"), | 606 | .memcpy => @panic("TODO try self.airMemcpy(inst)"), |
| 607 | .memmove => @panic("TODO try self.airMemmove(inst)"), | ||
| 607 | .memset => try self.airMemset(inst, false), | 608 | .memset => try self.airMemset(inst, false), |
| 608 | .memset_safe => try self.airMemset(inst, true), | 609 | .memset_safe => try self.airMemset(inst, true), |
| 609 | .set_union_tag => try self.airSetUnionTag(inst), | 610 | .set_union_tag => try self.airSetUnionTag(inst), |
src/arch/wasm/CodeGen.zig+1| ... | @@ -2061,6 +2061,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2061,6 +2061,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2061 | .c_va_copy, | 2061 | .c_va_copy, |
| 2062 | .c_va_end, | 2062 | .c_va_end, |
| 2063 | .c_va_start, | 2063 | .c_va_start, |
| 2064 | .memmove, | ||
| 2064 | => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), | 2065 | => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 2065 | 2066 | ||
| 2066 | .atomic_load => cg.airAtomicLoad(inst), | 2067 | .atomic_load => cg.airAtomicLoad(inst), |
src/arch/x86_64/CodeGen.zig+6| ... | @@ -89453,6 +89453,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -89453,6 +89453,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { |
| 89453 | .memset => try cg.airMemset(inst, false), | 89453 | .memset => try cg.airMemset(inst, false), |
| 89454 | .memset_safe => try cg.airMemset(inst, true), | 89454 | .memset_safe => try cg.airMemset(inst, true), |
| 89455 | .memcpy => try cg.airMemcpy(inst), | 89455 | .memcpy => try cg.airMemcpy(inst), |
| 89456 | .memmove => try cg.airMemmove(inst), | ||
| 89456 | .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst), | 89457 | .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst), |
| 89457 | .atomic_load => try cg.airAtomicLoad(inst), | 89458 | .atomic_load => try cg.airAtomicLoad(inst), |
| 89458 | .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered), | 89459 | .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered), |
| ... | @@ -106472,6 +106473,11 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void { | ... | @@ -106472,6 +106473,11 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void { |
| 106472 | return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); | 106473 | return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 106473 | } | 106474 | } |
| 106474 | 106475 | ||
| 106476 | fn airMemmove(self: *CodeGen, inst: Air.Inst.Index) !void { | ||
| 106477 | _ = inst; | ||
| 106478 | return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch}); | ||
| 106479 | } | ||
| 106480 | |||
| 106475 | fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void { | 106481 | fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void { |
| 106476 | const pt = self.pt; | 106482 | const pt = self.pt; |
| 106477 | const zcu = pt.zcu; | 106483 | const zcu = pt.zcu; |
src/codegen/c.zig+14-1| ... | @@ -3349,6 +3349,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -3349,6 +3349,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 3349 | .memset => try airMemset(f, inst, false), | 3349 | .memset => try airMemset(f, inst, false), |
| 3350 | .memset_safe => try airMemset(f, inst, true), | 3350 | .memset_safe => try airMemset(f, inst, true), |
| 3351 | .memcpy => try airMemcpy(f, inst), | 3351 | .memcpy => try airMemcpy(f, inst), |
| 3352 | .memmove => try airMemmove(f, inst), | ||
| 3352 | .set_union_tag => try airSetUnionTag(f, inst), | 3353 | .set_union_tag => try airSetUnionTag(f, inst), |
| 3353 | .get_union_tag => try airGetUnionTag(f, inst), | 3354 | .get_union_tag => try airGetUnionTag(f, inst), |
| 3354 | .clz => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "clz", .bits), | 3355 | .clz => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "clz", .bits), |
| ... | @@ -6976,6 +6977,14 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { | ... | @@ -6976,6 +6977,14 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { |
| 6976 | } | 6977 | } |
| 6977 | 6978 | ||
| 6978 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { | 6979 | fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6980 | return copyOp(f, inst, .memcpy); | ||
| 6981 | } | ||
| 6982 | |||
| 6983 | fn airMemmove(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 6984 | return copyOp(f, inst, .memmove); | ||
| 6985 | } | ||
| 6986 | |||
| 6987 | fn copyOp(f: *Function, inst: Air.Inst.Index, op: enum { memcpy, memmove }) !CValue { | ||
| 6979 | const pt = f.object.dg.pt; | 6988 | const pt = f.object.dg.pt; |
| 6980 | const zcu = pt.zcu; | 6989 | const zcu = pt.zcu; |
| 6981 | const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 6990 | const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | @@ -6990,7 +6999,11 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -6990,7 +6999,11 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue { |
| 6990 | try writeArrayLen(f, writer, dest_ptr, dest_ty); | 6999 | try writeArrayLen(f, writer, dest_ptr, dest_ty); |
| 6991 | try writer.writeAll(" != 0) "); | 7000 | try writer.writeAll(" != 0) "); |
| 6992 | } | 7001 | } |
| 6993 | try writer.writeAll("memcpy("); | 7002 | const function_paren = switch (op) { |
| 7003 | .memcpy => "memcpy(", | ||
| 7004 | .memmove => "memmove(", | ||
| 7005 | }; | ||
| 7006 | try writer.writeAll(function_paren); | ||
| 6994 | try writeSliceOrPtr(f, writer, dest_ptr, dest_ty); | 7007 | try writeSliceOrPtr(f, writer, dest_ptr, dest_ty); |
| 6995 | try writer.writeAll(", "); | 7008 | try writer.writeAll(", "); |
| 6996 | try writeSliceOrPtr(f, writer, src_ptr, src_ty); | 7009 | try writeSliceOrPtr(f, writer, src_ptr, src_ty); |
src/codegen/llvm.zig+27| ... | @@ -4940,6 +4940,7 @@ pub const FuncGen = struct { | ... | @@ -4940,6 +4940,7 @@ pub const FuncGen = struct { |
| 4940 | .memset => try self.airMemset(inst, false), | 4940 | .memset => try self.airMemset(inst, false), |
| 4941 | .memset_safe => try self.airMemset(inst, true), | 4941 | .memset_safe => try self.airMemset(inst, true), |
| 4942 | .memcpy => try self.airMemcpy(inst), | 4942 | .memcpy => try self.airMemcpy(inst), |
| 4943 | .memmove => try self.airMemmove(inst), | ||
| 4943 | .set_union_tag => try self.airSetUnionTag(inst), | 4944 | .set_union_tag => try self.airSetUnionTag(inst), |
| 4944 | .get_union_tag => try self.airGetUnionTag(inst), | 4945 | .get_union_tag => try self.airGetUnionTag(inst), |
| 4945 | .clz => try self.airClzCtz(inst, .ctlz), | 4946 | .clz => try self.airClzCtz(inst, .ctlz), |
| ... | @@ -9926,6 +9927,32 @@ pub const FuncGen = struct { | ... | @@ -9926,6 +9927,32 @@ pub const FuncGen = struct { |
| 9926 | return .none; | 9927 | return .none; |
| 9927 | } | 9928 | } |
| 9928 | 9929 | ||
| 9930 | fn airMemmove(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | ||
| 9931 | const o = self.ng.object; | ||
| 9932 | const pt = o.pt; | ||
| 9933 | const zcu = pt.zcu; | ||
| 9934 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | ||
| 9935 | const dest_slice = try self.resolveInst(bin_op.lhs); | ||
| 9936 | const dest_ptr_ty = self.typeOf(bin_op.lhs); | ||
| 9937 | const src_slice = try self.resolveInst(bin_op.rhs); | ||
| 9938 | const src_ptr_ty = self.typeOf(bin_op.rhs); | ||
| 9939 | const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ptr_ty); | ||
| 9940 | const len = try self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty); | ||
| 9941 | const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ptr_ty); | ||
| 9942 | const access_kind: Builder.MemoryAccessKind = if (src_ptr_ty.isVolatilePtr(zcu) or | ||
| 9943 | dest_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; | ||
| 9944 | |||
| 9945 | _ = try self.wip.callMemMove( | ||
| 9946 | dest_ptr, | ||
| 9947 | dest_ptr_ty.ptrAlignment(zcu).toLlvm(), | ||
| 9948 | src_ptr, | ||
| 9949 | src_ptr_ty.ptrAlignment(zcu).toLlvm(), | ||
| 9950 | len, | ||
| 9951 | access_kind, | ||
| 9952 | ); | ||
| 9953 | return .none; | ||
| 9954 | } | ||
| 9955 | |||
| 9929 | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { | 9956 | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9930 | const o = self.ng.object; | 9957 | const o = self.ng.object; |
| 9931 | const pt = o.pt; | 9958 | const pt = o.pt; |
src/codegen/spirv.zig+6| ... | @@ -3344,6 +3344,7 @@ const NavGen = struct { | ... | @@ -3344,6 +3344,7 @@ const NavGen = struct { |
| 3344 | .slice => try self.airSlice(inst), | 3344 | .slice => try self.airSlice(inst), |
| 3345 | .aggregate_init => try self.airAggregateInit(inst), | 3345 | .aggregate_init => try self.airAggregateInit(inst), |
| 3346 | .memcpy => return self.airMemcpy(inst), | 3346 | .memcpy => return self.airMemcpy(inst), |
| 3347 | .memmove => return self.airMemmove(inst), | ||
| 3347 | 3348 | ||
| 3348 | .slice_ptr => try self.airSliceField(inst, 0), | 3349 | .slice_ptr => try self.airSliceField(inst, 0), |
| 3349 | .slice_len => try self.airSliceField(inst, 1), | 3350 | .slice_len => try self.airSliceField(inst, 1), |
| ... | @@ -4914,6 +4915,11 @@ const NavGen = struct { | ... | @@ -4914,6 +4915,11 @@ const NavGen = struct { |
| 4914 | }); | 4915 | }); |
| 4915 | } | 4916 | } |
| 4916 | 4917 | ||
| 4918 | fn airMemmove(self: *NavGen, inst: Air.Inst.Index) !void { | ||
| 4919 | _ = inst; | ||
| 4920 | return self.fail("TODO implement airMemcpy for spirv", .{}); | ||
| 4921 | } | ||
| 4922 | |||
| 4917 | fn airSliceField(self: *NavGen, inst: Air.Inst.Index, field: u32) !?IdRef { | 4923 | fn airSliceField(self: *NavGen, inst: Air.Inst.Index, field: u32) !?IdRef { |
| 4918 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; | 4924 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4919 | const field_ty = self.typeOfIndex(inst); | 4925 | const field_ty = self.typeOfIndex(inst); |
src/print_air.zig+1| ... | @@ -160,6 +160,7 @@ const Writer = struct { | ... | @@ -160,6 +160,7 @@ const Writer = struct { |
| 160 | .cmp_gt_optimized, | 160 | .cmp_gt_optimized, |
| 161 | .cmp_neq_optimized, | 161 | .cmp_neq_optimized, |
| 162 | .memcpy, | 162 | .memcpy, |
| 163 | .memmove, | ||
| 163 | .memset, | 164 | .memset, |
| 164 | .memset_safe, | 165 | .memset_safe, |
| 165 | => try w.writeBinOp(s, inst), | 166 | => try w.writeBinOp(s, inst), |
src/print_zir.zig+1| ... | @@ -413,6 +413,7 @@ const Writer = struct { | ... | @@ -413,6 +413,7 @@ const Writer = struct { |
| 413 | .min, | 413 | .min, |
| 414 | .memcpy, | 414 | .memcpy, |
| 415 | .memset, | 415 | .memset, |
| 416 | .memmove, | ||
| 416 | .elem_ptr_node, | 417 | .elem_ptr_node, |
| 417 | .elem_val_node, | 418 | .elem_val_node, |
| 418 | .elem_ptr, | 419 | .elem_ptr, |
test/behavior.zig+1| ... | @@ -55,6 +55,7 @@ test { | ... | @@ -55,6 +55,7 @@ test { |
| 55 | _ = @import("behavior/member_func.zig"); | 55 | _ = @import("behavior/member_func.zig"); |
| 56 | _ = @import("behavior/memcpy.zig"); | 56 | _ = @import("behavior/memcpy.zig"); |
| 57 | _ = @import("behavior/memset.zig"); | 57 | _ = @import("behavior/memset.zig"); |
| 58 | _ = @import("behavior/memmove.zig"); | ||
| 58 | _ = @import("behavior/merge_error_sets.zig"); | 59 | _ = @import("behavior/merge_error_sets.zig"); |
| 59 | _ = @import("behavior/muladd.zig"); | 60 | _ = @import("behavior/muladd.zig"); |
| 60 | _ = @import("behavior/multiple_externs_with_conflicting_types.zig"); | 61 | _ = @import("behavior/multiple_externs_with_conflicting_types.zig"); |
test/behavior/builtin_functions_returning_void_or_noreturn.zig+2| ... | @@ -6,6 +6,7 @@ var x: u8 = 1; | ... | @@ -6,6 +6,7 @@ var x: u8 = 1; |
| 6 | 6 | ||
| 7 | // This excludes builtin functions that return void or noreturn that cannot be tested. | 7 | // This excludes builtin functions that return void or noreturn that cannot be tested. |
| 8 | test { | 8 | test { |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 9 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | 10 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 11 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | @@ -17,6 +18,7 @@ test { | ... | @@ -17,6 +18,7 @@ test { |
| 17 | try testing.expectEqual(void, @TypeOf(@breakpoint())); | 18 | try testing.expectEqual(void, @TypeOf(@breakpoint())); |
| 18 | try testing.expectEqual({}, @export(&x, .{ .name = "x" })); | 19 | try testing.expectEqual({}, @export(&x, .{ .name = "x" })); |
| 19 | try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0])); | 20 | try testing.expectEqual({}, @memcpy(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0])); |
| 21 | try testing.expectEqual({}, @memmove(@as([*]u8, @ptrFromInt(1))[0..0], @as([*]u8, @ptrFromInt(1))[0..0])); | ||
| 20 | try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined)); | 22 | try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined)); |
| 21 | try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); | 23 | try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); |
| 22 | try testing.expectEqual({}, @prefetch(&val, .{})); | 24 | try testing.expectEqual({}, @prefetch(&val, .{})); |
test/behavior/memmove.zig created+183| ... | @@ -0,0 +1,183 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | |||
| 5 | test "memmove and memset intrinsics" { | ||
| 6 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 7 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 9 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 10 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 11 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 12 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 13 | |||
| 14 | try testMemmoveMemset(); | ||
| 15 | try comptime testMemmoveMemset(); | ||
| 16 | } | ||
| 17 | |||
| 18 | fn testMemmoveMemset() !void { | ||
| 19 | var foo: [20]u8 = undefined; | ||
| 20 | |||
| 21 | @memset(foo[0..10], 'A'); | ||
| 22 | @memset(foo[10..20], 'B'); | ||
| 23 | |||
| 24 | try expect(foo[0] == 'A'); | ||
| 25 | try expect(foo[11] == 'B'); | ||
| 26 | try expect(foo[19] == 'B'); | ||
| 27 | |||
| 28 | @memmove(foo[10..20], foo[0..10]); | ||
| 29 | |||
| 30 | try expect(foo[0] == 'A'); | ||
| 31 | try expect(foo[11] == 'A'); | ||
| 32 | try expect(foo[19] == 'A'); | ||
| 33 | } | ||
| 34 | |||
| 35 | test "@memmove with both operands single-ptr-to-array, one is null-terminated" { | ||
| 36 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 37 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 38 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 39 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 40 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 41 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 42 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 43 | |||
| 44 | try testMemmoveBothSinglePtrArrayOneIsNullTerminated(); | ||
| 45 | try comptime testMemmoveBothSinglePtrArrayOneIsNullTerminated(); | ||
| 46 | } | ||
| 47 | |||
| 48 | fn testMemmoveBothSinglePtrArrayOneIsNullTerminated() !void { | ||
| 49 | var buf: [100]u8 = undefined; | ||
| 50 | const suffix = "hello"; | ||
| 51 | @memmove(buf[buf.len - suffix.len ..], suffix); | ||
| 52 | try expect(buf[95] == 'h'); | ||
| 53 | try expect(buf[96] == 'e'); | ||
| 54 | try expect(buf[97] == 'l'); | ||
| 55 | try expect(buf[98] == 'l'); | ||
| 56 | try expect(buf[99] == 'o'); | ||
| 57 | |||
| 58 | const start = buf.len - suffix.len - 3; | ||
| 59 | const end = start + suffix.len; | ||
| 60 | @memmove(buf[start..end], buf[buf.len - suffix.len ..]); | ||
| 61 | try expect(buf[92] == 'h'); | ||
| 62 | try expect(buf[93] == 'e'); | ||
| 63 | try expect(buf[94] == 'l'); | ||
| 64 | try expect(buf[95] == 'l'); | ||
| 65 | try expect(buf[96] == 'o'); | ||
| 66 | try expect(buf[97] == 'l'); | ||
| 67 | try expect(buf[98] == 'l'); | ||
| 68 | try expect(buf[99] == 'o'); | ||
| 69 | |||
| 70 | @memmove(buf[start + 2 .. end + 2], buf[start..end]); | ||
| 71 | try expect(buf[92] == 'h'); | ||
| 72 | try expect(buf[93] == 'e'); | ||
| 73 | try expect(buf[94] == 'h'); | ||
| 74 | try expect(buf[95] == 'e'); | ||
| 75 | try expect(buf[96] == 'l'); | ||
| 76 | try expect(buf[97] == 'l'); | ||
| 77 | try expect(buf[98] == 'o'); | ||
| 78 | try expect(buf[99] == 'o'); | ||
| 79 | } | ||
| 80 | |||
| 81 | test "@memmove dest many pointer" { | ||
| 82 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 83 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 84 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 85 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 86 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 87 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 88 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 89 | |||
| 90 | try testMemmoveDestManyPtr(); | ||
| 91 | try comptime testMemmoveDestManyPtr(); | ||
| 92 | } | ||
| 93 | |||
| 94 | fn testMemmoveDestManyPtr() !void { | ||
| 95 | var str = "hello".*; | ||
| 96 | var buf: [8]u8 = undefined; | ||
| 97 | var len: usize = 5; | ||
| 98 | _ = &len; | ||
| 99 | @memmove(@as([*]u8, @ptrCast(&buf)), @as([*]const u8, @ptrCast(&str))[0..len]); | ||
| 100 | try expect(buf[0] == 'h'); | ||
| 101 | try expect(buf[1] == 'e'); | ||
| 102 | try expect(buf[2] == 'l'); | ||
| 103 | try expect(buf[3] == 'l'); | ||
| 104 | try expect(buf[4] == 'o'); | ||
| 105 | @memmove(buf[3..].ptr, buf[0..len]); | ||
| 106 | try expect(buf[0] == 'h'); | ||
| 107 | try expect(buf[1] == 'e'); | ||
| 108 | try expect(buf[2] == 'l'); | ||
| 109 | try expect(buf[3] == 'h'); | ||
| 110 | try expect(buf[4] == 'e'); | ||
| 111 | try expect(buf[5] == 'l'); | ||
| 112 | try expect(buf[6] == 'l'); | ||
| 113 | try expect(buf[7] == 'o'); | ||
| 114 | @memmove(buf[2..7].ptr, buf[3 .. len + 3]); | ||
| 115 | try expect(buf[0] == 'h'); | ||
| 116 | try expect(buf[1] == 'e'); | ||
| 117 | try expect(buf[2] == 'h'); | ||
| 118 | try expect(buf[3] == 'e'); | ||
| 119 | try expect(buf[4] == 'l'); | ||
| 120 | try expect(buf[5] == 'l'); | ||
| 121 | try expect(buf[6] == 'o'); | ||
| 122 | try expect(buf[7] == 'o'); | ||
| 123 | } | ||
| 124 | |||
| 125 | test "@memmove slice" { | ||
| 126 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 127 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 128 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 129 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; | ||
| 130 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | ||
| 131 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 132 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | ||
| 133 | |||
| 134 | try testMemmoveSlice(); | ||
| 135 | try comptime testMemmoveSlice(); | ||
| 136 | } | ||
| 137 | |||
| 138 | fn testMemmoveSlice() !void { | ||
| 139 | var buf: [8]u8 = undefined; | ||
| 140 | const dst1: []u8 = buf[0..5]; | ||
| 141 | const dst2: []u8 = buf[3..8]; | ||
| 142 | const dst3: []u8 = buf[2..7]; | ||
| 143 | const src: []const u8 = "hello"; | ||
| 144 | @memmove(dst1, src); | ||
| 145 | try expect(buf[0] == 'h'); | ||
| 146 | try expect(buf[1] == 'e'); | ||
| 147 | try expect(buf[2] == 'l'); | ||
| 148 | try expect(buf[3] == 'l'); | ||
| 149 | try expect(buf[4] == 'o'); | ||
| 150 | @memmove(dst2, dst1); | ||
| 151 | try expect(buf[0] == 'h'); | ||
| 152 | try expect(buf[1] == 'e'); | ||
| 153 | try expect(buf[2] == 'l'); | ||
| 154 | try expect(buf[3] == 'h'); | ||
| 155 | try expect(buf[4] == 'e'); | ||
| 156 | try expect(buf[5] == 'l'); | ||
| 157 | try expect(buf[6] == 'l'); | ||
| 158 | try expect(buf[7] == 'o'); | ||
| 159 | @memmove(dst3, dst2); | ||
| 160 | try expect(buf[0] == 'h'); | ||
| 161 | try expect(buf[1] == 'e'); | ||
| 162 | try expect(buf[2] == 'h'); | ||
| 163 | try expect(buf[3] == 'e'); | ||
| 164 | try expect(buf[4] == 'l'); | ||
| 165 | try expect(buf[5] == 'l'); | ||
| 166 | try expect(buf[6] == 'o'); | ||
| 167 | try expect(buf[7] == 'o'); | ||
| 168 | } | ||
| 169 | |||
| 170 | comptime { | ||
| 171 | const S = struct { | ||
| 172 | buffer: [8]u8 = undefined, | ||
| 173 | fn set(self: *@This(), items: []const u8) void { | ||
| 174 | @memmove(self.buffer[0..items.len], items); | ||
| 175 | @memmove(self.buffer[3..], self.buffer[0..items.len]); | ||
| 176 | @memmove(self.buffer[2 .. 2 + items.len], self.buffer[3..]); | ||
| 177 | } | ||
| 178 | }; | ||
| 179 | |||
| 180 | var s = S{}; | ||
| 181 | s.set("hello"); | ||
| 182 | if (!std.mem.eql(u8, s.buffer[0..8], "hehelloo")) @compileError("bad"); | ||
| 183 | } | ||
test/cases/compile_errors/@memmove_type_mismatch.zig created+218| ... | @@ -0,0 +1,218 @@ | ||
| 1 | export fn foo() void { | ||
| 2 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 3 | const dest: []u8 = &buf; | ||
| 4 | const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 5 | |||
| 6 | @memmove(dest, src); | ||
| 7 | } | ||
| 8 | |||
| 9 | export fn bar() void { | ||
| 10 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 11 | const dest: []u8 = &buf; | ||
| 12 | const src: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 13 | |||
| 14 | @memmove(dest, src); | ||
| 15 | } | ||
| 16 | |||
| 17 | export fn baz() void { | ||
| 18 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 19 | const dest: []u8 = &buf; | ||
| 20 | const src: [*]align(1) u16 = @ptrCast(&buf); | ||
| 21 | |||
| 22 | @memmove(dest, src); | ||
| 23 | } | ||
| 24 | |||
| 25 | export fn qux() void { | ||
| 26 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 27 | const dest: *[8]u8 = &buf; | ||
| 28 | const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 29 | |||
| 30 | @memmove(dest, src); | ||
| 31 | } | ||
| 32 | |||
| 33 | export fn quux() void { | ||
| 34 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 35 | const dest: *[8]u8 = &buf; | ||
| 36 | const src: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 37 | |||
| 38 | @memmove(dest, src); | ||
| 39 | } | ||
| 40 | |||
| 41 | export fn quuux() void { | ||
| 42 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 43 | const dest: *[8]u8 = &buf; | ||
| 44 | const src: [*]align(1) u16 = @ptrCast(&buf); | ||
| 45 | |||
| 46 | @memmove(dest, src); | ||
| 47 | } | ||
| 48 | |||
| 49 | export fn foo2() void { | ||
| 50 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 51 | const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 52 | const src: []u8 = &buf; | ||
| 53 | |||
| 54 | @memmove(dest, src); | ||
| 55 | } | ||
| 56 | |||
| 57 | export fn bar2() void { | ||
| 58 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 59 | const dest: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 60 | const src: []u8 = &buf; | ||
| 61 | |||
| 62 | @memmove(dest, src); | ||
| 63 | } | ||
| 64 | |||
| 65 | export fn baz2() void { | ||
| 66 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 67 | const dest: [*]align(1) u16 = @ptrCast(&buf); | ||
| 68 | const src: []u8 = &buf; | ||
| 69 | |||
| 70 | @memmove(dest, src); | ||
| 71 | } | ||
| 72 | |||
| 73 | export fn qux2() void { | ||
| 74 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 75 | const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 76 | const src: *[8]u8 = &buf; | ||
| 77 | |||
| 78 | @memmove(dest, src); | ||
| 79 | } | ||
| 80 | |||
| 81 | export fn quux2() void { | ||
| 82 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 83 | const dest: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 84 | const src: *[8]u8 = &buf; | ||
| 85 | |||
| 86 | @memmove(dest, src); | ||
| 87 | } | ||
| 88 | |||
| 89 | export fn quuux2() void { | ||
| 90 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 91 | const dest: [*]align(1) u16 = @ptrCast(&buf); | ||
| 92 | const src: *[8]u8 = &buf; | ||
| 93 | |||
| 94 | @memmove(dest, src); | ||
| 95 | } | ||
| 96 | |||
| 97 | comptime { | ||
| 98 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 99 | const dest: []u8 = &buf; | ||
| 100 | const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 101 | @memmove(dest, src); | ||
| 102 | } | ||
| 103 | |||
| 104 | comptime { | ||
| 105 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 106 | const dest: []u8 = &buf; | ||
| 107 | const src: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 108 | @memmove(dest, src); | ||
| 109 | } | ||
| 110 | |||
| 111 | comptime { | ||
| 112 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 113 | const dest: []u8 = &buf; | ||
| 114 | const src: [*]align(1) u16 = @ptrCast(&buf); | ||
| 115 | @memmove(dest, src); | ||
| 116 | } | ||
| 117 | |||
| 118 | comptime { | ||
| 119 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 120 | const dest: *[8]u8 = &buf; | ||
| 121 | const src: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 122 | @memmove(dest, src); | ||
| 123 | } | ||
| 124 | |||
| 125 | comptime { | ||
| 126 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 127 | const dest: *[8]u8 = &buf; | ||
| 128 | const src: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 129 | @memmove(dest, src); | ||
| 130 | } | ||
| 131 | |||
| 132 | comptime { | ||
| 133 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 134 | const dest: *[8]u8 = &buf; | ||
| 135 | const src: [*]align(1) u16 = @ptrCast(&buf); | ||
| 136 | @memmove(dest, src); | ||
| 137 | } | ||
| 138 | |||
| 139 | comptime { | ||
| 140 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 141 | const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 142 | const src: []u8 = &buf; | ||
| 143 | @memmove(dest, src); | ||
| 144 | } | ||
| 145 | |||
| 146 | comptime { | ||
| 147 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 148 | const dest: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 149 | const src: []u8 = &buf; | ||
| 150 | @memmove(dest, src); | ||
| 151 | } | ||
| 152 | |||
| 153 | comptime { | ||
| 154 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 155 | const dest: [*]align(1) u16 = @ptrCast(&buf); | ||
| 156 | const src: []u8 = &buf; | ||
| 157 | @memmove(dest, src); | ||
| 158 | } | ||
| 159 | |||
| 160 | comptime { | ||
| 161 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 162 | const dest: []align(1) u16 = @as([*]align(1) u16, @ptrCast(&buf))[0..4]; | ||
| 163 | const src: *[8]u8 = &buf; | ||
| 164 | @memmove(dest, src); | ||
| 165 | } | ||
| 166 | |||
| 167 | comptime { | ||
| 168 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 169 | const dest: *align(1) [8]u16 = @ptrCast(&buf); | ||
| 170 | const src: *[8]u8 = &buf; | ||
| 171 | @memmove(dest, src); | ||
| 172 | } | ||
| 173 | |||
| 174 | comptime { | ||
| 175 | var buf: [8]u8 = .{ 0, 1, 2, 3, 4, 5, 6, 7 }; | ||
| 176 | const dest: [*]align(1) u16 = @ptrCast(&buf); | ||
| 177 | const src: *[8]u8 = &buf; | ||
| 178 | @memmove(dest, src); | ||
| 179 | } | ||
| 180 | |||
| 181 | // error | ||
| 182 | // | ||
| 183 | // :6:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 184 | // :6:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 185 | // :14:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 186 | // :14:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 187 | // :22:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 188 | // :22:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 189 | // :30:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 190 | // :30:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 191 | // :38:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 192 | // :38:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 193 | // :46:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 194 | // :46:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 195 | // :54:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 196 | // :62:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 197 | // :70:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 198 | // :78:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 199 | // :86:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 200 | // :94:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 201 | // :101:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 202 | // :101:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 203 | // :108:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 204 | // :108:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 205 | // :115:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 206 | // :115:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 207 | // :122:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 208 | // :122:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 209 | // :129:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 210 | // :129:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 211 | // :136:5: error: pointer element type 'u16' cannot coerce into element type 'u8' | ||
| 212 | // :136:5: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values | ||
| 213 | // :143:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 214 | // :150:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 215 | // :157:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 216 | // :164:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 217 | // :171:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
| 218 | // :178:5: error: pointer element type 'u8' cannot coerce into element type 'u16' | ||
test/cases/compile_errors/bad_panic_call_signature.zig+1| ... | @@ -29,6 +29,7 @@ pub const panic = struct { | ... | @@ -29,6 +29,7 @@ pub const panic = struct { |
| 29 | pub const forLenMismatch = simple_panic.forLenMismatch; | 29 | pub const forLenMismatch = simple_panic.forLenMismatch; |
| 30 | pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; | 30 | pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; |
| 31 | pub const memcpyAlias = simple_panic.memcpyAlias; | 31 | pub const memcpyAlias = simple_panic.memcpyAlias; |
| 32 | pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch; | ||
| 32 | pub const noreturnReturned = simple_panic.noreturnReturned; | 33 | pub const noreturnReturned = simple_panic.noreturnReturned; |
| 33 | }; | 34 | }; |
| 34 | 35 |
test/cases/compile_errors/bad_panic_generic_signature.zig+1| ... | @@ -25,6 +25,7 @@ pub const panic = struct { | ... | @@ -25,6 +25,7 @@ pub const panic = struct { |
| 25 | pub const forLenMismatch = simple_panic.forLenMismatch; | 25 | pub const forLenMismatch = simple_panic.forLenMismatch; |
| 26 | pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; | 26 | pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; |
| 27 | pub const memcpyAlias = simple_panic.memcpyAlias; | 27 | pub const memcpyAlias = simple_panic.memcpyAlias; |
| 28 | pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch; | ||
| 28 | pub const noreturnReturned = simple_panic.noreturnReturned; | 29 | pub const noreturnReturned = simple_panic.noreturnReturned; |
| 29 | }; | 30 | }; |
| 30 | 31 |
test/cases/compile_errors/comptime_var_referenced_at_runtime.zig+11| ... | @@ -63,6 +63,14 @@ export fn far() void { | ... | @@ -63,6 +63,14 @@ export fn far() void { |
| 63 | @memset(&rt, elem); | 63 | @memset(&rt, elem); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | export fn bax() void { | ||
| 67 | comptime var x: [2]u32 = undefined; | ||
| 68 | x = .{ 1, 2 }; | ||
| 69 | |||
| 70 | var rt: [2]u32 = undefined; | ||
| 71 | @memmove(&rt, &x); | ||
| 72 | } | ||
| 73 | |||
| 66 | // error | 74 | // error |
| 67 | // | 75 | // |
| 68 | // :5:19: error: runtime value contains reference to comptime var | 76 | // :5:19: error: runtime value contains reference to comptime var |
| ... | @@ -92,3 +100,6 @@ export fn far() void { | ... | @@ -92,3 +100,6 @@ export fn far() void { |
| 92 | // :63:18: error: runtime value contains reference to comptime var | 100 | // :63:18: error: runtime value contains reference to comptime var |
| 93 | // :63:18: note: comptime var pointers are not available at runtime | 101 | // :63:18: note: comptime var pointers are not available at runtime |
| 94 | // :59:27: note: 'runtime_value' points to comptime var declared here | 102 | // :59:27: note: 'runtime_value' points to comptime var declared here |
| 103 | // :71:19: error: runtime value contains reference to comptime var | ||
| 104 | // :71:19: note: comptime var pointers are not available at runtime | ||
| 105 | // :67:30: note: 'runtime_value' points to comptime var declared here |
test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig+43-1| ... | @@ -28,10 +28,39 @@ pub export fn memcpy_const_dest_ptr() void { | ... | @@ -28,10 +28,39 @@ pub export fn memcpy_const_dest_ptr() void { |
| 28 | var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 }; | 28 | var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 }; |
| 29 | @memcpy(&buf1, &buf2); | 29 | @memcpy(&buf1, &buf2); |
| 30 | } | 30 | } |
| 31 | pub export fn memset_array() void { | 31 | pub export fn memcpy_array() void { |
| 32 | const buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | 32 | const buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; |
| 33 | @memcpy(buf, 1); | 33 | @memcpy(buf, 1); |
| 34 | } | 34 | } |
| 35 | pub export fn entry_memmove() void { | ||
| 36 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 37 | const slice: []u8 = &buf; | ||
| 38 | const a: u32 = 1234; | ||
| 39 | @memmove(slice.ptr, @as([*]const u8, @ptrCast(&a))); | ||
| 40 | } | ||
| 41 | pub export fn entry1_memmove() void { | ||
| 42 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 43 | const ptr: *u8 = &buf[0]; | ||
| 44 | @memmove(ptr, 0); | ||
| 45 | } | ||
| 46 | pub export fn non_matching_lengths_memmove() void { | ||
| 47 | var buf1: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 48 | var buf2: [6]u8 = .{ 1, 2, 3, 4, 5, 6 }; | ||
| 49 | @memmove(&buf2, &buf1); | ||
| 50 | } | ||
| 51 | pub export fn memcpy_const_dest_ptr_memmove() void { | ||
| 52 | const buf1: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 53 | var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 54 | @memmove(&buf1, &buf2); | ||
| 55 | } | ||
| 56 | pub export fn memmove_array() void { | ||
| 57 | const buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 58 | @memmove(buf, 1); | ||
| 59 | } | ||
| 60 | pub export fn memset_array() void { | ||
| 61 | const buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 62 | @memset(buf, 1); | ||
| 63 | } | ||
| 35 | 64 | ||
| 36 | // error | 65 | // error |
| 37 | // backend=stage2 | 66 | // backend=stage2 |
| ... | @@ -51,3 +80,16 @@ pub export fn memset_array() void { | ... | @@ -51,3 +80,16 @@ pub export fn memset_array() void { |
| 51 | // :29:13: error: cannot memcpy to constant pointer | 80 | // :29:13: error: cannot memcpy to constant pointer |
| 52 | // :33:13: error: type '[5]u8' is not an indexable pointer | 81 | // :33:13: error: type '[5]u8' is not an indexable pointer |
| 53 | // :33:13: note: operand must be a slice, a many pointer or a pointer to an array | 82 | // :33:13: note: operand must be a slice, a many pointer or a pointer to an array |
| 83 | // :39:5: error: unknown @memmove length | ||
| 84 | // :39:19: note: destination type '[*]u8' provides no length | ||
| 85 | // :39:25: note: source type '[*]const u8' provides no length | ||
| 86 | // :44:14: error: type '*u8' is not an indexable pointer | ||
| 87 | // :44:14: note: operand must be a slice, a many pointer or a pointer to an array | ||
| 88 | // :49:5: error: non-matching @memmove lengths | ||
| 89 | // :49:14: note: length 6 here | ||
| 90 | // :49:21: note: length 5 here | ||
| 91 | // :54:14: error: cannot memmove to constant pointer | ||
| 92 | // :58:14: error: type '[5]u8' is not an indexable pointer | ||
| 93 | // :58:14: note: operand must be a slice, a many pointer or a pointer to an array | ||
| 94 | // :62:13: error: type '[5]u8' is not an indexable pointer | ||
| 95 | // :62:13: note: operand must be a slice, a many pointer or a pointer to an array |
test/cases/safety/memcpy_alias.zig+1| ... | @@ -12,6 +12,7 @@ pub fn main() !void { | ... | @@ -12,6 +12,7 @@ pub fn main() !void { |
| 12 | var len: usize = 5; | 12 | var len: usize = 5; |
| 13 | _ = &len; | 13 | _ = &len; |
| 14 | @memcpy(buffer[0..len], buffer[4 .. 4 + len]); | 14 | @memcpy(buffer[0..len], buffer[4 .. 4 + len]); |
| 15 | return error.TestFailed; | ||
| 15 | } | 16 | } |
| 16 | // run | 17 | // run |
| 17 | // backend=stage2,llvm | 18 | // backend=stage2,llvm |
test/cases/safety/memcpy_len_mismatch.zig+1| ... | @@ -12,6 +12,7 @@ pub fn main() !void { | ... | @@ -12,6 +12,7 @@ pub fn main() !void { |
| 12 | var len: usize = 5; | 12 | var len: usize = 5; |
| 13 | _ = &len; | 13 | _ = &len; |
| 14 | @memcpy(buffer[0..len], buffer[len .. len + 4]); | 14 | @memcpy(buffer[0..len], buffer[len .. len + 4]); |
| 15 | return error.TestFailed; | ||
| 15 | } | 16 | } |
| 16 | // run | 17 | // run |
| 17 | // backend=stage2,llvm | 18 | // backend=stage2,llvm |
test/cases/safety/memmove_len_mismatch.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | ||
| 4 | _ = stack_trace; | ||
| 5 | if (std.mem.eql(u8, message, "@memmove arguments have non-equal lengths")) { | ||
| 6 | std.process.exit(0); | ||
| 7 | } | ||
| 8 | std.process.exit(1); | ||
| 9 | } | ||
| 10 | pub fn main() !void { | ||
| 11 | var buffer = [2]u8{ 1, 2 } ** 5; | ||
| 12 | var len: usize = 5; | ||
| 13 | _ = &len; | ||
| 14 | @memmove(buffer[0..len], buffer[len .. len + 4]); | ||
| 15 | return error.TestFailed; | ||
| 16 | } | ||
| 17 | // run | ||
| 18 | // backend=llvm | ||
| 19 | // target=native | ||
test/incremental/change_panic_handler_explicit+3| ... | @@ -39,6 +39,7 @@ pub const panic = struct { | ... | @@ -39,6 +39,7 @@ pub const panic = struct { |
| 39 | pub const forLenMismatch = no_panic.forLenMismatch; | 39 | pub const forLenMismatch = no_panic.forLenMismatch; |
| 40 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; | 40 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; |
| 41 | pub const memcpyAlias = no_panic.memcpyAlias; | 41 | pub const memcpyAlias = no_panic.memcpyAlias; |
| 42 | pub const memmoveLenMismatch = no_panic.memmoveLenMismatch; | ||
| 42 | pub const noreturnReturned = no_panic.noreturnReturned; | 43 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 43 | }; | 44 | }; |
| 44 | fn myPanic(msg: []const u8, _: ?usize) noreturn { | 45 | fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| ... | @@ -86,6 +87,7 @@ pub const panic = struct { | ... | @@ -86,6 +87,7 @@ pub const panic = struct { |
| 86 | pub const forLenMismatch = no_panic.forLenMismatch; | 87 | pub const forLenMismatch = no_panic.forLenMismatch; |
| 87 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; | 88 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; |
| 88 | pub const memcpyAlias = no_panic.memcpyAlias; | 89 | pub const memcpyAlias = no_panic.memcpyAlias; |
| 90 | pub const memmoveLenMismatch = no_panic.memmoveLenMismatch; | ||
| 89 | pub const noreturnReturned = no_panic.noreturnReturned; | 91 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 90 | }; | 92 | }; |
| 91 | fn myPanic(msg: []const u8, _: ?usize) noreturn { | 93 | fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| ... | @@ -133,6 +135,7 @@ pub const panic = struct { | ... | @@ -133,6 +135,7 @@ pub const panic = struct { |
| 133 | pub const forLenMismatch = no_panic.forLenMismatch; | 135 | pub const forLenMismatch = no_panic.forLenMismatch; |
| 134 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; | 136 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; |
| 135 | pub const memcpyAlias = no_panic.memcpyAlias; | 137 | pub const memcpyAlias = no_panic.memcpyAlias; |
| 138 | pub const memmoveLenMismatch = no_panic.memmoveLenMismatch; | ||
| 136 | pub const noreturnReturned = no_panic.noreturnReturned; | 139 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 137 | }; | 140 | }; |
| 138 | fn myPanicNew(msg: []const u8, _: ?usize) noreturn { | 141 | fn myPanicNew(msg: []const u8, _: ?usize) noreturn { |
test/standalone/zerolength_check/src/main.zig+1| ... | @@ -5,6 +5,7 @@ test { | ... | @@ -5,6 +5,7 @@ test { |
| 5 | const source = foo(); | 5 | const source = foo(); |
| 6 | 6 | ||
| 7 | @memcpy(dest, source); | 7 | @memcpy(dest, source); |
| 8 | @memmove(dest, source); | ||
| 8 | @memset(dest, 4); | 9 | @memset(dest, 4); |
| 9 | @memset(dest, undefined); | 10 | @memset(dest, undefined); |
| 10 | 11 |