| ... | ... | @@ -10938,6 +10938,7 @@ fn analyzePtrArithmetic( |
| 10938 | 10938 | |
| 10939 | 10939 | const new_ptr_ty = t: { |
| 10940 | 10940 | // Calculate the new pointer alignment. |
| 10941 | // This code is duplicated in `elemPtrType`. |
| 10941 | 10942 | if (ptr_info.@"align" == 0) { |
| 10942 | 10943 | // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. |
| 10943 | 10944 | break :t ptr_ty; |
| ... | ... | @@ -18617,20 +18618,20 @@ fn elemPtr( |
| 18617 | 18618 | .Pointer => { |
| 18618 | 18619 | // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. |
| 18619 | 18620 | const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); |
| 18620 | | const result_ty = try indexable_ty.elemPtrType(sema.arena, sema.mod); |
| 18621 | 18621 | switch (indexable_ty.ptrSize()) { |
| 18622 | 18622 | .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), |
| 18623 | 18623 | .Many, .C => { |
| 18624 | 18624 | const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable); |
| 18625 | 18625 | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 18626 | | |
| 18627 | 18626 | const runtime_src = rs: { |
| 18628 | 18627 | const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src; |
| 18629 | 18628 | const index_val = maybe_index_val orelse break :rs elem_index_src; |
| 18630 | 18629 | const index = @intCast(usize, index_val.toUnsignedInt(target)); |
| 18631 | 18630 | const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod); |
| 18631 | const result_ty = try sema.elemPtrType(indexable_ty, index); |
| 18632 | 18632 | return sema.addConstant(result_ty, elem_ptr); |
| 18633 | 18633 | }; |
| 18634 | const result_ty = try sema.elemPtrType(indexable_ty, null); |
| 18634 | 18635 | |
| 18635 | 18636 | try sema.requireRuntimeBlock(block, runtime_src); |
| 18636 | 18637 | return block.addPtrElemPtr(indexable, elem_index, result_ty); |
| ... | ... | @@ -18883,29 +18884,29 @@ fn elemPtrArray( |
| 18883 | 18884 | const array_sent = array_ty.sentinel() != null; |
| 18884 | 18885 | const array_len = array_ty.arrayLen(); |
| 18885 | 18886 | const array_len_s = array_len + @boolToInt(array_sent); |
| 18886 | | const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, sema.mod); |
| 18887 | 18887 | |
| 18888 | 18888 | if (array_len_s == 0) { |
| 18889 | 18889 | return sema.fail(block, elem_index_src, "indexing into empty array", .{}); |
| 18890 | 18890 | } |
| 18891 | 18891 | |
| 18892 | 18892 | const maybe_undef_array_ptr_val = try sema.resolveMaybeUndefVal(block, array_ptr_src, array_ptr); |
| 18893 | | // index must be defined since it can index out of bounds |
| 18894 | | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 18895 | | |
| 18896 | | if (maybe_index_val) |index_val| { |
| 18897 | | const index = @intCast(usize, index_val.toUnsignedInt(target)); |
| 18893 | // The index must not be undefined since it can be out of bounds. |
| 18894 | const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { |
| 18895 | const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target)); |
| 18898 | 18896 | if (index >= array_len_s) { |
| 18899 | 18897 | const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; |
| 18900 | 18898 | return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); |
| 18901 | 18899 | } |
| 18902 | | } |
| 18900 | break :o index; |
| 18901 | } else null; |
| 18902 | |
| 18903 | const elem_ptr_ty = try sema.elemPtrType(array_ptr_ty, offset); |
| 18904 | |
| 18903 | 18905 | if (maybe_undef_array_ptr_val) |array_ptr_val| { |
| 18904 | 18906 | if (array_ptr_val.isUndef()) { |
| 18905 | 18907 | return sema.addConstUndef(elem_ptr_ty); |
| 18906 | 18908 | } |
| 18907 | | if (maybe_index_val) |index_val| { |
| 18908 | | const index = @intCast(usize, index_val.toUnsignedInt(target)); |
| 18909 | if (offset) |index| { |
| 18909 | 18910 | const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod); |
| 18910 | 18911 | return sema.addConstant(elem_ptr_ty, elem_ptr); |
| 18911 | 18912 | } |
| ... | ... | @@ -18932,14 +18933,14 @@ fn elemPtrArray( |
| 18932 | 18933 | |
| 18933 | 18934 | const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; |
| 18934 | 18935 | try sema.requireRuntimeBlock(block, runtime_src); |
| 18935 | | if (block.wantSafety()) { |
| 18936 | | // Runtime check is only needed if unable to comptime check |
| 18937 | | if (maybe_index_val == null) { |
| 18938 | | const len_inst = try sema.addIntUnsigned(Type.usize, array_len); |
| 18939 | | const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; |
| 18940 | | try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); |
| 18941 | | } |
| 18936 | |
| 18937 | // Runtime check is only needed if unable to comptime check. |
| 18938 | if (block.wantSafety() and offset == null) { |
| 18939 | const len_inst = try sema.addIntUnsigned(Type.usize, array_len); |
| 18940 | const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; |
| 18941 | try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); |
| 18942 | 18942 | } |
| 18943 | |
| 18943 | 18944 | return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); |
| 18944 | 18945 | } |
| 18945 | 18946 | |
| ... | ... | @@ -19007,11 +19008,15 @@ fn elemPtrSlice( |
| 19007 | 19008 | const target = sema.mod.getTarget(); |
| 19008 | 19009 | const slice_ty = sema.typeOf(slice); |
| 19009 | 19010 | const slice_sent = slice_ty.sentinel() != null; |
| 19010 | | const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, sema.mod); |
| 19011 | 19011 | |
| 19012 | 19012 | const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice); |
| 19013 | | // index must be defined since it can index out of bounds |
| 19014 | | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 19013 | // The index must not be undefined since it can be out of bounds. |
| 19014 | const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { |
| 19015 | const index = try sema.usizeCast(block, elem_index_src, index_val.toUnsignedInt(target)); |
| 19016 | break :o index; |
| 19017 | } else null; |
| 19018 | |
| 19019 | const elem_ptr_ty = try sema.elemPtrType(slice_ty, null); |
| 19015 | 19020 | |
| 19016 | 19021 | if (maybe_undef_slice_val) |slice_val| { |
| 19017 | 19022 | if (slice_val.isUndef()) { |
| ... | ... | @@ -19022,8 +19027,7 @@ fn elemPtrSlice( |
| 19022 | 19027 | if (slice_len_s == 0) { |
| 19023 | 19028 | return sema.fail(block, elem_index_src, "indexing into empty slice", .{}); |
| 19024 | 19029 | } |
| 19025 | | if (maybe_index_val) |index_val| { |
| 19026 | | const index = @intCast(usize, index_val.toUnsignedInt(target)); |
| 19030 | if (offset) |index| { |
| 19027 | 19031 | if (index >= slice_len_s) { |
| 19028 | 19032 | const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; |
| 19029 | 19033 | return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); |
| ... | ... | @@ -25364,3 +25368,42 @@ fn compareVector( |
| 25364 | 25368 | } |
| 25365 | 25369 | return Value.Tag.aggregate.create(sema.arena, result_data); |
| 25366 | 25370 | } |
| 25371 | |
| 25372 | /// Returns the type of a pointer to an element. |
| 25373 | /// Asserts that the type is a pointer, and that the element type is indexable. |
| 25374 | /// For *[N]T, return *T |
| 25375 | /// For [*]T, returns *T |
| 25376 | /// For []T, returns *T |
| 25377 | /// Handles const-ness and address spaces in particular. |
| 25378 | /// This code is duplicated in `analyzePtrArithmetic`. |
| 25379 | fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type { |
| 25380 | const ptr_info = ptr_ty.ptrInfo().data; |
| 25381 | const elem_ty = ptr_ty.elemType2(); |
| 25382 | const allow_zero = ptr_info.@"allowzero" and (offset orelse 0) == 0; |
| 25383 | const alignment: u32 = a: { |
| 25384 | // Calculate the new pointer alignment. |
| 25385 | if (ptr_info.@"align" == 0) { |
| 25386 | // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. |
| 25387 | break :a 0; |
| 25388 | } |
| 25389 | // If the addend is not a comptime-known value we can still count on |
| 25390 | // it being a multiple of the type size. |
| 25391 | const target = sema.mod.getTarget(); |
| 25392 | const elem_size = elem_ty.abiSize(target); |
| 25393 | const addend = if (offset) |off| elem_size * off else elem_size; |
| 25394 | |
| 25395 | // The resulting pointer is aligned to the lcd between the offset (an |
| 25396 | // arbitrary number) and the alignment factor (always a power of two, |
| 25397 | // non zero). |
| 25398 | const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align")); |
| 25399 | break :a new_align; |
| 25400 | }; |
| 25401 | return try Type.ptr(sema.arena, sema.mod, .{ |
| 25402 | .pointee_type = elem_ty, |
| 25403 | .mutable = ptr_info.mutable, |
| 25404 | .@"addrspace" = ptr_info.@"addrspace", |
| 25405 | .@"allowzero" = allow_zero, |
| 25406 | .@"volatile" = ptr_info.@"volatile", |
| 25407 | .@"align" = alignment, |
| 25408 | }); |
| 25409 | } |