Commit 9485043b3c

Andrew Kelley <superjoe30@gmail.com>
2018-09-26 17:06:09
fix implicit casting to *c_void
closes #1588 also some small std lib changes regarding posix sockets and one doc typo fix
1 parent 7b20464
Changed files (7)
doc/langref.html.in
@@ -587,7 +587,7 @@ const c_string_literal =
 ;
       {#code_end#}
       <p>
-      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.
       </p>
       {#see_also|@embedFile#}
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,79 +20252,75 @@ 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;
 
-    IrInstruction *dest_type_value = instruction->dest_type->other;
-    ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
-    if (type_is_invalid(dest_type))
-        return ira->codegen->builtin_types.entry_invalid;
-
-    IrInstruction *ptr = instruction->ptr->other;
     ZigType *src_type = ptr->value.type;
-    if (type_is_invalid(src_type))
-        return ira->codegen->builtin_types.entry_invalid;
+    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->builtin_types.entry_invalid;
+        return ira->codegen->invalid_instruction;
     }
 
     if (get_src_ptr_type(dest_type) == nullptr) {
-        ir_add_error(ira, dest_type_value,
+        ir_add_error(ira, dest_type_src,
                 buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
-        return ira->codegen->builtin_types.entry_invalid;
+        return ira->codegen->invalid_instruction;
     }
 
     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;
+        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->builtin_types.entry_invalid;
+            return ira->codegen->invalid_instruction;
 
-        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;
+        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->builtin_types.entry_invalid;
+        return ira->codegen->invalid_instruction;
 
     uint32_t dest_align_bytes;
     if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes)))
-        return ira->codegen->builtin_types.entry_invalid;
+        return ira->codegen->invalid_instruction;
 
     if (dest_align_bytes > src_align_bytes) {
-        ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cast increases pointer alignment"));
+        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_value->source_node,
+        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->builtin_types.entry_invalid;
+        return ira->codegen->invalid_instruction;
     }
 
-    IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, instruction->base.scope,
-            instruction->base.source_node, nullptr, ptr);
+    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, &instruction->base,
+        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_value->source_node,
+        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->builtin_types.entry_invalid;
+        return ira->codegen->invalid_instruction;
     }
 
     // Keep the bigger alignment, it can only help-
@@ -20315,10 +20329,28 @@ static ZigType *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtr
     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;
+            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))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    IrInstruction *ptr = instruction->ptr->other;
+    ZigType *src_type = ptr->value.type;
+    if (type_is_invalid(src_type))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    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;
+
     ir_link_new_instruction(result, &instruction->base);
     return result->value.type;
 }
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);
 
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 {
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,
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);
+}
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 {