diff --git a/doc/langref.html.in b/doc/langref.html.in index dc307255c..bc7bbf0cb 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -587,7 +587,7 @@ const c_string_literal = ; {#code_end#}

- In this example the variable {#syntax#}c_string_literal{#endsyntax#} has type {#syntax#}[*]const char{#endsyntax#} and + In this example the variable {#syntax#}c_string_literal{#endsyntax#} has type {#syntax#}[*]const u8{#endsyntax#} and has a terminating null byte.

{#see_also|@embedFile#} diff --git a/src/ir.cpp b/src/ir.cpp index 33d42cfc5..022909d18 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -155,6 +155,8 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val); static Error ir_read_const_ptr(IrAnalyze *ira, AstNode *source_node, ConstExprValue *out_val, ConstExprValue *ptr_val); +static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, + ZigType *dest_type, IrInstruction *dest_type_src); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -8573,17 +8575,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted return result; } - // *T and [*]T can always cast to *c_void - if (wanted_type->id == ZigTypeIdPointer && - wanted_type->data.pointer.ptr_len == PtrLenSingle && - wanted_type->data.pointer.child_type == g->builtin_types.entry_c_void && - actual_type->id == ZigTypeIdPointer && - (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) - { - return result; - } - // pointer const if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer) { ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, @@ -11156,6 +11147,33 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // cast from *T and [*]T to *c_void and ?*c_void + // but don't do it if the actual type is a double pointer + if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.child_type->id != ZigTypeIdPointer) { + ZigType *dest_ptr_type = nullptr; + if (wanted_type->id == ZigTypeIdPointer && + wanted_type->data.pointer.ptr_len == PtrLenSingle && + wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) + { + dest_ptr_type = wanted_type; + } else if (wanted_type->id == ZigTypeIdOptional && + wanted_type->data.maybe.child_type->id == ZigTypeIdPointer && + wanted_type->data.maybe.child_type->data.pointer.ptr_len == PtrLenSingle && + wanted_type->data.maybe.child_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) + { + dest_ptr_type = wanted_type->data.maybe.child_type; + } + if (dest_ptr_type != nullptr && + (!actual_type->data.pointer.is_const || dest_ptr_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || dest_ptr_type->data.pointer.is_volatile) && + actual_type->data.pointer.bit_offset == dest_ptr_type->data.pointer.bit_offset && + actual_type->data.pointer.unaligned_bit_count == dest_ptr_type->data.pointer.unaligned_bit_count && + get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type)) + { + return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr); + } + } + // cast from T to *T where T is zero bits if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, @@ -20234,9 +20252,91 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 return result; } -static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { +static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, + ZigType *dest_type, IrInstruction *dest_type_src) +{ Error err; + ZigType *src_type = ptr->value.type; + assert(!type_is_invalid(src_type)); + + // We have a check for zero bits later so we use get_src_ptr_type to + // validate src_type and dest_type. + + if (get_src_ptr_type(src_type) == nullptr) { + ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); + return ira->codegen->invalid_instruction; + } + + if (get_src_ptr_type(dest_type) == nullptr) { + ir_add_error(ira, dest_type_src, + buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) { + ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); + return ira->codegen->invalid_instruction; + } + + if (instr_is_comptime(ptr)) { + ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, + dest_type); + copy_const_val(&result->value, val, false); + result->value.type = dest_type; + return result; + } + + uint32_t src_align_bytes; + if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) + return ira->codegen->invalid_instruction; + + uint32_t dest_align_bytes; + if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) + return ira->codegen->invalid_instruction; + + if (dest_align_bytes > src_align_bytes) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + add_error_note(ira->codegen, msg, ptr->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes)); + add_error_note(ira->codegen, msg, dest_type_src->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes)); + return ira->codegen->invalid_instruction; + } + + IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, source_instr->scope, + source_instr->source_node, nullptr, ptr); + casted_ptr->value.type = dest_type; + + if (type_has_bits(dest_type) && !type_has_bits(src_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))); + add_error_note(ira->codegen, msg, ptr->source_node, + buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); + add_error_note(ira->codegen, msg, dest_type_src->source_node, + buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + + // Keep the bigger alignment, it can only help- + // unless the target is zero bits. + IrInstruction *result; + if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) { + result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); + if (type_is_invalid(result->value.type)) + return ira->codegen->invalid_instruction; + } else { + result = casted_ptr; + } + return result; +} + +static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->other; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -20247,78 +20347,10 @@ static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtr if (type_is_invalid(src_type)) return ira->codegen->builtin_types.entry_invalid; - // We have a check for zero bits later so we use get_src_ptr_type to - // validate src_type and dest_type. - - if (get_src_ptr_type(src_type) == nullptr) { - ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, dest_type_value, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) { - ir_add_error(ira, &instruction->base, buf_sprintf("cast discards const qualifier")); - return ira->codegen->builtin_types.entry_invalid; - } - - if (instr_is_comptime(ptr)) { - ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk); - if (!val) - return ira->codegen->builtin_types.entry_invalid; - - ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - copy_const_val(out_val, val, false); - out_val->type = dest_type; - return dest_type; - } - - uint32_t src_align_bytes; - if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) + IrInstruction *result = ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value); + if (type_is_invalid(result->value.type)) return ira->codegen->builtin_types.entry_invalid; - uint32_t dest_align_bytes; - if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) - return ira->codegen->builtin_types.entry_invalid; - - if (dest_align_bytes > src_align_bytes) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cast increases pointer alignment")); - add_error_note(ira->codegen, msg, ptr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes)); - add_error_note(ira->codegen, msg, dest_type_value->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes)); - return ira->codegen->builtin_types.entry_invalid; - } - - IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, nullptr, ptr); - casted_ptr->value.type = dest_type; - - if (type_has_bits(dest_type) && !type_has_bits(src_type)) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base, - buf_sprintf("'%s' and '%s' do not have the same in-memory representation", - buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); - add_error_note(ira->codegen, msg, ptr->source_node, - buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); - add_error_note(ira->codegen, msg, dest_type_value->source_node, - buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - // Keep the bigger alignment, it can only help- - // unless the target is zero bits. - IrInstruction *result; - if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) { - result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); - if (type_is_invalid(result->value.type)) - return ira->codegen->builtin_types.entry_invalid; - } else { - result = casted_ptr; - } ir_link_new_instruction(result, &instruction->base); return result->value.type; } diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 5715e46a6..c1e6e891c 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -110,13 +110,61 @@ pub const Server = struct { } }; +pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 { + const sockfd = try std.os.posixSocket( + posix.AF_UNIX, + posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, + 0, + ); + errdefer std.os.close(sockfd); + + var sock_addr = posix.sockaddr{ + .un = posix.sockaddr_un{ + .family = posix.AF_UNIX, + .path = undefined, + }, + }; + + if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong; + mem.copy(u8, sock_addr.un.path[0..], path); + const size = @intCast(u32, @sizeOf(posix.sa_family_t) + path.len); + try std.os.posixConnectAsync(sockfd, &sock_addr, size); + try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET); + try std.os.posixGetSockOptConnectError(sockfd); + + return sockfd; +} + +pub async fn socketRead(loop: *std.event.Loop, fd: i32, buffer: []u8) !void { + while (true) { + return std.os.posixRead(fd, buffer) catch |err| switch (err) { + error.WouldBlock => { + try await try async loop.linuxWaitFd(fd, std.os.posix.EPOLLET | std.os.posix.EPOLLIN); + continue; + }, + else => return err, + }; + } +} +pub async fn socketWrite(loop: *std.event.Loop, fd: i32, buffer: []const u8) !void { + while (true) { + return std.os.posixWrite(fd, buffer) catch |err| switch (err) { + error.WouldBlock => { + try await try async loop.linuxWaitFd(fd, std.os.posix.EPOLLET | std.os.posix.EPOLLOUT); + continue; + }, + else => return err, + }; + } +} + pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File { var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp); errdefer std.os.close(sockfd); - try std.os.posixConnectAsync(sockfd, &address.os_addr); + try std.os.posixConnectAsync(sockfd, &address.os_addr, @sizeOf(posix.sockaddr_in)); try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET); try std.os.posixGetSockOptConnectError(sockfd); diff --git a/std/os/index.zig b/std/os/index.zig index d86ab119d..1ce9bd727 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2677,9 +2677,9 @@ pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectEr /// Same as posixConnect except it is for blocking socket file descriptors. /// It expects to receive EINPROGRESS. -pub fn posixConnectAsync(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { +pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { while (true) { - const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); + const rc = posix.connect(sockfd, sockaddr, len); const err = posix.getErrno(rc); switch (err) { 0, posix.EINPROGRESS => return, diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig index c369921e1..6323c89dd 100644 --- a/std/os/linux/index.zig +++ b/std/os/linux/index.zig @@ -1094,6 +1094,7 @@ pub const in_port_t = u16; pub const sa_family_t = u16; pub const socklen_t = u32; +/// This intentionally only has ip4 and ip6 pub const sockaddr = extern union { in: sockaddr_in, in6: sockaddr_in6, @@ -1114,6 +1115,11 @@ pub const sockaddr_in6 = extern struct { scope_id: u32, }; +pub const sockaddr_un = extern struct { + family: sa_family_t, + path: [108]u8, +}; + pub const iovec = extern struct { iov_base: [*]u8, iov_len: usize, @@ -1148,8 +1154,8 @@ pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { return syscall3(SYS_sendmsg, @intCast(usize, fd), @ptrToInt(msg), flags); } -pub fn connect(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_connect, @intCast(usize, fd), @ptrToInt(addr), @intCast(usize, len)); +pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { + return syscall3(SYS_connect, @intCast(usize, fd), @ptrToInt(addr), len); } pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 880ebfb38..92148728a 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -537,3 +537,13 @@ pub const PFN_void = extern fn (*c_void) void; fn foobar(func: PFN_void) void { std.debug.assert(@ptrToInt(func) == @maxValue(usize)); } + +test "implicit ptr to *c_void" { + var a: u32 = 1; + var ptr: *c_void = &a; + var b: *u32 = @ptrCast(*u32, ptr); + assert(b.* == 1); + var ptr2: ?*c_void = &a; + var c: *u32 = @ptrCast(*u32, ptr2.?); + assert(c.* == 1); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d5d0cb38f..19bb630fb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "don't implicit cast double pointer to *c_void", + \\export fn entry() void { + \\ var a: u32 = 1; + \\ var ptr: *c_void = &a; + \\ var b: *u32 = @ptrCast(*u32, ptr); + \\ var ptr2: *c_void = &b; + \\} + , + ".tmp_source.zig:5:26: error: expected type '*c_void', found '**u32'", + ); + cases.add( "runtime index into comptime type slice", \\const Struct = struct {