From 2182d28cb0917b8d869d13802a5955ee35b4537a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Mar 2020 20:55:07 -0400 Subject: [PATCH 01/20] slicing with comptime start and end results in array implements #863 --- src/analyze.cpp | 5 +++- src/codegen.cpp | 72 +++++++++++++++++++++++++++++++++----------- src/ir.cpp | 79 ++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 119 insertions(+), 37 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 9ba247012db5..e9b9f037049a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -791,7 +791,10 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi return existing_entry->value; } - assert(type_is_resolved(child_type, ResolveStatusSizeKnown)); + Error err; + if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { + codegen_report_errors_and_exit(g); + } ZigType *entry = new_type_table_entry(ZigTypeIdArray); diff --git a/src/codegen.cpp b/src/codegen.cpp index 3343807691d5..5a0965507d62 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5422,8 +5422,22 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); - ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry; - ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel; + ZigType *result_type = instruction->base.value->type; + if (!type_has_bits(g, result_type)) { + return nullptr; + } + + ZigValue *sentinel = nullptr; + if (result_type->id == ZigTypeIdPointer) { + ZigType *result_array_type = result_type->data.pointer.child_type; + ir_assert(result_array_type->id == ZigTypeIdArray, &instruction->base); + sentinel = result_array_type->data.array.sentinel; + } else if (result_type->id == ZigTypeIdStruct) { + ZigType *res_slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; + sentinel = res_slice_ptr_type->data.pointer.sentinel; + } else { + zig_unreachable(); + } if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) @@ -5466,18 +5480,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI return tmp_struct_ptr; } - - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->llvm_type), start_val, }; LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (result_type->id == ZigTypeIdPointer) { + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + gen_store_untyped(g, bitcasted, tmp_struct_ptr, 0, false); + return slice_start_ptr; + } else { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + } return tmp_struct_ptr; } else if (array_type->id == ZigTypeIdPointer) { @@ -5493,14 +5513,21 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); + if (result_type->id == ZigTypeIdPointer) { + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + gen_store_untyped(g, bitcasted, tmp_struct_ptr, 0, false); + return bitcasted; + } + if (type_has_bits(g, array_type)) { - size_t gen_ptr_index = instruction->base.value->type->data.structure.fields[slice_ptr_index]->gen_index; + size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); } - size_t gen_len_index = instruction->base.value->type->data.structure.fields[slice_len_index]->gen_index; + size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); gen_store_untyped(g, len_value, len_field_ptr, 0, false); @@ -5510,7 +5537,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); + if (result_type->id != ZigTypeIdPointer) { + assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); + } size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; assert(ptr_index != SIZE_MAX); @@ -5547,15 +5576,22 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (result_type->id == ZigTypeIdPointer) { + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + gen_store_untyped(g, bitcasted, tmp_struct_ptr, 0, false); + return bitcasted; + } else { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; + return tmp_struct_ptr; + } } else { zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index ef7574457613..c52b7c3d74fa 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -849,11 +849,6 @@ static bool is_slice(ZigType *type) { return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; } -static bool slice_is_const(ZigType *type) { - assert(is_slice(type)); - return type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; -} - // This function returns true when you can change the type of a ZigValue and the // value remains meaningful. static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { @@ -26206,7 +26201,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; } - ZigType *return_type; ZigValue *sentinel_val = nullptr; if (instruction->sentinel) { IrInstGen *uncasted_sentinel = instruction->sentinel->child; @@ -26218,6 +26212,46 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); if (sentinel_val == nullptr) return ira->codegen->invalid_inst_gen; + } + + // If start index and end index are both comptime known, then the result type is a pointer to array + // not a slice. + ZigType *return_type; + + if (value_is_comptime(casted_start->value) && + ((end != nullptr && value_is_comptime(end->value)) || array_type->id == ZigTypeIdArray )) + { + ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); + if (!start_val) + return ira->codegen->invalid_inst_gen; + + uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); + + uint64_t end_scalar; + if (end != nullptr) { + ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); + if (!end_val) + return ira->codegen->invalid_inst_gen; + end_scalar = bigint_as_u64(&end_val->data.x_bigint); + } else { + end_scalar = array_type->data.array.len; + } + ZigValue *array_sentinel = (array_type->id == ZigTypeIdArray && end_scalar == array_type->data.array.len) + ? sentinel_val : nullptr; + + if (start_scalar > end_scalar) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, + array_sentinel); + return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, + non_sentinel_slice_ptr_type->data.pointer.is_const, + non_sentinel_slice_ptr_type->data.pointer.is_volatile, + PtrLenSingle, + 0, 0, 0, false); + } else if (sentinel_val != nullptr) { ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { @@ -26401,15 +26435,25 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); - ZigValue *out_val = result->value; - out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); - ZigValue *ptr_val = out_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_val; + if (return_type->id == ZigTypeIdPointer) { + // pointer to array + ptr_val = result->value; + } else { + // slice + result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); + + ptr_val = result->value->data.x_struct.fields[slice_ptr_index]; + + ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index]; + init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); + } + bool return_type_is_const = non_sentinel_slice_ptr_type->data.pointer.is_const; if (array_val) { size_t index = abs_offset + start_scalar; - bool is_const = slice_is_const(return_type); - init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown); + init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown); if (array_type->id == ZigTypeIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut; } else if (is_slice(array_type)) { @@ -26419,15 +26463,15 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } } else if (ptr_is_undef) { ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, - slice_is_const(return_type)); + return_type_is_const); ptr_val->special = ConstValSpecialUndef; } else switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: - init_const_ptr_ref(ira->codegen, ptr_val, - parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type)); + init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee, + return_type_is_const); break; case ConstPtrSpecialBaseArray: zig_unreachable(); @@ -26443,7 +26487,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, - slice_is_const(return_type)); + return_type_is_const); break; case ConstPtrSpecialFunction: zig_panic("TODO"); @@ -26451,9 +26495,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i zig_panic("TODO"); } - ZigValue *len_val = out_val->data.x_struct.fields[slice_len_index]; - init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); - + // In the case of pointer-to-array, we must restore this because above it overwrites ptr_val->type + result->value->type = return_type; return result; } From 0707be8de88823ff943c3fbd7d0035f88d32dd87 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Mar 2020 21:41:46 -0400 Subject: [PATCH 02/20] fixes in semantic analysis needed to support this feature --- lib/std/mem.zig | 4 +- src-self-hosted/stage2.zig | 2 +- src/all_types.hpp | 5 + src/analyze.cpp | 7 ++ src/codegen.cpp | 31 +++---- src/ir.cpp | 155 ++++++++++++++++++++++--------- test/stage1/behavior/align.zig | 21 +++-- test/stage1/behavior/cast.zig | 3 +- test/stage1/behavior/eval.zig | 2 +- test/stage1/behavior/misc.zig | 14 ++- test/stage1/behavior/ptrcast.zig | 2 +- test/stage1/behavior/slice.zig | 10 +- test/stage1/behavior/struct.zig | 4 +- 13 files changed, 172 insertions(+), 88 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 438c15c2ebd6..2ffac680c743 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -560,7 +560,7 @@ pub fn span(ptr: var) Span(@TypeOf(ptr)) { test "span" { var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; - const ptr = array[0..2 :3].ptr; + const ptr = @as([*:3]u16, array[0..2 :3]); testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 })); } @@ -602,7 +602,7 @@ test "len" { testing.expect(len(&array) == 5); testing.expect(len(array[0..3]) == 3); array[2] = 0; - const ptr = array[0..2 :0].ptr; + const ptr = @as([*:0]u16, array[0..2 :0]); testing.expect(len(ptr) == 2); } { diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 5393310f9e82..a1d741bc9b97 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -128,7 +128,7 @@ export fn stage2_translate_c( args_end: [*]?[*]const u8, resources_path: [*:0]const u8, ) Error { - var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0]; + var errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { error.SemanticAnalyzeFail => { out_errors_ptr.* = errors.ptr; diff --git a/src/all_types.hpp b/src/all_types.hpp index 53aae9e23647..5025ff7cb5fc 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -231,6 +231,7 @@ enum ConstPtrSpecial { // The pointer is a reference to a single object. ConstPtrSpecialRef, // The pointer points to an element in an underlying array. + // Not to be confused with ConstPtrSpecialSubArray. ConstPtrSpecialBaseArray, // The pointer points to a field in an underlying struct. ConstPtrSpecialBaseStruct, @@ -257,6 +258,10 @@ enum ConstPtrSpecial { // types to be the same, so all optionals of pointer types use x_ptr // instead of x_optional. ConstPtrSpecialNull, + // The pointer points to a sub-array (not an individual element). + // Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same + // union payload struct (base_array). + ConstPtrSpecialSubArray, }; enum ConstPtrMut { diff --git a/src/analyze.cpp b/src/analyze.cpp index e9b9f037049a..afffe47c82c0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5280,6 +5280,11 @@ static uint32_t hash_const_val_ptr(ZigValue *const_val) { hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); return hash_val; + case ConstPtrSpecialSubArray: + hash_val += (uint32_t)2643358777; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); + return hash_val; case ConstPtrSpecialBaseStruct: hash_val += (uint32_t)3518317043; hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); @@ -6746,6 +6751,7 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { return false; return true; case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) { return false; } @@ -7003,6 +7009,7 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigT render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); return; case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: buf_appendf(buf, "*"); // TODO we need a source node for const_ptr_pointee because it can generate compile errors render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); diff --git a/src/codegen.cpp b/src/codegen.cpp index 5a0965507d62..24e9e1017d28 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5418,8 +5418,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI ZigType *array_type = array_ptr_type->data.pointer.child_type; LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); ZigType *result_type = instruction->base.value->type; @@ -5472,6 +5470,8 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } if (!type_has_bits(g, array_type)) { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); // TODO if runtime safety is on, store 0xaaaaaaa in ptr field @@ -5486,20 +5486,20 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI }; LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - gen_store_untyped(g, bitcasted, tmp_struct_ptr, 0, false); - return slice_start_ptr; + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); } else { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); gen_store_untyped(g, len_value, len_field_ptr, 0, false); - } - return tmp_struct_ptr; + return tmp_struct_ptr; + } } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len != PtrLenSingle); LLVMValueRef start_val = ir_llvm_value(g, instruction->start); @@ -5515,12 +5515,12 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - gen_store_untyped(g, bitcasted, tmp_struct_ptr, 0, false); - return bitcasted; + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); } + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); if (type_has_bits(g, array_type)) { size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); @@ -5537,9 +5537,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - if (result_type->id != ZigTypeIdPointer) { - assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); - } size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; assert(ptr_index != SIZE_MAX); @@ -5578,11 +5575,11 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - gen_store_untyped(g, bitcasted, tmp_struct_ptr, 0, false); - return bitcasted; + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); } else { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); @@ -6676,7 +6673,6 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_co }; return LLVMConstInBoundsGEP(base_ptr, indices, 2); } else { - assert(parent->id == ConstParentIdScalar); return base_ptr; } } @@ -6904,6 +6900,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const cha return const_val->llvm_value; } case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: { ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; assert(array_const_val->type->id == ZigTypeIdArray); diff --git a/src/ir.cpp b/src/ir.cpp index c52b7c3d74fa..6ff067e09699 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -784,14 +784,32 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_ break; case ConstPtrSpecialBaseArray: { ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; - if (const_val->data.x_ptr.data.base_array.elem_index == array_val->type->data.array.len) { + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + if (elem_index == array_val->type->data.array.len) { result = array_val->type->data.array.sentinel; } else { expand_undef_array(g, array_val); - result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; + result = &array_val->data.x_array.data.s_none.elements[elem_index]; } break; } + case ConstPtrSpecialSubArray: { + ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + + // TODO handle sentinel terminated arrays + expand_undef_array(g, array_val); + result = g->pass1_arena->create(); + result->special = array_val->special; + result->type = get_array_type(g, array_val->type->data.array.child_type, + array_val->type->data.array.len - elem_index, nullptr); + result->data.x_array.special = ConstArraySpecialNone; + result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index]; + result->parent.id = ConstParentIdArray; + result->parent.data.p_array.array_val = array_val; + result->parent.data.p_array.elem_index = elem_index; + break; + } case ConstPtrSpecialBaseStruct: { ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; expand_undef_struct(g, struct_val); @@ -3727,8 +3745,8 @@ static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ir_ref_inst_gen(ptr, ira->new_irb.current_basic_block); ir_ref_inst_gen(start, ira->new_irb.current_basic_block); - if (end) ir_ref_inst_gen(end, ira->new_irb.current_basic_block); - ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block); + if (end != nullptr) ir_ref_inst_gen(end, ira->new_irb.current_basic_block); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block); return &instruction->base; } @@ -12672,40 +12690,63 @@ static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* sourc Error err; assert(array_ptr->value->type->id == ZigTypeIdPointer); + assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); - if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { - return ira->codegen->invalid_inst_gen; - } + ZigType *array_type = array_ptr->value->type->data.pointer.child_type; + const size_t array_len = array_type->data.array.len; - assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); + // A zero-sized array can be casted regardless of the destination alignment, or + // whether the pointer is undefined, and the result is always comptime known. + if (array_len == 0) { + ZigValue *undef_array = ira->codegen->pass1_arena->create(); + undef_array->special = ConstValSpecialUndef; + undef_array->type = array_type; - const size_t array_len = array_ptr->value->type->data.pointer.child_type->data.array.len; + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; + result->value->type = wanted_type; + return result; + } - // A zero-sized array can always be casted irregardless of the destination - // alignment - if (array_len != 0) { - wanted_type = adjust_slice_align(ira->codegen, wanted_type, - get_ptr_align(ira->codegen, array_ptr->value->type)); + if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { + return ira->codegen->invalid_inst_gen; } + wanted_type = adjust_slice_align(ira->codegen, wanted_type, + get_ptr_align(ira->codegen, array_ptr->value->type)); + if (instr_is_comptime(array_ptr)) { ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); if (array_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; - ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); - if (pointee == nullptr) - return ira->codegen->invalid_inst_gen; - if (pointee->special != ConstValSpecialRuntime) { - assert(array_ptr_val->type->id == ZigTypeIdPointer); - ZigType *array_type = array_ptr_val->type->data.pointer.child_type; - assert(is_slice(wanted_type)); - bool is_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; + ir_assert(is_slice(wanted_type), source_instr); + bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; + // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee + if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) { + ZigValue *array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; + if (array_val->special != ConstValSpecialRuntime) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, array_val, + array_ptr_val->data.x_ptr.data.base_array.elem_index, + array_type->data.array.len, wanted_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + result->value->type = wanted_type; + return result; + } + } else { + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); + if (pointee == nullptr) + return ira->codegen->invalid_inst_gen; + if (pointee->special != ConstValSpecialRuntime) { + assert(array_ptr_val->type->id == ZigTypeIdPointer); - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, is_const); - result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; - result->value->type = wanted_type; - return result; + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, wanted_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + result->value->type = wanted_type; + return result; + } } } @@ -19931,6 +19972,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; assert(array_val->type->id == ZigTypeIdArray); @@ -20814,6 +20856,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP } break; case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: { size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; new_index = offset + index; @@ -20884,6 +20927,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; @@ -25894,6 +25938,7 @@ static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset start = 0; bound_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; @@ -26027,6 +26072,7 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy dest_start = 0; dest_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; @@ -26070,6 +26116,7 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy src_start = 0; src_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; @@ -26219,7 +26266,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i ZigType *return_type; if (value_is_comptime(casted_start->value) && - ((end != nullptr && value_is_comptime(end->value)) || array_type->id == ZigTypeIdArray )) + ((end != nullptr && value_is_comptime(end->value)) || + (end == nullptr && array_type->id == ZigTypeIdArray))) { ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); if (!start_val) @@ -26244,13 +26292,16 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; } + // TODO in the case of non-zero start index, the byte alignment should be smarter here. + // we should be able to use the same logic as indexing. + uint32_t ptr_byte_alignment = ((end_scalar - start_scalar != 0) && start_scalar == 0) ? + non_sentinel_slice_ptr_type->data.pointer.explicit_alignment : 0; ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, array_sentinel); return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, non_sentinel_slice_ptr_type->data.pointer.is_const, non_sentinel_slice_ptr_type->data.pointer.is_volatile, - PtrLenSingle, - 0, 0, 0, false); + PtrLenSingle, ptr_byte_alignment, 0, 0, false); } else if (sentinel_val != nullptr) { ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); return_type = get_slice_type(ira->codegen, slice_ptr_type); @@ -26283,6 +26334,10 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i abs_offset = 0; rel_end = SIZE_MAX; ptr_is_undef = true; + } else if (parent_ptr->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; } else { array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.base.source_node); if (array_val == nullptr) @@ -26325,6 +26380,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i rel_end = 1; } break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; @@ -26375,6 +26431,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i abs_offset = SIZE_MAX; rel_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; @@ -26454,6 +26511,9 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i if (array_val) { size_t index = abs_offset + start_scalar; init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown); + if (return_type->id == ZigTypeIdPointer) { + ptr_val->data.x_ptr.special = ConstPtrSpecialSubArray; + } if (array_type->id == ZigTypeIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut; } else if (is_slice(array_type)) { @@ -26463,7 +26523,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } } else if (ptr_is_undef) { ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, - return_type_is_const); + return_type_is_const); ptr_val->special = ConstValSpecialUndef; } else switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: @@ -26473,6 +26533,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee, return_type_is_const); break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: zig_unreachable(); case ConstPtrSpecialBaseStruct: @@ -26500,20 +26561,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return result; } - IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - return_type, nullptr, true, true); - if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); - dummy_value->value->special = ConstValSpecialRuntime; - IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, - dummy_value, result_loc->value->type->data.pointer.child_type); - if (type_is_invalid(dummy_result->value->type)) - return ira->codegen->invalid_inst_gen; - } - if (generate_non_null_assert) { IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr); @@ -26523,6 +26570,24 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i ir_build_assert_non_null(ira, &instruction->base.base, ptr_val); } + IrInstGen *result_loc = nullptr; + + if (return_type->id != ZigTypeIdPointer) { + result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + return_type, nullptr, true, true); + if (result_loc != nullptr) { + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); + dummy_value->value->special = ConstValSpecialRuntime; + IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, + dummy_value, result_loc->value->type->data.pointer.child_type); + if (type_is_invalid(dummy_result->value->type)) + return ira->codegen->invalid_inst_gen; + } + } + return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc); } diff --git a/test/stage1/behavior/align.zig b/test/stage1/behavior/align.zig index 5a5138d567a1..d3dd282e5920 100644 --- a/test/stage1/behavior/align.zig +++ b/test/stage1/behavior/align.zig @@ -171,18 +171,19 @@ test "runtime known array index has best alignment possible" { // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = [_]u32{ 1, 2, 3, 4 }; - comptime expect(@TypeOf(smaller[0..]) == []align(2) u32); - comptime expect(@TypeOf(smaller[0..].ptr) == [*]align(2) u32); - testIndex(smaller[0..].ptr, 0, *align(2) u32); - testIndex(smaller[0..].ptr, 1, *align(2) u32); - testIndex(smaller[0..].ptr, 2, *align(2) u32); - testIndex(smaller[0..].ptr, 3, *align(2) u32); + var runtime_zero: usize = 0; + comptime expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32); + comptime expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - testIndex2(array[0..].ptr, 0, *u8); - testIndex2(array[0..].ptr, 1, *u8); - testIndex2(array[0..].ptr, 2, *u8); - testIndex2(array[0..].ptr, 3, *u8); + testIndex2(array[runtime_zero..].ptr, 0, *u8); + testIndex2(array[runtime_zero..].ptr, 1, *u8); + testIndex2(array[runtime_zero..].ptr, 2, *u8); + testIndex2(array[runtime_zero..].ptr, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { comptime expect(@TypeOf(&smaller[index]) == T); diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 0304ddfa9cc7..f1936069f1b8 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -435,7 +435,8 @@ fn incrementVoidPtrValue(value: ?*c_void) void { test "implicit cast from [*]T to ?*c_void" { var a = [_]u8{ 3, 2, 1 }; - incrementVoidPtrArray(a[0..].ptr, 3); + var runtime_zero: usize = 0; + incrementVoidPtrArray(a[runtime_zero..].ptr, 3); expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 })); } diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 55ace6198cdb..2af34eaf7e10 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -524,7 +524,7 @@ test "comptime slice of slice preserves comptime var" { test "comptime slice of pointer preserves comptime var" { comptime { var buff: [10]u8 = undefined; - var a = buff[0..].ptr; + var a = @ptrCast([*]u8, &buff); a[0..1][0] = 1; expect(buff[0..][0..][0] == 1); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 44a8334b5d04..9eeb9896229a 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -102,8 +102,8 @@ test "memcpy and memset intrinsics" { var foo: [20]u8 = undefined; var bar: [20]u8 = undefined; - @memset(foo[0..].ptr, 'A', foo.len); - @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); + @memset(&foo, 'A', foo.len); + @memcpy(&bar, &foo, bar.len); if (bar[11] != 'A') unreachable; } @@ -565,12 +565,16 @@ test "volatile load and store" { expect(ptr.* == 1235); } -test "slice string literal has type []const u8" { +test "slice string literal has correct type" { comptime { - expect(@TypeOf("aoeu"[0..]) == []const u8); + expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8); const array = [_]i32{ 1, 2, 3, 4 }; - expect(@TypeOf(array[0..]) == []const i32); + expect(@TypeOf(array[0..]) == *const [4]i32); } + var runtime_zero: usize = 0; + expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8); + const array = [_]i32{ 1, 2, 3, 4 }; + expect(@TypeOf(array[runtime_zero..]) == []const u8); } test "pointer child field" { diff --git a/test/stage1/behavior/ptrcast.zig b/test/stage1/behavior/ptrcast.zig index 1c1cf251a004..3925325e619b 100644 --- a/test/stage1/behavior/ptrcast.zig +++ b/test/stage1/behavior/ptrcast.zig @@ -13,7 +13,7 @@ fn testReinterpretBytesAsInteger() void { builtin.Endian.Little => 0xab785634, builtin.Endian.Big => 0x345678ab, }; - expect(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected); + expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); } test "reinterpret bytes of an array into an extern struct" { diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index 9dd57f474e67..0d0e33079eb5 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -7,7 +7,7 @@ const mem = std.mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - expect(@ptrToInt(x.ptr) == 0x1000); + expect(@ptrToInt(x) == 0x1000); expect(x.len == 0x500); expect(@ptrToInt(y.ptr) == 0x1100); @@ -47,7 +47,9 @@ test "C pointer slice access" { var buf: [10]u32 = [1]u32{42} ** 10; const c_ptr = @ptrCast([*c]const u32, &buf); - comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1])); + var runtime_zero: usize = 0; + comptime expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1])); + comptime expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1])); for (c_ptr[0..5]) |*cl| { expectEqual(@as(u32, 42), cl.*); @@ -107,7 +109,9 @@ test "obtaining a null terminated slice" { const ptr2 = buf[0..runtime_len :0]; // ptr2 is a null-terminated slice comptime expect(@TypeOf(ptr2) == [:0]u8); - comptime expect(@TypeOf(ptr2[0..2]) == []u8); + comptime expect(@TypeOf(ptr2[0..2]) == *[2]u8); + var runtime_zero: usize = 0; + comptime expect(@TypeOf(ptr2[runtime_zero..2]) == []u8); } test "empty array to slice" { diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index ecec7fe5d69e..0365991ed36f 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -409,8 +409,8 @@ const Bitfields = packed struct { test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; - @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; + @memcpy(&bytes, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, &bytes).*; expect(bitfields.f1 == 0x1111); expect(bitfields.f2 == 0x2222); From c896c5001f55c67ba2379505464f85dcabb3f3f2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Mar 2020 14:23:02 -0400 Subject: [PATCH 03/20] fix slice of string literal having the wrong type --- src/ir.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 6ff067e09699..555856060629 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -26261,13 +26261,16 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; } + ZigType *child_array_type = (array_type->id == ZigTypeIdPointer && + array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type; + // If start index and end index are both comptime known, then the result type is a pointer to array // not a slice. ZigType *return_type; if (value_is_comptime(casted_start->value) && ((end != nullptr && value_is_comptime(end->value)) || - (end == nullptr && array_type->id == ZigTypeIdArray))) + (end == nullptr && child_array_type->id == ZigTypeIdArray))) { ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); if (!start_val) @@ -26282,10 +26285,11 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; end_scalar = bigint_as_u64(&end_val->data.x_bigint); } else { - end_scalar = array_type->data.array.len; + end_scalar = child_array_type->data.array.len; } - ZigValue *array_sentinel = (array_type->id == ZigTypeIdArray && end_scalar == array_type->data.array.len) - ? sentinel_val : nullptr; + ZigValue *array_sentinel = (child_array_type->id == ZigTypeIdArray && + end_scalar == child_array_type->data.array.len) + ? child_array_type->data.array.sentinel : nullptr; if (start_scalar > end_scalar) { ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); @@ -26318,12 +26322,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; - if (array_type->id == ZigTypeIdArray || - (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) - { + if (child_array_type->id == ZigTypeIdArray) { if (array_type->id == ZigTypeIdPointer) { - ZigType *child_array_type = array_type->data.pointer.child_type; - assert(child_array_type->id == ZigTypeIdArray); parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (parent_ptr == nullptr) return ira->codegen->invalid_inst_gen; From 8d0ac6dc4d32daea3561e7de8eeee9ce34d2c5cb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Mar 2020 17:33:44 -0400 Subject: [PATCH 04/20] `@ptrCast` supports casting a slice to pointer --- lib/std/mem.zig | 46 +++++++++++++++++++++++++++++++--------------- src/analyze.cpp | 20 +++++++++++++++++--- src/analyze.hpp | 2 +- src/ir.cpp | 44 +++++++++++++++++++++++++++++++++++++------- 4 files changed, 86 insertions(+), 26 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 2ffac680c743..eb2539720ae4 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1750,34 +1750,50 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { } pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) { - const bytesSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(bytes))) bytes[0..] else bytes; - // let's not give an undefined pointer to @ptrCast // it may be equal to zero and fail a null check - if (bytesSlice.len == 0) { + if (bytes.len == 0) { return &[0]T{}; } - const bytesType = @TypeOf(bytesSlice); - const alignment = comptime meta.alignment(bytesType); + const Bytes = @TypeOf(bytes); + const alignment = comptime meta.alignment(Bytes); - const castTarget = if (comptime trait.isConstPtr(bytesType)) [*]align(alignment) const T else [*]align(alignment) T; + const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T; - return @ptrCast(castTarget, bytesSlice.ptr)[0..@divExact(bytes.len, @sizeOf(T))]; + return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))]; } test "bytesAsSlice" { - const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; - const slice = bytesAsSlice(u16, bytes[0..]); - testing.expect(slice.len == 2); - testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); - testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); + { + const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; + const slice = bytesAsSlice(u16, bytes[0..]); + testing.expect(slice.len == 2); + testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); + testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); + } + { + const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; + var runtime_zero: usize = 0; + const slice = bytesAsSlice(u16, bytes[runtime_zero..]); + testing.expect(slice.len == 2); + testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); + testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); + } } test "bytesAsSlice keeps pointer alignment" { - var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; - const numbers = bytesAsSlice(u32, bytes[0..]); - comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); + { + var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; + const numbers = bytesAsSlice(u32, bytes[0..]); + comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); + } + { + var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; + var runtime_zero: usize = 0; + const numbers = bytesAsSlice(u32, bytes[runtime_zero..]); + comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); + } } test "bytesAsSlice on a packed struct" { diff --git a/src/analyze.cpp b/src/analyze.cpp index afffe47c82c0..1f511fa83450 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4486,7 +4486,14 @@ static uint32_t get_async_frame_align_bytes(CodeGen *g) { } uint32_t get_ptr_align(CodeGen *g, ZigType *type) { - ZigType *ptr_type = get_src_ptr_type(type); + ZigType *ptr_type; + if (type->id == ZigTypeIdStruct) { + assert(type->data.structure.special == StructSpecialSlice); + TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(g, ptr_field); + } else { + ptr_type = get_src_ptr_type(type); + } if (ptr_type->id == ZigTypeIdPointer) { return (ptr_type->data.pointer.explicit_alignment == 0) ? get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment; @@ -4503,8 +4510,15 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) { } } -bool get_ptr_const(ZigType *type) { - ZigType *ptr_type = get_src_ptr_type(type); +bool get_ptr_const(CodeGen *g, ZigType *type) { + ZigType *ptr_type; + if (type->id == ZigTypeIdStruct) { + assert(type->data.structure.special == StructSpecialSlice); + TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(g, ptr_field); + } else { + ptr_type = get_src_ptr_type(type); + } if (ptr_type->id == ZigTypeIdPointer) { return ptr_type->data.pointer.is_const; } else if (ptr_type->id == ZigTypeIdFn) { diff --git a/src/analyze.hpp b/src/analyze.hpp index c7cbab4b8c14..b6404b1882b1 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -76,7 +76,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all ZigType *get_src_ptr_type(ZigType *type); uint32_t get_ptr_align(CodeGen *g, ZigType *type); -bool get_ptr_const(ZigType *type); +bool get_ptr_const(CodeGen *g, ZigType *type); ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry); ZigType *container_ref_type(ZigType *type_entry); bool type_is_complete(ZigType *type_entry); diff --git a/src/ir.cpp b/src/ir.cpp index 555856060629..15edfbfc7bed 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -25479,11 +25479,22 @@ static IrInstGen *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstSrcE static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { Error err; - ZigType *ptr_type = get_src_ptr_type(ty); + ZigType *ptr_type; + if (is_slice(ty)) { + TypeStructField *ptr_field = ty->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + } else { + ptr_type = get_src_ptr_type(ty); + } assert(ptr_type != nullptr); if (ptr_type->id == ZigTypeIdPointer) { if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return err; + } else if (is_slice(ptr_type)) { + TypeStructField *ptr_field = ptr_type->data.structure.fields[slice_ptr_index]; + ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; } *result_align = get_ptr_align(ira->codegen, ty); @@ -27615,10 +27626,18 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn // We have a check for zero bits later so we use get_src_ptr_type to // validate src_type and dest_type. - ZigType *src_ptr_type = get_src_ptr_type(src_type); - if (src_ptr_type == nullptr) { - ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_inst_gen; + ZigType *if_slice_ptr_type; + if (is_slice(src_type)) { + TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; + if_slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + } else { + if_slice_ptr_type = src_type; + + ZigType *src_ptr_type = get_src_ptr_type(src_type); + if (src_ptr_type == nullptr) { + ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); + return ira->codegen->invalid_inst_gen; + } } ZigType *dest_ptr_type = get_src_ptr_type(dest_type); @@ -27628,7 +27647,7 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn return ira->codegen->invalid_inst_gen; } - if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) { + if (get_ptr_const(ira->codegen, src_type) && !get_ptr_const(ira->codegen, dest_type)) { ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); return ira->codegen->invalid_inst_gen; } @@ -27646,7 +27665,10 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; - if (type_has_bits(ira->codegen, dest_type) && !type_has_bits(ira->codegen, src_type) && safety_check_on) { + if (safety_check_on && + type_has_bits(ira->codegen, dest_type) && + !type_has_bits(ira->codegen, if_slice_ptr_type)) + { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("'%s' and '%s' do not have the same in-memory representation", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); @@ -27657,6 +27679,14 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn return ira->codegen->invalid_inst_gen; } + // For slices, follow the `ptr` field. + if (is_slice(src_type)) { + TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; + IrInstGen *ptr_ref = ir_get_ref(ira, source_instr, ptr, true, false); + IrInstGen *ptr_ptr = ir_analyze_struct_field_ptr(ira, source_instr, ptr_field, ptr_ref, src_type, false); + ptr = ir_get_deref(ira, source_instr, ptr_ptr, nullptr); + } + if (instr_is_comptime(ptr)) { bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad; From 8ea0a00f406bb04c08a8fa4471c3a3895f82b24a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Mar 2020 18:54:09 -0400 Subject: [PATCH 05/20] improve std lib code for the new semantics --- lib/std/mem.zig | 12 +++++------ lib/std/meta.zig | 37 ++++++++++++++++++++------------- src-self-hosted/translate_c.zig | 26 +++++++++++------------ 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index eb2539720ae4..dcdf76e19539 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1829,21 +1829,19 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type { } pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) { - const actualSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(slice))) slice[0..] else slice; - const actualSliceTypeInfo = @typeInfo(@TypeOf(actualSlice)).Pointer; + const Slice = @TypeOf(slice); // let's not give an undefined pointer to @ptrCast // it may be equal to zero and fail a null check - if (actualSlice.len == 0 and actualSliceTypeInfo.sentinel == null) { + if (slice.len == 0 and comptime meta.sentinel(Slice) == null) { return &[0]u8{}; } - const sliceType = @TypeOf(actualSlice); - const alignment = comptime meta.alignment(sliceType); + const alignment = comptime meta.alignment(Slice); - const castTarget = if (comptime trait.isConstPtr(sliceType)) [*]align(alignment) const u8 else [*]align(alignment) u8; + const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8; - return @ptrCast(castTarget, actualSlice.ptr)[0 .. actualSlice.len * @sizeOf(comptime meta.Child(sliceType))]; + return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Child(Slice))]; } test "sliceAsBytes" { diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 65809abb5ca5..e8b9f16895c6 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -115,30 +115,37 @@ test "std.meta.Child" { testing.expect(Child(?u8) == u8); } -/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel -pub fn Sentinel(comptime T: type) Child(T) { - // comptime asserts that ptr has a sentinel +/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value, +/// or `null` if there is not one. +/// Types which cannot possibly have a sentinel will be a compile error. +pub fn sentinel(comptime T: type) ?Child(T) { switch (@typeInfo(T)) { - .Array => |arrayInfo| { - return comptime arrayInfo.sentinel.?; - }, - .Pointer => |ptrInfo| { - switch (ptrInfo.size) { - .Many, .Slice => { - return comptime ptrInfo.sentinel.?; + .Array => |info| return info.sentinel, + .Pointer => |info| { + switch (info.size) { + .Many, .Slice => return info.sentinel, + .One => switch (info.child) { + .Array => |array_info| return array_info.sentinel, + else => {}, }, else => {}, } }, else => {}, } - @compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'"); + @compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel"); } -test "std.meta.Sentinel" { - testing.expectEqual(@as(u8, 0), Sentinel([:0]u8)); - testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8)); - testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8)); +test "std.meta.sentinel" { + testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?); + testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?); + testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?); + testing.expectEqual(@as(u8, 0), sentinel(*const [5:0]u8).?); + + testing.expect(sentinel([]u8) == null); + testing.expect(sentinel([*]u8) == null); + testing.expect(sentinel([5]u8) == null); + testing.expect(sentinel(*const [5]u8) == null); } pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 84416ac98085..e6cf8fb149dd 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1744,20 +1744,18 @@ fn writeEscapedString(buf: []u8, s: []const u8) void { // Returns either a string literal or a slice of `buf`. fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { return switch (c) { - '\"' => "\\\""[0..], - '\'' => "\\'"[0..], - '\\' => "\\\\"[0..], - '\n' => "\\n"[0..], - '\r' => "\\r"[0..], - '\t' => "\\t"[0..], - else => { - // Handle the remaining escapes Zig doesn't support by turning them - // into their respective hex representation - if (std.ascii.isCntrl(c)) - return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable - else - return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; - }, + '\"' => "\\\"", + '\'' => "\\'", + '\\' => "\\\\", + '\n' => "\\n", + '\r' => "\\r", + '\t' => "\\t", + // Handle the remaining escapes Zig doesn't support by turning them + // into their respective hex representation + else => if (std.ascii.isCntrl(c)) + std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable + else + std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable, }; } From 2b4134459d468812ac106c124ee6fa5f5e8b9574 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Mar 2020 19:45:59 -0400 Subject: [PATCH 06/20] fix alignment when slicing with comptime start and end index --- src/ir.cpp | 79 +++++++++++++++++++++++----------- test/stage1/behavior/slice.zig | 2 +- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 15edfbfc7bed..059a73c2f967 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -20575,6 +20575,44 @@ static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_ allow_zero); } +static Error compute_elem_align(IrAnalyze *ira, ZigType *elem_type, uint32_t base_ptr_align, + uint64_t elem_index, uint32_t *result) +{ + Error err; + + if (base_ptr_align == 0) { + *result = 0; + return ErrorNone; + } + + // figure out the largest alignment possible + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return err; + + uint64_t elem_size = type_size(ira->codegen, elem_type); + uint64_t abi_align = get_abi_alignment(ira->codegen, elem_type); + uint64_t ptr_align = base_ptr_align; + + uint64_t chosen_align = abi_align; + if (ptr_align >= abi_align) { + while (ptr_align > abi_align) { + if ((elem_index * elem_size) % ptr_align == 0) { + chosen_align = ptr_align; + break; + } + ptr_align >>= 1; + } + } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { + chosen_align = ptr_align; + } else { + // can't get here because guaranteed elem_size >= abi_align + zig_unreachable(); + } + + *result = chosen_align; + return ErrorNone; +} + static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) { Error err; IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child; @@ -20713,29 +20751,11 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, nullptr, nullptr); } else if (return_type->data.pointer.explicit_alignment != 0) { - // figure out the largest alignment possible - - if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown))) + uint32_t chosen_align; + if ((err = compute_elem_align(ira, return_type->data.pointer.child_type, + return_type->data.pointer.explicit_alignment, index, &chosen_align))) + { return ira->codegen->invalid_inst_gen; - - uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); - uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); - uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); - - uint64_t chosen_align = abi_align; - if (ptr_align >= abi_align) { - while (ptr_align > abi_align) { - if ((index * elem_size) % ptr_align == 0) { - chosen_align = ptr_align; - break; - } - ptr_align >>= 1; - } - } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { - chosen_align = ptr_align; - } else { - // can't get here because guaranteed elem_size >= abi_align - zig_unreachable(); } return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align); } @@ -26172,6 +26192,8 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy } static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) { + Error err; + IrInstGen *ptr_ptr = instruction->ptr->child; if (type_is_invalid(ptr_ptr->value->type)) return ira->codegen->invalid_inst_gen; @@ -26307,10 +26329,13 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; } - // TODO in the case of non-zero start index, the byte alignment should be smarter here. - // we should be able to use the same logic as indexing. - uint32_t ptr_byte_alignment = ((end_scalar - start_scalar != 0) && start_scalar == 0) ? - non_sentinel_slice_ptr_type->data.pointer.explicit_alignment : 0; + uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; + uint32_t ptr_byte_alignment = 0; + if (end_scalar > start_scalar) { + if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) + return ira->codegen->invalid_inst_gen; + } + ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, array_sentinel); return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, @@ -26318,9 +26343,11 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i non_sentinel_slice_ptr_type->data.pointer.is_volatile, PtrLenSingle, ptr_byte_alignment, 0, 0, false); } else if (sentinel_val != nullptr) { + // TODO deal with non-abi-alignment here ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { + // TODO deal with non-abi-alignment here return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); } diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index 0d0e33079eb5..26ad8425f27f 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -10,7 +10,7 @@ test "compile time slice of pointer to hard coded address" { expect(@ptrToInt(x) == 0x1000); expect(x.len == 0x500); - expect(@ptrToInt(y.ptr) == 0x1100); + expect(@ptrToInt(y) == 0x1100); expect(y.len == 0x400); } From 4435b05b6b007df9159058fb5ae6f8902eecd1d8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Mar 2020 20:11:47 -0400 Subject: [PATCH 07/20] fix regression when slicing 0-bit pointers --- src/codegen.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 24e9e1017d28..14fb6dc8b33a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5513,6 +5513,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } + if (!type_has_bits(g, array_type)) { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + return tmp_struct_ptr; + } + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); if (result_type->id == ZigTypeIdPointer) { ir_assert(instruction->result_loc == nullptr, &instruction->base); @@ -5521,18 +5530,11 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - if (type_has_bits(g, array_type)) { - size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - } - - size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - + size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); return tmp_struct_ptr; + } else if (array_type->id == ZigTypeIdStruct) { assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); From 72a261b4d35b4f720e189150eb5e73e0fa52ae35 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 Mar 2020 23:32:03 -0400 Subject: [PATCH 08/20] fix runtime slice of pointer not setting length --- src/codegen.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 14fb6dc8b33a..21e74ba6098f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5530,9 +5530,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + return tmp_struct_ptr; } else if (array_type->id == ZigTypeIdStruct) { From e947f0c7409c719377ca08fb09ec72a558a60d99 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Mar 2020 11:19:24 -0400 Subject: [PATCH 09/20] 0-bit array type does not resolve child type --- src/analyze.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1f511fa83450..77d3f333312d 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -780,6 +780,8 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa } ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) { + Error err; + TypeId type_id = {}; type_id.id = ZigTypeIdArray; type_id.data.array.codegen = g; @@ -791,8 +793,9 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi return existing_entry->value; } - Error err; - if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { + size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); + + if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { codegen_report_errors_and_exit(g); } @@ -806,9 +809,8 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi } buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name)); - size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); entry->size_in_bits = child_type->size_in_bits * full_array_size; - entry->abi_align = child_type->abi_align; + entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align; entry->abi_size = child_type->abi_size * full_array_size; entry->data.array.child_type = child_type; From 8688c437455d8e8f1f031177375e570022c16ce7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Mar 2020 12:22:39 -0400 Subject: [PATCH 10/20] when result loc is a slice, avoid evaluating lazy start..end This prevents lazy values from being unnecessarily evaluated. --- src/ir.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 059a73c2f967..657956f3346d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -26191,6 +26191,16 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy return ir_build_memcpy_gen(ira, &instruction->base.base, casted_dest_ptr, casted_src_ptr, casted_count); } +static ZigType *get_result_loc_type(IrAnalyze *ira, ResultLoc *result_loc) { + if (result_loc == nullptr) return nullptr; + + if (result_loc->id == ResultLocIdCast) { + return ir_resolve_type(ira, result_loc->source_instruction->child); + } + + return nullptr; +} + static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) { Error err; @@ -26297,11 +26307,16 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i ZigType *child_array_type = (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type; - // If start index and end index are both comptime known, then the result type is a pointer to array - // not a slice. ZigType *return_type; - if (value_is_comptime(casted_start->value) && + // If start index and end index are both comptime known, then the result type is a pointer to array + // not a slice. However, if the start or end index is a lazy value, and the result location is a slice, + // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these + // values by making the return type a slice. + ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc); + + if ((res_loc_type == nullptr || !is_slice(res_loc_type)) && + value_is_comptime(casted_start->value) && ((end != nullptr && value_is_comptime(end->value)) || (end == nullptr && child_array_type->id == ZigTypeIdArray))) { From 2164b511cce235ec4a49de99452e4900835bfba8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Mar 2020 19:07:37 -0400 Subject: [PATCH 11/20] partial revert of an improvement this branch made because it uncovered a result location bug, and I need to get this branch merged before going into a result location rabbit hole. also fix the result type of slicing when the indexes are runtime known and the result should be sentinel terminated. --- src/ir.cpp | 111 ++++++++++++++++++++------------- test/stage1/behavior/align.zig | 15 +++-- test/stage1/behavior/misc.zig | 4 +- 3 files changed, 79 insertions(+), 51 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 657956f3346d..93f265a2d562 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12693,34 +12693,50 @@ static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* sourc assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); ZigType *array_type = array_ptr->value->type->data.pointer.child_type; - const size_t array_len = array_type->data.array.len; + size_t array_len = array_type->data.array.len; // A zero-sized array can be casted regardless of the destination alignment, or // whether the pointer is undefined, and the result is always comptime known. - if (array_len == 0) { - ZigValue *undef_array = ira->codegen->pass1_arena->create(); - undef_array->special = ConstValSpecialUndef; - undef_array->type = array_type; - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); - result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; - result->value->type = wanted_type; - return result; - } + // TODO However, this is exposing a result location bug that I failed to solve on the first try. + // If you want to try to fix the bug, uncomment this block and get the tests passing. + //if (array_len == 0 && array_type->data.array.sentinel == nullptr) { + // ZigValue *undef_array = ira->codegen->pass1_arena->create(); + // undef_array->special = ConstValSpecialUndef; + // undef_array->type = array_type; + + // IrInstGen *result = ir_const(ira, source_instr, wanted_type); + // init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + // result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; + // result->value->type = wanted_type; + // return result; + //} if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } - wanted_type = adjust_slice_align(ira->codegen, wanted_type, - get_ptr_align(ira->codegen, array_ptr->value->type)); + if (array_len != 0) { + wanted_type = adjust_slice_align(ira->codegen, wanted_type, + get_ptr_align(ira->codegen, array_ptr->value->type)); + } if (instr_is_comptime(array_ptr)) { - ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); + UndefAllowed undef_allowed = (array_len == 0) ? UndefOk : UndefBad; + ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, undef_allowed); if (array_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; ir_assert(is_slice(wanted_type), source_instr); + if (array_ptr_val->special == ConstValSpecialUndef) { + ZigValue *undef_array = ira->codegen->pass1_arena->create(); + undef_array->special = ConstValSpecialUndef; + undef_array->type = array_type; + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; + result->value->type = wanted_type; + return result; + } bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) { @@ -26314,18 +26330,13 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these // values by making the return type a slice. ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc); - - if ((res_loc_type == nullptr || !is_slice(res_loc_type)) && - value_is_comptime(casted_start->value) && + bool result_loc_is_slice = (res_loc_type != nullptr && is_slice(res_loc_type)); + bool end_is_known = !result_loc_is_slice && ((end != nullptr && value_is_comptime(end->value)) || - (end == nullptr && child_array_type->id == ZigTypeIdArray))) - { - ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); - if (!start_val) - return ira->codegen->invalid_inst_gen; - - uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); + (end == nullptr && child_array_type->id == ZigTypeIdArray)); + ZigValue *array_sentinel = sentinel_val; + if (end_is_known) { uint64_t end_scalar; if (end != nullptr) { ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); @@ -26335,36 +26346,46 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } else { end_scalar = child_array_type->data.array.len; } - ZigValue *array_sentinel = (child_array_type->id == ZigTypeIdArray && - end_scalar == child_array_type->data.array.len) - ? child_array_type->data.array.sentinel : nullptr; + array_sentinel = (child_array_type->id == ZigTypeIdArray && end_scalar == child_array_type->data.array.len) + ? child_array_type->data.array.sentinel : sentinel_val; - if (start_scalar > end_scalar) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); - return ira->codegen->invalid_inst_gen; - } + if (value_is_comptime(casted_start->value)) { + ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); + if (!start_val) + return ira->codegen->invalid_inst_gen; - uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; - uint32_t ptr_byte_alignment = 0; - if (end_scalar > start_scalar) { - if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) + uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); + + if (start_scalar > end_scalar) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); return ira->codegen->invalid_inst_gen; - } + } + + uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; + uint32_t ptr_byte_alignment = 0; + if (end_scalar > start_scalar) { + if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) + return ira->codegen->invalid_inst_gen; + } - ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, - array_sentinel); - return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, - non_sentinel_slice_ptr_type->data.pointer.is_const, - non_sentinel_slice_ptr_type->data.pointer.is_volatile, - PtrLenSingle, ptr_byte_alignment, 0, 0, false); - } else if (sentinel_val != nullptr) { + ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, + array_sentinel); + return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, + non_sentinel_slice_ptr_type->data.pointer.is_const, + non_sentinel_slice_ptr_type->data.pointer.is_volatile, + PtrLenSingle, ptr_byte_alignment, 0, 0, false); + goto done_with_return_type; + } + } + if (array_sentinel != nullptr) { // TODO deal with non-abi-alignment here - ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); + ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, array_sentinel); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { // TODO deal with non-abi-alignment here return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); } +done_with_return_type: if (instr_is_comptime(ptr_ptr) && value_is_comptime(casted_start->value) && diff --git a/test/stage1/behavior/align.zig b/test/stage1/behavior/align.zig index d3dd282e5920..e9b7e0f1d689 100644 --- a/test/stage1/behavior/align.zig +++ b/test/stage1/behavior/align.zig @@ -5,10 +5,17 @@ const builtin = @import("builtin"); var foo: u8 align(4) = 100; test "global variable alignment" { - expect(@TypeOf(&foo).alignment == 4); - expect(@TypeOf(&foo) == *align(4) u8); - const slice = @as(*[1]u8, &foo)[0..]; - expect(@TypeOf(slice) == []align(4) u8); + comptime expect(@TypeOf(&foo).alignment == 4); + comptime expect(@TypeOf(&foo) == *align(4) u8); + { + const slice = @as(*[1]u8, &foo)[0..]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + } + { + var runtime_zero: usize = 0; + const slice = @as(*[1]u8, &foo)[runtime_zero..]; + comptime expect(@TypeOf(slice) == []align(4) u8); + } } fn derp() align(@sizeOf(usize) * 2) i32 { diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 9eeb9896229a..51ee6f3a4f55 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -572,9 +572,9 @@ test "slice string literal has correct type" { expect(@TypeOf(array[0..]) == *const [4]i32); } var runtime_zero: usize = 0; - expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8); + comptime expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8); const array = [_]i32{ 1, 2, 3, 4 }; - expect(@TypeOf(array[runtime_zero..]) == []const u8); + comptime expect(@TypeOf(array[runtime_zero..]) == []const i32); } test "pointer child field" { From b5dba702fff35c2d9aa86c9d5dd93a4a38d3b75b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Mar 2020 19:29:24 -0400 Subject: [PATCH 12/20] fixes to std.meta behavior tests are passing now --- lib/std/meta.zig | 34 ++++++++++++++++++++++++++++--- test/stage1/behavior/pointers.zig | 9 ++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index e8b9f16895c6..7343cfc51a4e 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -104,7 +104,7 @@ pub fn Child(comptime T: type) type { .Array => |info| info.child, .Pointer => |info| info.child, .Optional => |info| info.child, - else => @compileError("Expected pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), + else => @compileError("Expected pointer, optional, or array type, found '" ++ @typeName(T) ++ "'"), }; } @@ -115,16 +115,39 @@ test "std.meta.Child" { testing.expect(Child(?u8) == u8); } +/// Given a "memory span" type, returns the "element type". +pub fn Elem(comptime T: type) type { + switch (@typeInfo(T)) { + .Array => |info| return info.child, + .Pointer => |info| switch (info.size) { + .One => switch (@typeInfo(info.child)) { + .Array => |array_info| return array_info.child, + else => {}, + }, + .Many, .C, .Slice => return info.child, + }, + else => {}, + } + @compileError("Expected pointer, slice, or array, found '" ++ @typeName(T) ++ "'"); +} + +test "std.meta.Elem" { + testing.expect(Elem([1]u8) == u8); + testing.expect(Elem([*]u8) == u8); + testing.expect(Elem([]u8) == u8); + testing.expect(Elem(*[10]u8) == u8); +} + /// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value, /// or `null` if there is not one. /// Types which cannot possibly have a sentinel will be a compile error. -pub fn sentinel(comptime T: type) ?Child(T) { +pub fn sentinel(comptime T: type) ?Elem(T) { switch (@typeInfo(T)) { .Array => |info| return info.sentinel, .Pointer => |info| { switch (info.size) { .Many, .Slice => return info.sentinel, - .One => switch (info.child) { + .One => switch (@typeInfo(info.child)) { .Array => |array_info| return array_info.sentinel, else => {}, }, @@ -137,6 +160,11 @@ pub fn sentinel(comptime T: type) ?Child(T) { } test "std.meta.sentinel" { + testSentinel(); + comptime testSentinel(); +} + +fn testSentinel() void { testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?); testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?); testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?); diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index bcc1d62df36d..fbce9731f0df 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -159,12 +159,13 @@ test "allowzero pointer and slice" { var opt_ptr: ?[*]allowzero i32 = ptr; expect(opt_ptr != null); expect(@ptrToInt(ptr) == 0); - var slice = ptr[0..10]; - expect(@TypeOf(slice) == []allowzero i32); + var runtime_zero: usize = 0; + var slice = ptr[runtime_zero..10]; + comptime expect(@TypeOf(slice) == []allowzero i32); expect(@ptrToInt(&slice[5]) == 20); - expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); - expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); + comptime expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); + comptime expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); } test "assign null directly to C pointer and test null equality" { From 7fa88cc0a678a3690b61054030f5597b623964a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Mar 2020 20:35:19 -0400 Subject: [PATCH 13/20] std lib fixups for new semantics std lib tests are passing now --- lib/std/fmt.zig | 3 ++- lib/std/fs.zig | 2 +- lib/std/hash/auto_hash.zig | 12 ++++++---- lib/std/json.zig | 23 +++++++++++++------ lib/std/mem.zig | 47 ++++++-------------------------------- lib/std/meta/trait.zig | 12 ++++++---- lib/std/net.zig | 10 ++++---- src/ir.cpp | 2 +- 8 files changed, 47 insertions(+), 64 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 592fe9bb4e02..7fc996193856 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1223,7 +1223,8 @@ test "slice" { try testFmt("slice: abc\n", "slice: {}\n", .{value}); } { - const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; + var runtime_zero: usize = 0; + const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero]; try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); } diff --git a/lib/std/fs.zig b/lib/std/fs.zig index bc02fd0c91e9..01ed80c742f3 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -360,7 +360,7 @@ pub const Dir = struct { if (self.index >= self.end_index) { const rc = os.system.getdirentries( self.dir.fd, - self.buf[0..].ptr, + &self.buf, self.buf.len, &self.seek, ); diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 2c6b37e3db78..b5d6c528a502 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -40,7 +40,9 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void { .DeepRecursive => hashArray(hasher, key, .DeepRecursive), }, - .Many, .C, => switch (strat) { + .Many, + .C, + => switch (strat) { .Shallow => hash(hasher, @ptrToInt(key), .Shallow), else => @compileError( \\ unknown-length pointers and C pointers cannot be hashed deeply. @@ -236,9 +238,11 @@ test "hash slice shallow" { defer std.testing.allocator.destroy(array1); array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 }; const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 }; - const a = array1[0..]; - const b = array2[0..]; - const c = array1[0..3]; + // TODO audit deep/shallow - maybe it has the wrong behavior with respect to array pointers and slices + var runtime_zero: usize = 0; + const a = array1[runtime_zero..]; + const b = array2[runtime_zero..]; + const c = array1[runtime_zero..3]; testing.expect(testHashShallow(a) == testHashShallow(a)); testing.expect(testHashShallow(a) != testHashShallow(array1)); testing.expect(testHashShallow(a) != testHashShallow(b)); diff --git a/lib/std/json.zig b/lib/std/json.zig index f5a72d86da23..3dd8088785cf 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2249,11 +2249,16 @@ pub const StringifyOptions = struct { // TODO: allow picking if []u8 is string or array? }; +pub const StringifyError = error{ + TooMuchData, + DifferentData, +}; + pub fn stringify( value: var, options: StringifyOptions, out_stream: var, -) !void { +) StringifyError!void { const T = @TypeOf(value); switch (@typeInfo(T)) { .Float, .ComptimeFloat => { @@ -2320,9 +2325,15 @@ pub fn stringify( return; }, .Pointer => |ptr_info| switch (ptr_info.size) { - .One => { - // TODO: avoid loops? - return try stringify(value.*, options, out_stream); + .One => switch (@typeInfo(ptr_info.child)) { + .Array => { + const Slice = []const std.meta.Elem(ptr_info.child); + return stringify(@as(Slice, value), options, out_stream); + }, + else => { + // TODO: avoid loops? + return stringify(value.*, options, out_stream); + }, }, // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) .Slice => { @@ -2381,9 +2392,7 @@ pub fn stringify( }, else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), }, - .Array => |info| { - return try stringify(value[0..], options, out_stream); - }, + .Array => return stringify(&value, options, out_stream), else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), } unreachable; diff --git a/lib/std/mem.zig b/lib/std/mem.zig index dcdf76e19539..951f40ef19f6 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1586,24 +1586,24 @@ pub fn nativeToBig(comptime T: type, x: T) T { } fn AsBytesReturnType(comptime P: type) type { - if (comptime !trait.isSingleItemPtr(P)) + if (!trait.isSingleItemPtr(P)) @compileError("expected single item pointer, passed " ++ @typeName(P)); - const size = @as(usize, @sizeOf(meta.Child(P))); - const alignment = comptime meta.alignment(P); + const size = @sizeOf(meta.Child(P)); + const alignment = meta.alignment(P); if (alignment == 0) { - if (comptime trait.isConstPtr(P)) + if (trait.isConstPtr(P)) return *const [size]u8; return *[size]u8; } - if (comptime trait.isConstPtr(P)) + if (trait.isConstPtr(P)) return *align(alignment) const [size]u8; return *align(alignment) [size]u8; } -///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness. +/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness. pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) { const P = @TypeOf(ptr); return @ptrCast(AsBytesReturnType(P), ptr); @@ -1841,7 +1841,7 @@ pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) { const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8; - return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Child(Slice))]; + return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))]; } test "sliceAsBytes" { @@ -1911,39 +1911,6 @@ test "sliceAsBytes and bytesAsSlice back" { testing.expect(bytes[11] == math.maxInt(u8)); } -fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { - if (trait.isConstPtr(T)) - return *const [length]meta.Child(meta.Child(T)); - return *[length]meta.Child(meta.Child(T)); -} - -/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. -/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863 -pub fn subArrayPtr( - ptr: var, - comptime start: usize, - comptime length: usize, -) SubArrayPtrReturnType(@TypeOf(ptr), length) { - assert(start + length <= ptr.*.len); - - const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length); - const T = meta.Child(meta.Child(@TypeOf(ptr))); - return @ptrCast(ReturnType, &ptr[start]); -} - -test "subArrayPtr" { - const a1: [6]u8 = "abcdef".*; - const sub1 = subArrayPtr(&a1, 2, 3); - testing.expect(eql(u8, sub1, "cde")); - - var a2: [6]u8 = "abcdef".*; - var sub2 = subArrayPtr(&a2, 2, 3); - - testing.expect(eql(u8, sub2, "cde")); - sub2[1] = 'X'; - testing.expect(eql(u8, &a2, "abcXef")); -} - /// Round an address up to the nearest aligned address /// The alignment must be a power of 2 and greater than 0. pub fn alignForward(addr: usize, alignment: usize) usize { diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig index c0fb8c50256a..c99e1389f4b6 100644 --- a/lib/std/meta/trait.zig +++ b/lib/std/meta/trait.zig @@ -230,9 +230,10 @@ pub fn isSingleItemPtr(comptime T: type) bool { test "std.meta.trait.isSingleItemPtr" { const array = [_]u8{0} ** 10; - testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); - testing.expect(!isSingleItemPtr(@TypeOf(array))); - testing.expect(!isSingleItemPtr(@TypeOf(array[0..1]))); + comptime testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); + comptime testing.expect(!isSingleItemPtr(@TypeOf(array))); + var runtime_zero: usize = 0; + testing.expect(!isSingleItemPtr(@TypeOf(array[runtime_zero..1]))); } pub fn isManyItemPtr(comptime T: type) bool { @@ -259,7 +260,8 @@ pub fn isSlice(comptime T: type) bool { test "std.meta.trait.isSlice" { const array = [_]u8{0} ** 10; - testing.expect(isSlice(@TypeOf(array[0..]))); + var runtime_zero: usize = 0; + testing.expect(isSlice(@TypeOf(array[runtime_zero..]))); testing.expect(!isSlice(@TypeOf(array))); testing.expect(!isSlice(@TypeOf(&array[0]))); } @@ -276,7 +278,7 @@ pub fn isIndexable(comptime T: type) bool { test "std.meta.trait.isIndexable" { const array = [_]u8{0} ** 10; - const slice = array[0..]; + const slice = @as([]const u8, &array); testing.expect(isIndexable(@TypeOf(array))); testing.expect(isIndexable(@TypeOf(&array))); diff --git a/lib/std/net.zig b/lib/std/net.zig index c2328b9bd0eb..44efc4644a2d 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -612,8 +612,7 @@ fn linuxLookupName( } else { mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntNative(u32, @ptrCast(*[4]u8, da6.addr[12..].ptr), addr.addr.in.addr); + mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr); da4.addr = addr.addr.in.addr; da = @ptrCast(*os.sockaddr, &da4); dalen = @sizeOf(os.sockaddr_in); @@ -821,7 +820,7 @@ fn linuxLookupNameFromHosts( // Skip to the delimiter in the stream, to fix parsing try stream.skipUntilDelimiterOrEof('\n'); // Use the truncated line. A truncated comment or hostname will be handled correctly. - break :blk line_buf[0..]; + break :blk @as([]u8, &line_buf); // TODO the cast should not be necessary }, else => |e| return e, }) |line| { @@ -958,7 +957,8 @@ fn linuxLookupNameFromDns( } } - var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] }; + var hack: usize = 0; // TODO remove this hack + var ap = [2][]u8{ apbuf[0][0..hack], apbuf[1][0..hack] }; try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc); var i: usize = 0; @@ -1015,7 +1015,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { // Skip to the delimiter in the stream, to fix parsing try stream.skipUntilDelimiterOrEof('\n'); // Give an empty line to the while loop, which will be skipped. - break :blk line_buf[0..0]; + break :blk @as([]u8, line_buf[0..0]); // TODO the cast should not be necessary }, else => |e| return e, }) |line| { diff --git a/src/ir.cpp b/src/ir.cpp index 93f265a2d562..4800dd910945 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14633,7 +14633,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // *[N]T to ?[]const T + // *[N]T to ?[]T if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdPointer && From 61266d26212e89e97d285eb61416d625303704bc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Mar 2020 21:25:35 -0400 Subject: [PATCH 14/20] test & docs fixups to work with new semantics --- doc/langref.html.in | 36 ++++++++++++++++-------------------- lib/std/os/windows.zig | 10 +++++++++- test/compare_output.zig | 2 +- test/compile_errors.zig | 33 +++++++++++---------------------- test/runtime_safety.zig | 2 +- 5 files changed, 38 insertions(+), 45 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 3a7892fd455e..7def9b42baf9 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2093,8 +2093,9 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@TypeOf(&foo).alignment == 4); assert(@TypeOf(&foo) == *align(4) u8); - const slice = @as(*[1]u8, &foo)[0..]; - assert(@TypeOf(slice) == []align(4) u8); + const as_pointer_to_array: *[1]u8 = &foo; + const as_slice: []u8 = as_pointer_to_array; + assert(@TypeOf(as_slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; } @@ -2187,7 +2188,8 @@ test "basic slices" { // a slice is that the array's length is part of the type and known at // compile-time, whereas the slice's length is known at runtime. // Both can be accessed with the `len` field. - const slice = array[0..array.len]; + var known_at_runtime_zero: usize = 0; + const slice = array[known_at_runtime_zero..array.len]; assert(&slice[0] == &array[0]); assert(slice.len == array.len); @@ -2207,13 +2209,15 @@ test "basic slices" { {#code_end#}

This is one reason we prefer slices to pointers.

{#code_begin|test|slices#} -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const fmt = @import("std").fmt; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const fmt = std.fmt; test "using slices for strings" { - // Zig has no concept of strings. String literals are arrays of u8, and - // in general the string type is []u8 (slice of u8). + // Zig has no concept of strings. String literals are const pointers to + // arrays of u8, and by convention parameters that are "strings" are + // expected to be UTF-8 encoded slices of u8. // Here we coerce [5]u8 to []const u8 const hello: []const u8 = "hello"; const world: []const u8 = "世界"; @@ -2222,7 +2226,7 @@ test "using slices for strings" { // You can use slice syntax on an array to convert an array into a slice. const all_together_slice = all_together[0..]; // String concatenation example. - const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{hello, world}); + const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{ hello, world }); // Generally, you can use UTF-8 and not worry about whether something is a // string. If you don't need to deal with individual characters, no need @@ -2239,23 +2243,15 @@ test "slice pointer" { slice[2] = 3; assert(slice[2] == 3); // The slice is mutable because we sliced a mutable pointer. - assert(@TypeOf(slice) == []u8); + // Furthermore, it is actually a pointer to an array, since the start + // and end indexes were both comptime-known. + assert(@TypeOf(slice) == *[5]u8); // You can also slice a slice: const slice2 = slice[2..3]; assert(slice2.len == 1); assert(slice2[0] == 3); } - -test "slice widening" { - // Zig supports slice widening and slice narrowing. Cast a slice of u8 - // to a slice of anything else, and Zig will perform the length conversion. - const array align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13 }; - const slice = mem.bytesAsSlice(u32, array[0..]); - assert(slice.len == 2); - assert(slice[0] == 0x12121212); - assert(slice[1] == 0x13131313); -} {#code_end#} {#see_also|Pointers|for|Arrays#} diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index f5acb70adb20..813a77c27586 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1276,7 +1276,15 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { // 614 is the length of the longest windows error desciption var buf_u16: [614]u16 = undefined; var buf_u8: [614]u8 = undefined; - var len = kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), buf_u16[0..].ptr, buf_u16.len / @sizeOf(TCHAR), null); + const len = kernel32.FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + null, + err, + MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), + &buf_u16, + buf_u16.len / @sizeOf(TCHAR), + null, + ); _ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable; std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] }); std.debug.dumpCurrentStackTrace(null); diff --git a/test/compare_output.zig b/test/compare_output.zig index 1a0179c4c22d..46c475e04609 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -292,7 +292,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub export fn main() c_int { \\ var array = [_]u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?*c_void, &array), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f894a152a74b..abcdc48dc66f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -103,18 +103,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:3:23: error: pointer to size 0 type has no address", }); - cases.addTest("slice to pointer conversion mismatch", - \\pub fn bytesAsSlice(bytes: var) [*]align(1) const u16 { - \\ return @ptrCast([*]align(1) const u16, bytes.ptr)[0..1]; - \\} - \\test "bytesAsSlice" { - \\ const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; - \\ const slice = bytesAsSlice(bytes[0..]); - \\} - , &[_][]const u8{ - "tmp.zig:2:54: error: expected type '[*]align(1) const u16', found '[]align(1) const u16'", - }); - cases.addTest("access invalid @typeInfo decl", \\const A = B; \\test "Crash" { @@ -1915,16 +1903,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:7:15: error: switch must handle all possibilities", }); - cases.add("reading past end of pointer casted array", - \\comptime { - \\ const array: [4]u8 = "aoeu".*; - \\ const slice = array[1..]; - \\ const int_ptr = @ptrCast(*const u24, slice.ptr); - \\ const deref = int_ptr.*; - \\} - , &[_][]const u8{ - "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", - }); + // TODO uncomment before merging branch + //cases.add("reading past end of pointer casted array", + // \\comptime { + // \\ const array: [4]u8 = "aoeu".*; + // \\ const sub_array = array[1..]; + // \\ const int_ptr = @ptrCast(*const u24, sub_array); + // \\ const deref = int_ptr.*; + // \\} + //, &[_][]const u8{ + // "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", + //}); cases.add("error note for function parameter incompatibility", \\fn do_the_thing(func: fn (arg: i32) void) void {} diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 5047bfd0d0b6..b8ab47ddac99 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -69,7 +69,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\pub fn main() void { \\ var buf: [4]u8 = undefined; - \\ const ptr = buf[0..].ptr; + \\ const ptr: [*]u8 = &buf; \\ const slice = ptr[0..3 :0]; \\} ); From f824658e136738ea75c8bb3b53d9a67b2c4402b7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Mar 2020 10:54:20 -0400 Subject: [PATCH 15/20] slicing sentinel-terminated slice without end now results in a sentinel-terminated slice. --- src/ir.cpp | 4 ++ test/stage1/behavior/slice.zig | 79 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 4800dd910945..e20111e21f32 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -26249,6 +26249,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i end = nullptr; } + ZigValue *slice_sentinel_val = nullptr; ZigType *non_sentinel_slice_ptr_type; ZigType *elem_type; @@ -26299,6 +26300,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } } else if (is_slice(array_type)) { ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + slice_sentinel_val = maybe_sentineled_slice_ptr_type->data.pointer.sentinel; non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; } else { @@ -26376,6 +26378,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i PtrLenSingle, ptr_byte_alignment, 0, 0, false); goto done_with_return_type; } + } else if (array_sentinel == nullptr && end == nullptr) { + array_sentinel = slice_sentinel_val; } if (array_sentinel != nullptr) { // TODO deal with non-abi-alignment here diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index 26ad8425f27f..203a3b72d32f 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -130,3 +130,82 @@ test "empty array to slice" { S.doTheTest(); comptime S.doTheTest(); } + +test "@ptrCast slice to pointer" { + const S = struct { + fn doTheTest() void { + var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; + var slice: []u8 = &array; + var ptr = @ptrCast(*u16, slice); + expect(ptr.* == 65535); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} + +test "slicing producing an array" { + const S = struct { + fn doTheTest() void { + testArray(); + testArrayZ(); + testPointer(); + testPointerZ(); + testSlice(); + testSliceZ(); + } + + fn testArray() void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = array[1..3]; + comptime expect(@TypeOf(slice) == *[2]u8); + expect(slice[0] == 2); + expect(slice[1] == 3); + } + + fn testArrayZ() void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + comptime expect(@TypeOf(array[1..3]) == *[2]u8); + comptime expect(@TypeOf(array[1..5]) == *[4:0]u8); + comptime expect(@TypeOf(array[1..]) == *[4:0]u8); + comptime expect(@TypeOf(array[1..3 :4]) == *[2:4]u8); + } + + fn testPointer() void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*]u8 = &array; + var slice = pointer[1..3]; + comptime expect(@TypeOf(slice) == *[2]u8); + expect(slice[0] == 2); + expect(slice[1] == 3); + } + + fn testPointerZ() void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*:0]u8 = &array; + comptime expect(@TypeOf(pointer[1..3]) == *[2]u8); + comptime expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8); + } + + fn testSlice() void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var src_slice: []u8 = &array; + var slice = src_slice[1..3]; + comptime expect(@TypeOf(slice) == *[2]u8); + expect(slice[0] == 2); + expect(slice[1] == 3); + } + + fn testSliceZ() void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var slice: [:0]u8 = &array; + comptime expect(@TypeOf(slice[1..3]) == *[2]u8); + comptime expect(@TypeOf(slice[1..]) == [:0]u8); + comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} From 8ddf9d84ffb208042ae7ea0fb3dc9fbfb2b5c983 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Mar 2020 11:17:46 -0400 Subject: [PATCH 16/20] add behavior tests for slicing with comptime indexes --- test/stage1/behavior/slice.zig | 70 +++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index 203a3b72d32f..d58132cb08a2 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -145,15 +145,21 @@ test "@ptrCast slice to pointer" { comptime S.doTheTest(); } -test "slicing producing an array" { +test "slice syntax resulting in pointer-to-array" { const S = struct { fn doTheTest() void { testArray(); testArrayZ(); + testArray0(); + testArrayAlign(); testPointer(); testPointerZ(); + testPointer0(); + testPointerAlign(); testSlice(); testSliceZ(); + testSlice0(); + testSliceAlign(); } fn testArray() void { @@ -172,6 +178,28 @@ test "slicing producing an array" { comptime expect(@TypeOf(array[1..3 :4]) == *[2:4]u8); } + fn testArray0() void { + { + var array = [0]u8{}; + var slice = array[0..0]; + comptime expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var slice = array[0..0]; + comptime expect(@TypeOf(slice) == *[0:0]u8); + expect(slice[0] == 0); + } + } + + fn testArrayAlign() void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = array[4..5]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + expect(slice[0] == 5); + comptime expect(@TypeOf(array[0..2]) == *align(4) [2]u8); + } + fn testPointer() void { var array = [5]u8{ 1, 2, 3, 4, 5 }; var pointer: [*]u8 = &array; @@ -188,6 +216,22 @@ test "slicing producing an array" { comptime expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8); } + fn testPointer0() void { + var pointer: [*]u0 = &[1]u0{0}; + var slice = pointer[0..1]; + comptime expect(@TypeOf(slice) == *[1]u0); + expect(slice[0] == 0); + } + + fn testPointerAlign() void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*]align(4) u8 = &array; + var slice = pointer[4..5]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + expect(slice[0] == 5); + comptime expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8); + } + fn testSlice() void { var array = [5]u8{ 1, 2, 3, 4, 5 }; var src_slice: []u8 = &array; @@ -204,6 +248,30 @@ test "slicing producing an array" { comptime expect(@TypeOf(slice[1..]) == [:0]u8); comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); } + + fn testSlice0() void { + { + var array = [0]u8{}; + var src_slice: []u8 = &array; + var slice = src_slice[0..0]; + comptime expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var src_slice: [:0]u8 = &array; + var slice = src_slice[0..0]; + comptime expect(@TypeOf(slice) == *[0]u8); + } + } + + fn testSliceAlign() void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var src_slice: []align(4) u8 = &array; + var slice = src_slice[4..5]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + expect(slice[0] == 5); + comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8); + } }; S.doTheTest(); From 1d7861a36e1bcd8f7bfdb53716ef53467704922b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Mar 2020 13:18:14 -0400 Subject: [PATCH 17/20] fix incorrect sentinel check --- lib/std/crypto/sha2.zig | 6 ++---- src/all_types.hpp | 1 + src/codegen.cpp | 14 +++----------- src/ir.cpp | 6 ++++-- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index fd7ad532a3f7..f004bceac3bd 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -167,8 +167,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { const rr = d.s[0 .. params.out_len / 32]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntBig(u32, out[4 * j ..][0..4], s); } } @@ -509,8 +508,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { const rr = d.s[0 .. params.out_len / 64]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceBig(u64, out[8 * j .. 8 * j + 8], s); + mem.writeIntBig(u64, out[8 * j ..][0..8], s); } } diff --git a/src/all_types.hpp b/src/all_types.hpp index 5025ff7cb5fc..6719d78a9245 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3711,6 +3711,7 @@ struct IrInstGenSlice { IrInstGen *start; IrInstGen *end; IrInstGen *result_loc; + ZigValue *sentinel; bool safety_check_on; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 21e74ba6098f..8fae16e55113 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5425,17 +5425,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI return nullptr; } - ZigValue *sentinel = nullptr; - if (result_type->id == ZigTypeIdPointer) { - ZigType *result_array_type = result_type->data.pointer.child_type; - ir_assert(result_array_type->id == ZigTypeIdArray, &instruction->base); - sentinel = result_array_type->data.array.sentinel; - } else if (result_type->id == ZigTypeIdStruct) { - ZigType *res_slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; - sentinel = res_slice_ptr_type->data.pointer.sentinel; - } else { - zig_unreachable(); - } + // This is not whether the result type has a sentinel, but whether there should be a sentinel check, + // e.g. if they used [a..b :s] syntax. + ZigValue *sentinel = instruction->sentinel; if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) diff --git a/src/ir.cpp b/src/ir.cpp index e20111e21f32..8cd780e46d85 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3732,7 +3732,8 @@ static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *s } static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type, - IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc) + IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc, + ZigValue *sentinel) { IrInstGenSlice *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); @@ -3742,6 +3743,7 @@ static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, instruction->end = end; instruction->safety_check_on = safety_check_on; instruction->result_loc = result_loc; + instruction->sentinel = sentinel; ir_ref_inst_gen(ptr, ira->new_irb.current_basic_block); ir_ref_inst_gen(start, ira->new_irb.current_basic_block); @@ -26667,7 +26669,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, - casted_start, end, instruction->safety_check_on, result_loc); + casted_start, end, instruction->safety_check_on, result_loc, sentinel_val); } static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) { From f614d94faa3b2a259c3a82cd66f167029f20d224 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Mar 2020 14:48:47 -0400 Subject: [PATCH 18/20] update std lib to take advantage of slicing with comptime indexes --- lib/std/crypto/aes.zig | 38 +++++++++++------------ lib/std/crypto/blake2.zig | 11 +++---- lib/std/crypto/chacha20.zig | 61 ++++++++++++++++++------------------- lib/std/crypto/md5.zig | 3 +- lib/std/crypto/poly1305.zig | 29 +++++++++--------- lib/std/crypto/sha1.zig | 3 +- lib/std/crypto/sha3.zig | 5 ++- lib/std/crypto/x25519.zig | 41 ++++++++++++------------- lib/std/hash/siphash.zig | 6 ++-- lib/std/hash/wyhash.zig | 2 +- lib/std/mem.zig | 6 ++-- lib/std/rand.zig | 2 +- lib/std/unicode.zig | 20 ++++++------ 13 files changed, 108 insertions(+), 119 deletions(-) diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig index 1cc166f94301..81dc56f0b311 100644 --- a/lib/std/crypto/aes.zig +++ b/lib/std/crypto/aes.zig @@ -15,10 +15,10 @@ fn rotw(w: u32) u32 { // Encrypt one block from src into dst, using the expanded key xk. fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { - var s0 = mem.readIntSliceBig(u32, src[0..4]); - var s1 = mem.readIntSliceBig(u32, src[4..8]); - var s2 = mem.readIntSliceBig(u32, src[8..12]); - var s3 = mem.readIntSliceBig(u32, src[12..16]); + var s0 = mem.readIntBig(u32, src[0..4]); + var s1 = mem.readIntBig(u32, src[4..8]); + var s2 = mem.readIntBig(u32, src[8..12]); + var s3 = mem.readIntBig(u32, src[12..16]); // First round just XORs input with key. s0 ^= xk[0]; @@ -58,18 +58,18 @@ fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { s2 ^= xk[k + 2]; s3 ^= xk[k + 3]; - mem.writeIntSliceBig(u32, dst[0..4], s0); - mem.writeIntSliceBig(u32, dst[4..8], s1); - mem.writeIntSliceBig(u32, dst[8..12], s2); - mem.writeIntSliceBig(u32, dst[12..16], s3); + mem.writeIntBig(u32, dst[0..4], s0); + mem.writeIntBig(u32, dst[4..8], s1); + mem.writeIntBig(u32, dst[8..12], s2); + mem.writeIntBig(u32, dst[12..16], s3); } // Decrypt one block from src into dst, using the expanded key xk. pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { - var s0 = mem.readIntSliceBig(u32, src[0..4]); - var s1 = mem.readIntSliceBig(u32, src[4..8]); - var s2 = mem.readIntSliceBig(u32, src[8..12]); - var s3 = mem.readIntSliceBig(u32, src[12..16]); + var s0 = mem.readIntBig(u32, src[0..4]); + var s1 = mem.readIntBig(u32, src[4..8]); + var s2 = mem.readIntBig(u32, src[8..12]); + var s3 = mem.readIntBig(u32, src[12..16]); // First round just XORs input with key. s0 ^= xk[0]; @@ -109,10 +109,10 @@ pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { s2 ^= xk[k + 2]; s3 ^= xk[k + 3]; - mem.writeIntSliceBig(u32, dst[0..4], s0); - mem.writeIntSliceBig(u32, dst[4..8], s1); - mem.writeIntSliceBig(u32, dst[8..12], s2); - mem.writeIntSliceBig(u32, dst[12..16], s3); + mem.writeIntBig(u32, dst[0..4], s0); + mem.writeIntBig(u32, dst[4..8], s1); + mem.writeIntBig(u32, dst[8..12], s2); + mem.writeIntBig(u32, dst[12..16], s3); } fn xorBytes(dst: []u8, a: []const u8, b: []const u8) usize { @@ -154,8 +154,8 @@ fn AES(comptime keysize: usize) type { var n: usize = 0; while (n < src.len) { ctx.encrypt(keystream[0..], ctrbuf[0..]); - var ctr_i = std.mem.readIntSliceBig(u128, ctrbuf[0..]); - std.mem.writeIntSliceBig(u128, ctrbuf[0..], ctr_i +% 1); + var ctr_i = std.mem.readIntBig(u128, ctrbuf[0..]); + std.mem.writeIntBig(u128, ctrbuf[0..], ctr_i +% 1); n += xorBytes(dst[n..], src[n..], &keystream); } @@ -251,7 +251,7 @@ fn expandKey(key: []const u8, enc: []u32, dec: []u32) void { var i: usize = 0; var nk = key.len / 4; while (i < nk) : (i += 1) { - enc[i] = mem.readIntSliceBig(u32, key[4 * i .. 4 * i + 4]); + enc[i] = mem.readIntBig(u32, key[4 * i ..][0..4]); } while (i < enc.len) : (i += 1) { var t = enc[i - 1]; diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig index e03d8f7dab87..fc1d59290ed3 100644 --- a/lib/std/crypto/blake2.zig +++ b/lib/std/crypto/blake2.zig @@ -123,8 +123,7 @@ fn Blake2s(comptime out_len: usize) type { const rr = d.h[0 .. out_len / 32]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntLittle(u32, out[4 * j ..][0..4], s); } } @@ -135,8 +134,7 @@ fn Blake2s(comptime out_len: usize) type { var v: [16]u32 = undefined; for (m) |*r, i| { - // TODO https://github.com/ziglang/zig/issues/863 - r.* = mem.readIntSliceLittle(u32, b[4 * i .. 4 * i + 4]); + r.* = mem.readIntLittle(u32, b[4 * i ..][0..4]); } var k: usize = 0; @@ -358,8 +356,7 @@ fn Blake2b(comptime out_len: usize) type { const rr = d.h[0 .. out_len / 64]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u64, out[8 * j .. 8 * j + 8], s); + mem.writeIntLittle(u64, out[8 * j ..][0..8], s); } } @@ -370,7 +367,7 @@ fn Blake2b(comptime out_len: usize) type { var v: [16]u64 = undefined; for (m) |*r, i| { - r.* = mem.readIntSliceLittle(u64, b[8 * i .. 8 * i + 8]); + r.* = mem.readIntLittle(u64, b[8 * i ..][0..8]); } var k: usize = 0; diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index d67877b05117..f6008745af7d 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -61,8 +61,7 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { } for (x) |_, i| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u32, out[4 * i .. 4 * i + 4], x[i] +% input[i]); + mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i] +% input[i]); } } @@ -73,10 +72,10 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo const c = "expand 32-byte k"; const constant_le = [_]u32{ - mem.readIntSliceLittle(u32, c[0..4]), - mem.readIntSliceLittle(u32, c[4..8]), - mem.readIntSliceLittle(u32, c[8..12]), - mem.readIntSliceLittle(u32, c[12..16]), + mem.readIntLittle(u32, c[0..4]), + mem.readIntLittle(u32, c[4..8]), + mem.readIntLittle(u32, c[8..12]), + mem.readIntLittle(u32, c[12..16]), }; mem.copy(u32, ctx[0..], constant_le[0..4]); @@ -120,19 +119,19 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: var k: [8]u32 = undefined; var c: [4]u32 = undefined; - k[0] = mem.readIntSliceLittle(u32, key[0..4]); - k[1] = mem.readIntSliceLittle(u32, key[4..8]); - k[2] = mem.readIntSliceLittle(u32, key[8..12]); - k[3] = mem.readIntSliceLittle(u32, key[12..16]); - k[4] = mem.readIntSliceLittle(u32, key[16..20]); - k[5] = mem.readIntSliceLittle(u32, key[20..24]); - k[6] = mem.readIntSliceLittle(u32, key[24..28]); - k[7] = mem.readIntSliceLittle(u32, key[28..32]); + k[0] = mem.readIntLittle(u32, key[0..4]); + k[1] = mem.readIntLittle(u32, key[4..8]); + k[2] = mem.readIntLittle(u32, key[8..12]); + k[3] = mem.readIntLittle(u32, key[12..16]); + k[4] = mem.readIntLittle(u32, key[16..20]); + k[5] = mem.readIntLittle(u32, key[20..24]); + k[6] = mem.readIntLittle(u32, key[24..28]); + k[7] = mem.readIntLittle(u32, key[28..32]); c[0] = counter; - c[1] = mem.readIntSliceLittle(u32, nonce[0..4]); - c[2] = mem.readIntSliceLittle(u32, nonce[4..8]); - c[3] = mem.readIntSliceLittle(u32, nonce[8..12]); + c[1] = mem.readIntLittle(u32, nonce[0..4]); + c[2] = mem.readIntLittle(u32, nonce[4..8]); + c[3] = mem.readIntLittle(u32, nonce[8..12]); chaCha20_internal(out, in, k, c); } @@ -147,19 +146,19 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32] var k: [8]u32 = undefined; var c: [4]u32 = undefined; - k[0] = mem.readIntSliceLittle(u32, key[0..4]); - k[1] = mem.readIntSliceLittle(u32, key[4..8]); - k[2] = mem.readIntSliceLittle(u32, key[8..12]); - k[3] = mem.readIntSliceLittle(u32, key[12..16]); - k[4] = mem.readIntSliceLittle(u32, key[16..20]); - k[5] = mem.readIntSliceLittle(u32, key[20..24]); - k[6] = mem.readIntSliceLittle(u32, key[24..28]); - k[7] = mem.readIntSliceLittle(u32, key[28..32]); + k[0] = mem.readIntLittle(u32, key[0..4]); + k[1] = mem.readIntLittle(u32, key[4..8]); + k[2] = mem.readIntLittle(u32, key[8..12]); + k[3] = mem.readIntLittle(u32, key[12..16]); + k[4] = mem.readIntLittle(u32, key[16..20]); + k[5] = mem.readIntLittle(u32, key[20..24]); + k[6] = mem.readIntLittle(u32, key[24..28]); + k[7] = mem.readIntLittle(u32, key[28..32]); c[0] = @truncate(u32, counter); c[1] = @truncate(u32, counter >> 32); - c[2] = mem.readIntSliceLittle(u32, nonce[0..4]); - c[3] = mem.readIntSliceLittle(u32, nonce[4..8]); + c[2] = mem.readIntLittle(u32, nonce[0..4]); + c[3] = mem.readIntLittle(u32, nonce[4..8]); const block_size = (1 << 6); // The full block size is greater than the address space on a 32bit machine @@ -463,8 +462,8 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, mac.update(zeros[0..padding]); } var lens: [16]u8 = undefined; - mem.writeIntSliceLittle(u64, lens[0..8], data.len); - mem.writeIntSliceLittle(u64, lens[8..16], plaintext.len); + mem.writeIntLittle(u64, lens[0..8], data.len); + mem.writeIntLittle(u64, lens[8..16], plaintext.len); mac.update(lens[0..]); mac.final(dst[plaintext.len..]); } @@ -500,8 +499,8 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, mac.update(zeros[0..padding]); } var lens: [16]u8 = undefined; - mem.writeIntSliceLittle(u64, lens[0..8], data.len); - mem.writeIntSliceLittle(u64, lens[8..16], ciphertext.len); + mem.writeIntLittle(u64, lens[0..8], data.len); + mem.writeIntLittle(u64, lens[8..16], ciphertext.len); mac.update(lens[0..]); var computedTag: [16]u8 = undefined; mac.final(computedTag[0..]); diff --git a/lib/std/crypto/md5.zig b/lib/std/crypto/md5.zig index d9dd08c9047d..ac8948ca20a5 100644 --- a/lib/std/crypto/md5.zig +++ b/lib/std/crypto/md5.zig @@ -112,8 +112,7 @@ pub const Md5 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntLittle(u32, out[4 * j ..][0..4], s); } } diff --git a/lib/std/crypto/poly1305.zig b/lib/std/crypto/poly1305.zig index 2395b1c7aac9..fda978307d28 100644 --- a/lib/std/crypto/poly1305.zig +++ b/lib/std/crypto/poly1305.zig @@ -3,11 +3,11 @@ // https://monocypher.org/ const std = @import("../std.zig"); -const builtin = @import("builtin"); +const builtin = std.builtin; const Endian = builtin.Endian; -const readIntSliceLittle = std.mem.readIntSliceLittle; -const writeIntSliceLittle = std.mem.writeIntSliceLittle; +const readIntLittle = std.mem.readIntLittle; +const writeIntLittle = std.mem.writeIntLittle; pub const Poly1305 = struct { const Self = @This(); @@ -59,19 +59,19 @@ pub const Poly1305 = struct { { var i: usize = 0; while (i < 1) : (i += 1) { - ctx.r[0] = readIntSliceLittle(u32, key[0..4]) & 0x0fffffff; + ctx.r[0] = readIntLittle(u32, key[0..4]) & 0x0fffffff; } } { var i: usize = 1; while (i < 4) : (i += 1) { - ctx.r[i] = readIntSliceLittle(u32, key[i * 4 .. i * 4 + 4]) & 0x0ffffffc; + ctx.r[i] = readIntLittle(u32, key[i * 4 ..][0..4]) & 0x0ffffffc; } } { var i: usize = 0; while (i < 4) : (i += 1) { - ctx.pad[i] = readIntSliceLittle(u32, key[i * 4 + 16 .. i * 4 + 16 + 4]); + ctx.pad[i] = readIntLittle(u32, key[i * 4 + 16 ..][0..4]); } } @@ -168,10 +168,10 @@ pub const Poly1305 = struct { const nb_blocks = nmsg.len >> 4; var i: usize = 0; while (i < nb_blocks) : (i += 1) { - ctx.c[0] = readIntSliceLittle(u32, nmsg[0..4]); - ctx.c[1] = readIntSliceLittle(u32, nmsg[4..8]); - ctx.c[2] = readIntSliceLittle(u32, nmsg[8..12]); - ctx.c[3] = readIntSliceLittle(u32, nmsg[12..16]); + ctx.c[0] = readIntLittle(u32, nmsg[0..4]); + ctx.c[1] = readIntLittle(u32, nmsg[4..8]); + ctx.c[2] = readIntLittle(u32, nmsg[8..12]); + ctx.c[3] = readIntLittle(u32, nmsg[12..16]); polyBlock(ctx); nmsg = nmsg[16..]; } @@ -210,11 +210,10 @@ pub const Poly1305 = struct { const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000 const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000 - // TODO https://github.com/ziglang/zig/issues/863 - writeIntSliceLittle(u32, out[0..], @truncate(u32, uu0)); - writeIntSliceLittle(u32, out[4..], @truncate(u32, uu1)); - writeIntSliceLittle(u32, out[8..], @truncate(u32, uu2)); - writeIntSliceLittle(u32, out[12..], @truncate(u32, uu3)); + writeIntLittle(u32, out[0..4], @truncate(u32, uu0)); + writeIntLittle(u32, out[4..8], @truncate(u32, uu1)); + writeIntLittle(u32, out[8..12], @truncate(u32, uu2)); + writeIntLittle(u32, out[12..16], @truncate(u32, uu3)); ctx.secureZero(); } diff --git a/lib/std/crypto/sha1.zig b/lib/std/crypto/sha1.zig index 5be42180a16e..6edf7b745e1d 100644 --- a/lib/std/crypto/sha1.zig +++ b/lib/std/crypto/sha1.zig @@ -109,8 +109,7 @@ pub const Sha1 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntBig(u32, out[4 * j ..][0..4], s); } } diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index d7b2fbe2566d..7c60674d7594 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -120,7 +120,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { var c = [_]u64{0} ** 5; for (s) |*r, i| { - r.* = mem.readIntSliceLittle(u64, d[8 * i .. 8 * i + 8]); + r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]); } comptime var x: usize = 0; @@ -167,8 +167,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { } for (s) |r, i| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u64, d[8 * i .. 8 * i + 8], r); + mem.writeIntLittle(u64, d[8 * i ..][0..8], r); } } diff --git a/lib/std/crypto/x25519.zig b/lib/std/crypto/x25519.zig index 16e3f073f846..e2e2bf90e506 100644 --- a/lib/std/crypto/x25519.zig +++ b/lib/std/crypto/x25519.zig @@ -7,8 +7,8 @@ const builtin = @import("builtin"); const fmt = std.fmt; const Endian = builtin.Endian; -const readIntSliceLittle = std.mem.readIntSliceLittle; -const writeIntSliceLittle = std.mem.writeIntSliceLittle; +const readIntLittle = std.mem.readIntLittle; +const writeIntLittle = std.mem.writeIntLittle; // Based on Supercop's ref10 implementation. pub const X25519 = struct { @@ -255,16 +255,16 @@ const Fe = struct { var t: [10]i64 = undefined; - t[0] = readIntSliceLittle(u32, s[0..4]); - t[1] = @as(u32, readIntSliceLittle(u24, s[4..7])) << 6; - t[2] = @as(u32, readIntSliceLittle(u24, s[7..10])) << 5; - t[3] = @as(u32, readIntSliceLittle(u24, s[10..13])) << 3; - t[4] = @as(u32, readIntSliceLittle(u24, s[13..16])) << 2; - t[5] = readIntSliceLittle(u32, s[16..20]); - t[6] = @as(u32, readIntSliceLittle(u24, s[20..23])) << 7; - t[7] = @as(u32, readIntSliceLittle(u24, s[23..26])) << 5; - t[8] = @as(u32, readIntSliceLittle(u24, s[26..29])) << 4; - t[9] = (@as(u32, readIntSliceLittle(u24, s[29..32])) & 0x7fffff) << 2; + t[0] = readIntLittle(u32, s[0..4]); + t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6; + t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5; + t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3; + t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2; + t[5] = readIntLittle(u32, s[16..20]); + t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7; + t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5; + t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4; + t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2; carry1(h, t[0..]); } @@ -544,15 +544,14 @@ const Fe = struct { ut[i] = @bitCast(u32, @intCast(i32, t[i])); } - // TODO https://github.com/ziglang/zig/issues/863 - writeIntSliceLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26)); - writeIntSliceLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19)); - writeIntSliceLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13)); - writeIntSliceLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6)); - writeIntSliceLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25)); - writeIntSliceLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19)); - writeIntSliceLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12)); - writeIntSliceLittle(u32, s[28..], (ut[8] >> 20) | (ut[9] << 6)); + writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26)); + writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19)); + writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13)); + writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6)); + writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25)); + writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19)); + writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12)); + writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6)); std.mem.secureZero(i64, t[0..]); } diff --git a/lib/std/hash/siphash.zig b/lib/std/hash/siphash.zig index ccef47c4b2f2..ebafdd68554d 100644 --- a/lib/std/hash/siphash.zig +++ b/lib/std/hash/siphash.zig @@ -39,8 +39,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round pub fn init(key: []const u8) Self { assert(key.len >= 16); - const k0 = mem.readIntSliceLittle(u64, key[0..8]); - const k1 = mem.readIntSliceLittle(u64, key[8..16]); + const k0 = mem.readIntLittle(u64, key[0..8]); + const k1 = mem.readIntLittle(u64, key[8..16]); var d = Self{ .v0 = k0 ^ 0x736f6d6570736575, @@ -111,7 +111,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round fn round(self: *Self, b: []const u8) void { assert(b.len == 8); - const m = mem.readIntSliceLittle(u64, b[0..]); + const m = mem.readIntLittle(u64, b[0..8]); self.v3 ^= m; // TODO this is a workaround, should be able to supply the value without a separate variable diff --git a/lib/std/hash/wyhash.zig b/lib/std/hash/wyhash.zig index 8fcbbbce4c47..8b7edd246f87 100644 --- a/lib/std/hash/wyhash.zig +++ b/lib/std/hash/wyhash.zig @@ -11,7 +11,7 @@ const primes = [_]u64{ fn read_bytes(comptime bytes: u8, data: []const u8) u64 { const T = std.meta.IntType(false, 8 * bytes); - return mem.readIntSliceLittle(T, data[0..bytes]); + return mem.readIntLittle(T, data[0..bytes]); } fn read_8bytes_swapped(data: []const u8) u64 { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 951f40ef19f6..2f7a21c6b6d2 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -824,8 +824,7 @@ pub const readIntBig = switch (builtin.endian) { pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T { const n = @divExact(T.bit_count, 8); assert(bytes.len >= n); - // TODO https://github.com/ziglang/zig/issues/863 - return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr)); + return readIntNative(T, bytes[0..n]); } /// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 @@ -863,8 +862,7 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, en pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T { const n = @divExact(T.bit_count, 8); assert(bytes.len >= n); - // TODO https://github.com/ziglang/zig/issues/863 - return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian); + return readInt(T, bytes[0..n], endian); } test "comptime read/write int" { diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 31891f5f0e68..ede46e306593 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -5,7 +5,7 @@ // ``` // var buf: [8]u8 = undefined; // try std.crypto.randomBytes(buf[0..]); -// const seed = mem.readIntSliceLittle(u64, buf[0..8]); +// const seed = mem.readIntLittle(u64, buf[0..8]); // // var r = DefaultPrng.init(seed); // diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 8ed51fa1451c..a971a730e840 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -251,12 +251,12 @@ pub const Utf16LeIterator = struct { pub fn nextCodepoint(it: *Utf16LeIterator) !?u21 { assert(it.i <= it.bytes.len); if (it.i == it.bytes.len) return null; - const c0: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); + const c0: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]); if (c0 & ~@as(u21, 0x03ff) == 0xd800) { // surrogate pair it.i += 2; if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf; - const c1: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); + const c1: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]); if (c1 & ~@as(u21, 0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; it.i += 2; return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); @@ -630,11 +630,11 @@ test "utf8ToUtf16LeWithNull" { } } -/// Converts a UTF-8 string literal into a UTF-16LE string literal. -pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) :0] u16 { +/// Converts a UTF-8 string literal into a UTF-16LE string literal. +pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8):0]u16 { comptime { const len: usize = calcUtf16LeLen(utf8); - var utf16le: [len :0]u16 = [_ :0]u16{0} ** len; + var utf16le: [len:0]u16 = [_:0]u16{0} ** len; const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err); assert(len == utf16le_len); return &utf16le; @@ -660,8 +660,8 @@ fn calcUtf16LeLen(utf8: []const u8) usize { } test "utf8ToUtf16LeStringLiteral" { -{ - const bytes = [_:0]u16{ 0x41 }; + { + const bytes = [_:0]u16{0x41}; const utf16 = utf8ToUtf16LeStringLiteral("A"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); @@ -673,19 +673,19 @@ test "utf8ToUtf16LeStringLiteral" { testing.expect(utf16[2] == 0); } { - const bytes = [_:0]u16{ 0x02FF }; + const bytes = [_:0]u16{0x02FF}; const utf16 = utf8ToUtf16LeStringLiteral("\u{02FF}"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); } { - const bytes = [_:0]u16{ 0x7FF }; + const bytes = [_:0]u16{0x7FF}; const utf16 = utf8ToUtf16LeStringLiteral("\u{7FF}"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); } { - const bytes = [_:0]u16{ 0x801 }; + const bytes = [_:0]u16{0x801}; const utf16 = utf8ToUtf16LeStringLiteral("\u{801}"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); From 6b6f2fcf96b0d8493be45b484226ea8ae9a83a88 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Mar 2020 15:09:52 -0400 Subject: [PATCH 19/20] std.net: remove the hack from earlier in the branch --- lib/std/net.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 44efc4644a2d..b9c1281191d2 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -957,8 +957,10 @@ fn linuxLookupNameFromDns( } } - var hack: usize = 0; // TODO remove this hack - var ap = [2][]u8{ apbuf[0][0..hack], apbuf[1][0..hack] }; + var ap = [2][]u8{ apbuf[0], apbuf[1] }; + ap[0].len = 0; + ap[1].len = 0; + try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc); var i: usize = 0; From 160367e0ddcb36b6957e603d869507b9d7542edc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Mar 2020 17:23:53 -0400 Subject: [PATCH 20/20] fix compile error for reading past end of pointer casted array --- src/ir.cpp | 28 +++++++++++++++++++++++++++- test/compile_errors.zig | 21 ++++++++++----------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 8cd780e46d85..b3b3198ce154 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19971,6 +19971,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; + buf_deinit(&buf); return ErrorNone; } @@ -19990,7 +19991,31 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } - case ConstPtrSpecialSubArray: + case ConstPtrSpecialSubArray: { + ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; + assert(array_val->type->id == ZigTypeIdArray); + if (array_val->data.x_array.special != ConstArraySpecialNone) + zig_panic("TODO"); + if (dst_size > src_size) { + size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", + dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); + return ErrorSemanticAnalyzeFail; + } + size_t elem_size = src_size; + size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); + Buf buf = BUF_INIT; + buf_resize(&buf, elem_count * elem_size); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[i]; + buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); + } + if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; + buf_deinit(&buf); + return ErrorNone; + } case ConstPtrSpecialBaseArray: { ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; assert(array_val->type->id == ZigTypeIdArray); @@ -20014,6 +20039,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source } if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; + buf_deinit(&buf); return ErrorNone; } case ConstPtrSpecialBaseStruct: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index abcdc48dc66f..e3e14621738c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1903,17 +1903,16 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:7:15: error: switch must handle all possibilities", }); - // TODO uncomment before merging branch - //cases.add("reading past end of pointer casted array", - // \\comptime { - // \\ const array: [4]u8 = "aoeu".*; - // \\ const sub_array = array[1..]; - // \\ const int_ptr = @ptrCast(*const u24, sub_array); - // \\ const deref = int_ptr.*; - // \\} - //, &[_][]const u8{ - // "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", - //}); + cases.add("reading past end of pointer casted array", + \\comptime { + \\ const array: [4]u8 = "aoeu".*; + \\ const sub_array = array[1..]; + \\ const int_ptr = @ptrCast(*const u24, sub_array); + \\ const deref = int_ptr.*; + \\} + , &[_][]const u8{ + "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", + }); cases.add("error note for function parameter incompatibility", \\fn do_the_thing(func: fn (arg: i32) void) void {}