fix implicit casting to *c_void

closes #1588

also some small std lib changes regarding posix sockets
and one doc typo fix
This commit is contained in:
Andrew Kelley 2018-09-26 11:06:09 -04:00
parent 7b204649e3
commit 9485043b3c
No known key found for this signature in database
GPG Key ID: 4E7CD66038A4D47C
7 changed files with 196 additions and 88 deletions

View File

@ -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#}

View File

@ -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)))
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);
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;
} else {
result = casted_ptr;
}
ir_link_new_instruction(result, &instruction->base);
return result->value.type;
}

View File

@ -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);

View File

@ -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,

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 {