| 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 | 5149 | {#syntax#}std.crypto.secureZero{#endsyntax#}</p> |
| 5150 | 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 | 5169 | {#header_open|@min#} |
| 5153 | 5170 | <pre>{#syntax#}@min(...) T{#endsyntax#}</pre> |
| 5154 | 5171 | <p> |
lib/std/debug.zig+4| ... | ... | @@ -134,6 +134,10 @@ pub fn FullPanic(comptime panicFn: fn ([]const u8, ?usize) noreturn) type { |
| 134 | 134 | @branchHint(.cold); |
| 135 | 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 | 141 | pub fn noreturnReturned() noreturn { |
| 138 | 142 | @branchHint(.cold); |
| 139 | 143 | call("'noreturn' function returned", @returnAddress()); |
lib/std/debug/no_panic.zig+5| ... | ... | @@ -135,6 +135,11 @@ pub fn memcpyAlias() noreturn { |
| 135 | 135 | @trap(); |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | pub fn memmoveLenMismatch() noreturn { | |
| 139 | @branchHint(.cold); | |
| 140 | @trap(); | |
| 141 | } | |
| 142 | ||
| 138 | 143 | pub fn noreturnReturned() noreturn { |
| 139 | 144 | @branchHint(.cold); |
| 140 | 145 | @trap(); |
lib/std/debug/simple_panic.zig+4| ... | ... | @@ -128,6 +128,10 @@ pub fn memcpyAlias() noreturn { |
| 128 | 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 | 135 | pub fn noreturnReturned() noreturn { |
| 132 | 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 | 232 | /// Copy all of source into dest at position 0. |
| 233 | 233 | /// dest.len must be >= source.len. |
| 234 | 234 | /// If the slices overlap, dest.ptr must be <= src.ptr. |
| 235 | /// This function is deprecated; use @memmove instead. | |
| 235 | 236 | pub fn copyForwards(comptime T: type, dest: []T, source: []const T) void { |
| 236 | 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 | 240 | /// Copy all of source into dest at position 0. |
| 240 | 241 | /// dest.len must be >= source.len. |
| 241 | 242 | /// If the slices overlap, dest.ptr must be >= src.ptr. |
| 243 | /// This function is deprecated; use @memmove instead. | |
| 242 | 244 | pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void { |
| 243 | 245 | // TODO instead of manually doing this check for the whole array |
| 244 | 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 | 2919 | .set_runtime_safety, |
| 2920 | 2920 | .memcpy, |
| 2921 | 2921 | .memset, |
| 2922 | .memmove, | |
| 2922 | 2923 | .validate_deref, |
| 2923 | 2924 | .validate_destructure, |
| 2924 | 2925 | .save_err_ret_index, |
| ... | ... | @@ -9717,6 +9718,13 @@ fn builtinCall( |
| 9717 | 9718 | }); |
| 9718 | 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 | 9728 | .shuffle => { |
| 9721 | 9729 | const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{ |
| 9722 | 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 | 1055 | _ = try astrl.expr(args[2], block, ResultInfo.none); |
| 1056 | 1056 | return false; |
| 1057 | 1057 | }, |
| 1058 | .memcpy => { | |
| 1058 | .memcpy, .memmove => { | |
| 1059 | 1059 | _ = try astrl.expr(args[0], block, ResultInfo.none); |
| 1060 | 1060 | _ = try astrl.expr(args[1], block, ResultInfo.none); |
| 1061 | 1061 | return false; |
lib/std/zig/BuiltinFn.zig+8| ... | ... | @@ -68,6 +68,7 @@ pub const Tag = enum { |
| 68 | 68 | max, |
| 69 | 69 | memcpy, |
| 70 | 70 | memset, |
| 71 | memmove, | |
| 71 | 72 | min, |
| 72 | 73 | wasm_memory_size, |
| 73 | 74 | wasm_memory_grow, |
| ... | ... | @@ -641,6 +642,13 @@ pub const list = list: { |
| 641 | 642 | .param_count = 2, |
| 642 | 643 | }, |
| 643 | 644 | }, |
| 645 | .{ | |
| 646 | "@memmove", | |
| 647 | .{ | |
| 648 | .tag = .memmove, | |
| 649 | .param_count = 2, | |
| 650 | }, | |
| 651 | }, | |
| 644 | 652 | .{ |
| 645 | 653 | "@min", |
| 646 | 654 | .{ |
lib/std/zig/Zir.zig+7| ... | ... | @@ -986,6 +986,9 @@ pub const Inst = struct { |
| 986 | 986 | /// Implements the `@memset` builtin. |
| 987 | 987 | /// Uses the `pl_node` union field with payload `Bin`. |
| 988 | 988 | memset, |
| 989 | /// Implements the `@memmove` builtin. | |
| 990 | /// Uses the `pl_node` union field with payload `Bin`. | |
| 991 | memmove, | |
| 989 | 992 | /// Implements the `@min` builtin for 2 args. |
| 990 | 993 | /// Uses the `pl_node` union field with payload `Bin` |
| 991 | 994 | min, |
| ... | ... | @@ -1272,6 +1275,7 @@ pub const Inst = struct { |
| 1272 | 1275 | .max, |
| 1273 | 1276 | .memcpy, |
| 1274 | 1277 | .memset, |
| 1278 | .memmove, | |
| 1275 | 1279 | .min, |
| 1276 | 1280 | .c_import, |
| 1277 | 1281 | .@"resume", |
| ... | ... | @@ -1355,6 +1359,7 @@ pub const Inst = struct { |
| 1355 | 1359 | .set_runtime_safety, |
| 1356 | 1360 | .memcpy, |
| 1357 | 1361 | .memset, |
| 1362 | .memmove, | |
| 1358 | 1363 | .check_comptime_control_flow, |
| 1359 | 1364 | .@"defer", |
| 1360 | 1365 | .defer_err_code, |
| ... | ... | @@ -1832,6 +1837,7 @@ pub const Inst = struct { |
| 1832 | 1837 | .max = .pl_node, |
| 1833 | 1838 | .memcpy = .pl_node, |
| 1834 | 1839 | .memset = .pl_node, |
| 1840 | .memmove = .pl_node, | |
| 1835 | 1841 | .min = .pl_node, |
| 1836 | 1842 | .c_import = .pl_node, |
| 1837 | 1843 | |
| ... | ... | @@ -4291,6 +4297,7 @@ fn findTrackableInner( |
| 4291 | 4297 | .mul_add, |
| 4292 | 4298 | .memcpy, |
| 4293 | 4299 | .memset, |
| 4300 | .memmove, | |
| 4294 | 4301 | .min, |
| 4295 | 4302 | .max, |
| 4296 | 4303 | .alloc, |
lib/std/zig/llvm/Builder.zig+30| ... | ... | @@ -6125,6 +6125,36 @@ pub const WipFunction = struct { |
| 6125 | 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 | 6158 | pub fn callMemSet( |
| 6129 | 6159 | self: *WipFunction, |
| 6130 | 6160 | dst: Value, |
lib/zig.h+1| ... | ... | @@ -481,6 +481,7 @@ |
| 481 | 481 | |
| 482 | 482 | zig_extern void *memcpy (void *zig_restrict, void const *zig_restrict, size_t); |
| 483 | 483 | zig_extern void *memset (void *, int, size_t); |
| 484 | zig_extern void *memmove (void *, void const *, size_t); | |
| 484 | 485 | |
| 485 | 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 | 730 | /// source being a pointer-to-array), then it is guaranteed to be |
| 731 | 731 | /// greater than zero. |
| 732 | 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 | 746 | /// Uses the `ty_pl` field with payload `Cmpxchg`. |
| 735 | 747 | cmpxchg_weak, |
| ... | ... | @@ -1533,6 +1545,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool) |
| 1533 | 1545 | .memset, |
| 1534 | 1546 | .memset_safe, |
| 1535 | 1547 | .memcpy, |
| 1548 | .memmove, | |
| 1536 | 1549 | .set_union_tag, |
| 1537 | 1550 | .prefetch, |
| 1538 | 1551 | .set_err_return_trace, |
| ... | ... | @@ -1696,6 +1709,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool { |
| 1696 | 1709 | .memset, |
| 1697 | 1710 | .memset_safe, |
| 1698 | 1711 | .memcpy, |
| 1712 | .memmove, | |
| 1699 | 1713 | .cmpxchg_weak, |
| 1700 | 1714 | .cmpxchg_strong, |
| 1701 | 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 | 83 | .memset, |
| 84 | 84 | .memset_safe, |
| 85 | 85 | .memcpy, |
| 86 | .memmove, | |
| 86 | 87 | .atomic_store_unordered, |
| 87 | 88 | .atomic_store_monotonic, |
| 88 | 89 | .atomic_store_release, |
src/Liveness.zig+2| ... | ... | @@ -300,6 +300,7 @@ pub fn categorizeOperand( |
| 300 | 300 | .memset, |
| 301 | 301 | .memset_safe, |
| 302 | 302 | .memcpy, |
| 303 | .memmove, | |
| 303 | 304 | => { |
| 304 | 305 | const o = air_datas[@intFromEnum(inst)].bin_op; |
| 305 | 306 | if (o.lhs == operand_ref) return matchOperandSmallIndex(l, inst, 0, .write); |
| ... | ... | @@ -936,6 +937,7 @@ fn analyzeInst( |
| 936 | 937 | .memset, |
| 937 | 938 | .memset_safe, |
| 938 | 939 | .memcpy, |
| 940 | .memmove, | |
| 939 | 941 | => { |
| 940 | 942 | const o = inst_datas[@intFromEnum(inst)].bin_op; |
| 941 | 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 | 267 | .memset, |
| 268 | 268 | .memset_safe, |
| 269 | 269 | .memcpy, |
| 270 | .memmove, | |
| 270 | 271 | => { |
| 271 | 272 | const bin_op = data[@intFromEnum(inst)].bin_op; |
| 272 | 273 | try self.verifyInstOperands(inst, .{ bin_op.lhs, bin_op.rhs, .none }); |
src/Sema.zig+45-12| ... | ... | @@ -1583,6 +1583,11 @@ fn analyzeBodyInner( |
| 1583 | 1583 | i += 1; |
| 1584 | 1584 | continue; |
| 1585 | 1585 | }, |
| 1586 | .memmove => { | |
| 1587 | try sema.zirMemmove(block, inst); | |
| 1588 | i += 1; | |
| 1589 | continue; | |
| 1590 | }, | |
| 1586 | 1591 | .check_comptime_control_flow => { |
| 1587 | 1592 | if (!block.isComptime()) { |
| 1588 | 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 | 25615 | } |
| 25611 | 25616 | |
| 25612 | 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 | 25631 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 25614 | 25632 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 25615 | 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 | 25643 | const zcu = pt.zcu; |
| 25626 | 25644 | |
| 25627 | 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 | 25649 | if (dest_len == .none and src_len == .none) { |
| 25632 | 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 | 25652 | errdefer msg.destroy(sema.gpa); |
| 25635 | 25653 | try sema.errNote(dest_src, msg, "destination type '{}' provides no length", .{ |
| 25636 | 25654 | dest_ty.fmt(pt), |
| ... | ... | @@ -25676,7 +25694,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25676 | 25694 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { |
| 25677 | 25695 | if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { |
| 25678 | 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 | 25698 | errdefer msg.destroy(sema.gpa); |
| 25681 | 25699 | try sema.errNote(dest_src, msg, "length {} here", .{ |
| 25682 | 25700 | dest_len_val.fmtValueSema(pt, sema), |
| ... | ... | @@ -25696,7 +25714,11 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25696 | 25714 | |
| 25697 | 25715 | if (block.wantSafety()) { |
| 25698 | 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 | 25723 | } else if (dest_len != .none) { |
| 25702 | 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 | 25746 | return; |
| 25725 | 25747 | } |
| 25726 | 25748 | |
| 25749 | const check_aliasing = switch (op) { | |
| 25750 | .memcpy => true, | |
| 25751 | .memmove => false, | |
| 25752 | }; | |
| 25753 | ||
| 25727 | 25754 | const runtime_src = rs: { |
| 25728 | 25755 | const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; |
| 25729 | 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 | 25760 | |
| 25734 | 25761 | const len_u64 = try len_val.?.toUnsignedIntSema(pt); |
| 25735 | 25762 | |
| 25736 | if (Value.doPointersOverlap( | |
| 25737 | raw_src_ptr, | |
| 25738 | raw_dest_ptr, | |
| 25739 | len_u64, | |
| 25740 | zcu, | |
| 25741 | )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); | |
| 25763 | if (check_aliasing) { | |
| 25764 | if (Value.doPointersOverlap( | |
| 25765 | raw_src_ptr, | |
| 25766 | raw_dest_ptr, | |
| 25767 | len_u64, | |
| 25768 | zcu, | |
| 25769 | )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); | |
| 25770 | } | |
| 25742 | 25771 | |
| 25743 | 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 | 25839 | try sema.validateRuntimeValue(block, src_src, src_ptr); |
| 25811 | 25840 | |
| 25812 | 25841 | // Aliasing safety check. |
| 25813 | if (block.wantSafety()) { | |
| 25842 | if (check_aliasing and block.wantSafety()) { | |
| 25814 | 25843 | const len = if (len_val) |v| |
| 25815 | 25844 | Air.internedToRef(v.toIntern()) |
| 25816 | 25845 | else if (dest_len != .none) |
| ... | ... | @@ -25853,7 +25882,10 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 25853 | 25882 | } |
| 25854 | 25883 | |
| 25855 | 25884 | _ = try block.addInst(.{ |
| 25856 | .tag = .memcpy, | |
| 25885 | .tag = switch (op) { | |
| 25886 | .memcpy => .memcpy, | |
| 25887 | .memmove => .memmove, | |
| 25888 | }, | |
| 25857 | 25889 | .data = .{ .bin_op = .{ |
| 25858 | 25890 | .lhs = new_dest_ptr, |
| 25859 | 25891 | .rhs = new_src_ptr, |
| ... | ... | @@ -38078,6 +38110,7 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ |
| 38078 | 38110 | .@"panic.forLenMismatch", |
| 38079 | 38111 | .@"panic.memcpyLenMismatch", |
| 38080 | 38112 | .@"panic.memcpyAlias", |
| 38113 | .@"panic.memmoveLenMismatch", | |
| 38081 | 38114 | .@"panic.noreturnReturned", |
| 38082 | 38115 | => try pt.funcType(.{ |
| 38083 | 38116 | .param_types = &.{}, |
src/Zcu.zig+4| ... | ... | @@ -302,6 +302,7 @@ pub const BuiltinDecl = enum { |
| 302 | 302 | @"panic.forLenMismatch", |
| 303 | 303 | @"panic.memcpyLenMismatch", |
| 304 | 304 | @"panic.memcpyAlias", |
| 305 | @"panic.memmoveLenMismatch", | |
| 305 | 306 | @"panic.noreturnReturned", |
| 306 | 307 | |
| 307 | 308 | VaList, |
| ... | ... | @@ -379,6 +380,7 @@ pub const BuiltinDecl = enum { |
| 379 | 380 | .@"panic.forLenMismatch", |
| 380 | 381 | .@"panic.memcpyLenMismatch", |
| 381 | 382 | .@"panic.memcpyAlias", |
| 383 | .@"panic.memmoveLenMismatch", | |
| 382 | 384 | .@"panic.noreturnReturned", |
| 383 | 385 | => .func, |
| 384 | 386 | }; |
| ... | ... | @@ -446,6 +448,7 @@ pub const SimplePanicId = enum { |
| 446 | 448 | for_len_mismatch, |
| 447 | 449 | memcpy_len_mismatch, |
| 448 | 450 | memcpy_alias, |
| 451 | memmove_len_mismatch, | |
| 449 | 452 | noreturn_returned, |
| 450 | 453 | |
| 451 | 454 | pub fn toBuiltin(id: SimplePanicId) BuiltinDecl { |
| ... | ... | @@ -470,6 +473,7 @@ pub const SimplePanicId = enum { |
| 470 | 473 | .for_len_mismatch => .@"panic.forLenMismatch", |
| 471 | 474 | .memcpy_len_mismatch => .@"panic.memcpyLenMismatch", |
| 472 | 475 | .memcpy_alias => .@"panic.memcpyAlias", |
| 476 | .memmove_len_mismatch => .@"panic.memmoveLenMismatch", | |
| 473 | 477 | .noreturn_returned => .@"panic.noreturnReturned", |
| 474 | 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 | 760 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 761 | 761 | .atomic_load => try self.airAtomicLoad(inst), |
| 762 | 762 | .memcpy => try self.airMemcpy(inst), |
| 763 | .memmove => try self.airMemmove(inst), | |
| 763 | 764 | .memset => try self.airMemset(inst, false), |
| 764 | 765 | .memset_safe => try self.airMemset(inst, true), |
| 765 | 766 | .set_union_tag => try self.airSetUnionTag(inst), |
| ... | ... | @@ -5993,6 +5994,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5993 | 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 | 6002 | fn airTagName(self: *Self, inst: Air.Inst.Index) InnerError!void { |
| 5997 | 6003 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5998 | 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 | 749 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 750 | 750 | .atomic_load => try self.airAtomicLoad(inst), |
| 751 | 751 | .memcpy => try self.airMemcpy(inst), |
| 752 | .memmove => try self.airMemmove(inst), | |
| 752 | 753 | .memset => try self.airMemset(inst, false), |
| 753 | 754 | .memset_safe => try self.airMemset(inst, true), |
| 754 | 755 | .set_union_tag => try self.airSetUnionTag(inst), |
| ... | ... | @@ -5963,6 +5964,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { |
| 5963 | 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 | 5972 | fn airTagName(self: *Self, inst: Air.Inst.Index) !void { |
| 5967 | 5973 | const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5968 | 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 | 1581 | .atomic_rmw => try func.airAtomicRmw(inst), |
| 1582 | 1582 | .atomic_load => try func.airAtomicLoad(inst), |
| 1583 | 1583 | .memcpy => try func.airMemcpy(inst), |
| 1584 | .memmove => try func.airMemmove(inst), | |
| 1584 | 1585 | .memset => try func.airMemset(inst, false), |
| 1585 | 1586 | .memset_safe => try func.airMemset(inst, true), |
| 1586 | 1587 | .set_union_tag => try func.airSetUnionTag(inst), |
| ... | ... | @@ -7919,6 +7920,11 @@ fn airMemcpy(func: *Func, inst: Air.Inst.Index) !void { |
| 7919 | 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 | 7928 | fn airTagName(func: *Func, inst: Air.Inst.Index) !void { |
| 7923 | 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 | 604 | .atomic_rmw => try self.airAtomicRmw(inst), |
| 605 | 605 | .atomic_load => try self.airAtomicLoad(inst), |
| 606 | 606 | .memcpy => @panic("TODO try self.airMemcpy(inst)"), |
| 607 | .memmove => @panic("TODO try self.airMemmove(inst)"), | |
| 607 | 608 | .memset => try self.airMemset(inst, false), |
| 608 | 609 | .memset_safe => try self.airMemset(inst, true), |
| 609 | 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 | 2061 | .c_va_copy, |
| 2062 | 2062 | .c_va_end, |
| 2063 | 2063 | .c_va_start, |
| 2064 | .memmove, | |
| 2064 | 2065 | => |tag| return cg.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 2065 | 2066 | |
| 2066 | 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 | 89453 | .memset => try cg.airMemset(inst, false), |
| 89454 | 89454 | .memset_safe => try cg.airMemset(inst, true), |
| 89455 | 89455 | .memcpy => try cg.airMemcpy(inst), |
| 89456 | .memmove => try cg.airMemmove(inst), | |
| 89456 | 89457 | .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst), |
| 89457 | 89458 | .atomic_load => try cg.airAtomicLoad(inst), |
| 89458 | 89459 | .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered), |
| ... | ... | @@ -106472,6 +106473,11 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void { |
| 106472 | 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 | 106481 | fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void { |
| 106476 | 106482 | const pt = self.pt; |
| 106477 | 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 | 3349 | .memset => try airMemset(f, inst, false), |
| 3350 | 3350 | .memset_safe => try airMemset(f, inst, true), |
| 3351 | 3351 | .memcpy => try airMemcpy(f, inst), |
| 3352 | .memmove => try airMemmove(f, inst), | |
| 3352 | 3353 | .set_union_tag => try airSetUnionTag(f, inst), |
| 3353 | 3354 | .get_union_tag => try airGetUnionTag(f, inst), |
| 3354 | 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 | 6977 | } |
| 6977 | 6978 | |
| 6978 | 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 | 6988 | const pt = f.object.dg.pt; |
| 6980 | 6989 | const zcu = pt.zcu; |
| 6981 | 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 | 6999 | try writeArrayLen(f, writer, dest_ptr, dest_ty); |
| 6991 | 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 | 7007 | try writeSliceOrPtr(f, writer, dest_ptr, dest_ty); |
| 6995 | 7008 | try writer.writeAll(", "); |
| 6996 | 7009 | try writeSliceOrPtr(f, writer, src_ptr, src_ty); |
src/codegen/llvm.zig+27| ... | ... | @@ -4940,6 +4940,7 @@ pub const FuncGen = struct { |
| 4940 | 4940 | .memset => try self.airMemset(inst, false), |
| 4941 | 4941 | .memset_safe => try self.airMemset(inst, true), |
| 4942 | 4942 | .memcpy => try self.airMemcpy(inst), |
| 4943 | .memmove => try self.airMemmove(inst), | |
| 4943 | 4944 | .set_union_tag => try self.airSetUnionTag(inst), |
| 4944 | 4945 | .get_union_tag => try self.airGetUnionTag(inst), |
| 4945 | 4946 | .clz => try self.airClzCtz(inst, .ctlz), |
| ... | ... | @@ -9926,6 +9927,32 @@ pub const FuncGen = struct { |
| 9926 | 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 | 9956 | fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { |
| 9930 | 9957 | const o = self.ng.object; |
| 9931 | 9958 | const pt = o.pt; |
src/codegen/spirv.zig+6| ... | ... | @@ -3344,6 +3344,7 @@ const NavGen = struct { |
| 3344 | 3344 | .slice => try self.airSlice(inst), |
| 3345 | 3345 | .aggregate_init => try self.airAggregateInit(inst), |
| 3346 | 3346 | .memcpy => return self.airMemcpy(inst), |
| 3347 | .memmove => return self.airMemmove(inst), | |
| 3347 | 3348 | |
| 3348 | 3349 | .slice_ptr => try self.airSliceField(inst, 0), |
| 3349 | 3350 | .slice_len => try self.airSliceField(inst, 1), |
| ... | ... | @@ -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 | 4923 | fn airSliceField(self: *NavGen, inst: Air.Inst.Index, field: u32) !?IdRef { |
| 4918 | 4924 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 4919 | 4925 | const field_ty = self.typeOfIndex(inst); |
src/print_air.zig+1| ... | ... | @@ -160,6 +160,7 @@ const Writer = struct { |
| 160 | 160 | .cmp_gt_optimized, |
| 161 | 161 | .cmp_neq_optimized, |
| 162 | 162 | .memcpy, |
| 163 | .memmove, | |
| 163 | 164 | .memset, |
| 164 | 165 | .memset_safe, |
| 165 | 166 | => try w.writeBinOp(s, inst), |
src/print_zir.zig+1| ... | ... | @@ -413,6 +413,7 @@ const Writer = struct { |
| 413 | 413 | .min, |
| 414 | 414 | .memcpy, |
| 415 | 415 | .memset, |
| 416 | .memmove, | |
| 416 | 417 | .elem_ptr_node, |
| 417 | 418 | .elem_val_node, |
| 418 | 419 | .elem_ptr, |
test/behavior.zig+1| ... | ... | @@ -55,6 +55,7 @@ test { |
| 55 | 55 | _ = @import("behavior/member_func.zig"); |
| 56 | 56 | _ = @import("behavior/memcpy.zig"); |
| 57 | 57 | _ = @import("behavior/memset.zig"); |
| 58 | _ = @import("behavior/memmove.zig"); | |
| 58 | 59 | _ = @import("behavior/merge_error_sets.zig"); |
| 59 | 60 | _ = @import("behavior/muladd.zig"); |
| 60 | 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 | |
| 7 | 7 | // This excludes builtin functions that return void or noreturn that cannot be tested. |
| 8 | 8 | test { |
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 9 | 10 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 10 | 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 11 | 12 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -17,6 +18,7 @@ test { |
| 17 | 18 | try testing.expectEqual(void, @TypeOf(@breakpoint())); |
| 18 | 19 | try testing.expectEqual({}, @export(&x, .{ .name = "x" })); |
| 19 | 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 | 22 | try testing.expectEqual({}, @memset(@as([*]u8, @ptrFromInt(1))[0..0], undefined)); |
| 21 | 23 | try testing.expectEqual(noreturn, @TypeOf(if (true) @panic("") else {})); |
| 22 | 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 | 29 | pub const forLenMismatch = simple_panic.forLenMismatch; |
| 30 | 30 | pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; |
| 31 | 31 | pub const memcpyAlias = simple_panic.memcpyAlias; |
| 32 | pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch; | |
| 32 | 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 | 25 | pub const forLenMismatch = simple_panic.forLenMismatch; |
| 26 | 26 | pub const memcpyLenMismatch = simple_panic.memcpyLenMismatch; |
| 27 | 27 | pub const memcpyAlias = simple_panic.memcpyAlias; |
| 28 | pub const memmoveLenMismatch = simple_panic.memmoveLenMismatch; | |
| 28 | 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 | 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 | 74 | // error |
| 67 | 75 | // |
| 68 | 76 | // :5:19: error: runtime value contains reference to comptime var |
| ... | ... | @@ -92,3 +100,6 @@ export fn far() void { |
| 92 | 100 | // :63:18: error: runtime value contains reference to comptime var |
| 93 | 101 | // :63:18: note: comptime var pointers are not available at runtime |
| 94 | 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 | 28 | var buf2: [5]u8 = .{ 1, 2, 3, 4, 5 }; |
| 29 | 29 | @memcpy(&buf1, &buf2); |
| 30 | 30 | } |
| 31 | pub export fn memset_array() void { | |
| 31 | pub export fn memcpy_array() void { | |
| 32 | 32 | const buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; |
| 33 | 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 | 65 | // error |
| 37 | 66 | // backend=stage2 |
| ... | ... | @@ -51,3 +80,16 @@ pub export fn memset_array() void { |
| 51 | 80 | // :29:13: error: cannot memcpy to constant pointer |
| 52 | 81 | // :33:13: error: type '[5]u8' is not an indexable pointer |
| 53 | 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 | 12 | var len: usize = 5; |
| 13 | 13 | _ = &len; |
| 14 | 14 | @memcpy(buffer[0..len], buffer[4 .. 4 + len]); |
| 15 | return error.TestFailed; | |
| 15 | 16 | } |
| 16 | 17 | // run |
| 17 | 18 | // backend=stage2,llvm |
test/cases/safety/memcpy_len_mismatch.zig+1| ... | ... | @@ -12,6 +12,7 @@ pub fn main() !void { |
| 12 | 12 | var len: usize = 5; |
| 13 | 13 | _ = &len; |
| 14 | 14 | @memcpy(buffer[0..len], buffer[len .. len + 4]); |
| 15 | return error.TestFailed; | |
| 15 | 16 | } |
| 16 | 17 | // run |
| 17 | 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 | 39 | pub const forLenMismatch = no_panic.forLenMismatch; |
| 40 | 40 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; |
| 41 | 41 | pub const memcpyAlias = no_panic.memcpyAlias; |
| 42 | pub const memmoveLenMismatch = no_panic.memmoveLenMismatch; | |
| 42 | 43 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 43 | 44 | }; |
| 44 | 45 | fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| ... | ... | @@ -86,6 +87,7 @@ pub const panic = struct { |
| 86 | 87 | pub const forLenMismatch = no_panic.forLenMismatch; |
| 87 | 88 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; |
| 88 | 89 | pub const memcpyAlias = no_panic.memcpyAlias; |
| 90 | pub const memmoveLenMismatch = no_panic.memmoveLenMismatch; | |
| 89 | 91 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 90 | 92 | }; |
| 91 | 93 | fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| ... | ... | @@ -133,6 +135,7 @@ pub const panic = struct { |
| 133 | 135 | pub const forLenMismatch = no_panic.forLenMismatch; |
| 134 | 136 | pub const memcpyLenMismatch = no_panic.memcpyLenMismatch; |
| 135 | 137 | pub const memcpyAlias = no_panic.memcpyAlias; |
| 138 | pub const memmoveLenMismatch = no_panic.memmoveLenMismatch; | |
| 136 | 139 | pub const noreturnReturned = no_panic.noreturnReturned; |
| 137 | 140 | }; |
| 138 | 141 | fn myPanicNew(msg: []const u8, _: ?usize) noreturn { |
test/standalone/zerolength_check/src/main.zig+1| ... | ... | @@ -5,6 +5,7 @@ test { |
| 5 | 5 | const source = foo(); |
| 6 | 6 | |
| 7 | 7 | @memcpy(dest, source); |
| 8 | @memmove(dest, source); | |
| 8 | 9 | @memset(dest, 4); |
| 9 | 10 | @memset(dest, undefined); |
| 10 | 11 |