diff --git a/src/codegen.cpp b/src/codegen.cpp index 00103be259..15648cbdec 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4201,6 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c continue; } ConstExprValue *field_val = &const_val->data.x_struct.fields[i]; + assert(field_val->type != nullptr); LLVMValueRef val = gen_const_val(g, field_val, ""); fields[type_struct_field->gen_index] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val); diff --git a/src/ir.cpp b/src/ir.cpp index bb9a153894..7eac9e4d23 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8183,7 +8183,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi } if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); if (!val) return ira->codegen->invalid_instruction; bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true; @@ -14931,6 +14931,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio ConstExprValue *parent_ptr; size_t abs_offset; size_t rel_end; + bool ptr_is_undef = false; if (array_type->id == TypeTableEntryIdArray) { array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); abs_offset = 0; @@ -14938,7 +14939,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio parent_ptr = nullptr; } else if (array_type->id == TypeTableEntryIdPointer) { parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); - switch (parent_ptr->data.x_ptr.special) { + if (parent_ptr->special == ConstValSpecialUndef) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; + ptr_is_undef = true; + } else switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); @@ -14992,7 +14998,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio } uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint); - if (start_scalar > rel_end) { + if (!ptr_is_undef && start_scalar > rel_end) { ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); return ira->codegen->builtin_types.entry_invalid; } @@ -15003,12 +15009,18 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio } else { end_scalar = rel_end; } - if (end_scalar > rel_end) { - ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); - return ira->codegen->builtin_types.entry_invalid; + if (!ptr_is_undef) { + if (end_scalar > rel_end) { + ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); + return ira->codegen->builtin_types.entry_invalid; + } + if (start_scalar > end_scalar) { + ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end")); + return ira->codegen->builtin_types.entry_invalid; + } } - if (start_scalar > end_scalar) { - ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end")); + if (ptr_is_undef && start_scalar != end_scalar) { + ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer")); return ira->codegen->builtin_types.entry_invalid; } @@ -15024,25 +15036,27 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio if (array_type->id == TypeTableEntryIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; } - } 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)); - break; - case ConstPtrSpecialBaseArray: - zig_unreachable(); - case ConstPtrSpecialBaseStruct: - zig_panic("TODO"); - case ConstPtrSpecialHardCodedAddr: - 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)); - } + } 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)); + 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)); + break; + case ConstPtrSpecialBaseArray: + zig_unreachable(); + case ConstPtrSpecialBaseStruct: + zig_panic("TODO"); + case ConstPtrSpecialHardCodedAddr: + 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)); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; diff --git a/std/mem.zig b/std/mem.zig index f40fc9bbea..07521bfcb8 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -42,6 +42,9 @@ pub const Allocator = struct { fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { + if (n == 0) { + return (&align(alignment) T)(undefined)[0..0]; + } const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); assert(byte_slice.len == byte_count); @@ -62,6 +65,10 @@ pub const Allocator = struct { if (old_mem.len == 0) { return self.alloc(T, n); } + if (n == 0) { + self.free(old_mem); + return (&align(alignment) T)(undefined)[0..0]; + } const old_byte_slice = ([]u8)(old_mem); const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; diff --git a/test/cases/eval.zig b/test/cases/eval.zig index e8dd828b4d..fc67d8f135 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -388,3 +388,10 @@ test "string literal used as comptime slice is memoized" { comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); } + +test "comptime slice of undefined pointer of length 0" { + const slice1 = (&i32)(undefined)[0..0]; + assert(slice1.len == 0); + const slice2 = (&i32)(undefined)[100..100]; + assert(slice2.len == 0); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 90b3ff023a..940125711b 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,13 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("comptime slice of undefined pointer non-zero len", + \\export fn entry() void { + \\ const slice = (&i32)(undefined)[0..1]; + \\} + , + ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer"); + cases.add("type checking function pointers", \\fn a(b: fn (&const u8) void) void { \\ b('a');