Commit d6e0d82c32

Andrew Kelley <andrew@ziglang.org>
2019-02-15 05:09:12
translate-c: back to *c_void for opaque types
See #1059
1 parent d5bbd74
src/analyze.cpp
@@ -437,7 +437,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
     // move this to a parameter
     bool allow_zero = (ptr_len == PtrLenC);
     assert(!type_is_invalid(child_type));
-    assert(ptr_len != PtrLenUnknown || child_type->id != ZigTypeIdOpaque);
+    assert(ptr_len == PtrLenSingle || child_type->id != ZigTypeIdOpaque);
 
     if (byte_alignment != 0) {
         uint32_t abi_alignment = get_abi_alignment(g, child_type);
src/ir.cpp
@@ -21205,10 +21205,15 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct
     } else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) {
         ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
         return ira->codegen->invalid_instruction;
-    } else if (instruction->ptr_len == PtrLenC && !type_allowed_in_extern(ira->codegen, child_type)) {
-        ir_add_error(ira, &instruction->base,
-            buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
-        return ira->codegen->invalid_instruction;
+    } else if (instruction->ptr_len == PtrLenC) {
+        if (!type_allowed_in_extern(ira->codegen, child_type)) {
+            ir_add_error(ira, &instruction->base,
+                buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
+            return ira->codegen->invalid_instruction;
+        } else if (child_type->id == ZigTypeIdOpaque) {
+            ir_add_error(ira, &instruction->base, buf_sprintf("C pointers cannot point opaque types"));
+            return ira->codegen->invalid_instruction;
+        }
     }
 
     uint32_t align_bytes;
src/translate_c.cpp
@@ -763,6 +763,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
     }
 }
 
+static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) {
+    switch (ty->getTypeClass()) {
+        case Type::Builtin: {
+            const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
+            return builtin_ty->getKind() == BuiltinType::Void;
+        }
+        case Type::Record: {
+            const RecordType *record_ty = static_cast<const RecordType*>(ty);
+            return record_ty->getDecl()->getDefinition() == nullptr;
+        }
+        case Type::Elaborated: {
+            const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
+            return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc);
+        }
+        case Type::Typedef: {
+            const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
+            const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
+            return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc);
+        }
+        default:
+            return false;
+    }
+}
+
 static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
     switch (ty->getTypeClass()) {
         case Type::Builtin:
@@ -912,8 +936,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
                     return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
                 }
 
-                return trans_create_node_ptr_type(c, child_qt.isConstQualified(),
-                        child_qt.isVolatileQualified(), child_node, PtrLenC);
+                if (type_is_opaque(c, child_qt.getTypePtr(), source_loc)) {
+                    AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
+                            child_qt.isVolatileQualified(), child_node, PtrLenSingle);
+                    return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
+                } else {
+                    return trans_create_node_ptr_type(c, child_qt.isConstQualified(),
+                            child_qt.isVolatileQualified(), child_node, PtrLenC);
+                }
             }
         case Type::Typedef:
             {
test/compile_errors.zig
@@ -1,6 +1,16 @@
 const tests = @import("tests.zig");
 
 pub fn addCases(cases: *tests.CompileErrorContext) void {
+    cases.addTest(
+        "C pointer to c_void",
+        \\export fn a() void {
+        \\    var x: *c_void = undefined;
+        \\    var y: [*c]c_void = x;
+        \\}
+    ,
+        ".tmp_source.zig:3:12: error: C pointers cannot point opaque types",
+    );
+
     cases.addTest(
         "directly embedding opaque type in struct and union",
         \\const O = @OpaqueType();
test/translate_c.zig
@@ -202,7 +202,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
     cases.add("restrict -> noalias",
         \\void foo(void *restrict bar, void *restrict);
     ,
-        \\pub extern fn foo(noalias bar: [*c]c_void, noalias arg1: [*c]c_void) void;
+        \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
     );
 
     cases.add("simple struct",
@@ -275,7 +275,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
     ,
         \\pub const struct_Foo = @OpaqueType();
     ,
-        \\pub extern fn some_func(foo: [*c]struct_Foo, x: c_int) [*c]struct_Foo;
+        \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
     ,
         \\pub const Foo = struct_Foo;
     );
@@ -336,7 +336,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
     ,
         \\pub const Foo = c_void;
     ,
-        \\pub extern fn fun(a: [*c]Foo) Foo;
+        \\pub extern fn fun(a: ?*Foo) Foo;
     );
 
     cases.add("generate inline func for #define global extern fn",
@@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    return 6;
         \\}
     ,
-        \\pub export fn and_or_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
+        \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
         \\    if ((a != 0) and (b != 0)) return 0;
         \\    if ((b != 0) and (c != 0)) return 1;
         \\    if ((a != 0) and (c != 0)) return 2;
@@ -756,8 +756,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    return x;
         \\}
     ,
-        \\pub export fn foo(x: [*c]c_ushort) [*c]c_void {
-        \\    return @ptrCast([*c]c_void, x);
+        \\pub export fn foo(x: [*c]c_ushort) ?*c_void {
+        \\    return @ptrCast(?*c_void, x);
         \\}
     );
 
@@ -1276,7 +1276,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    return !c;
         \\}
     ,
-        \\pub fn foo(a: c_int, b: f32, c: [*c]c_void) c_int {
+        \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
         \\    return !(a == 0);
         \\    return !(a != 0);
         \\    return !(b != 0);
@@ -1334,7 +1334,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    B,
         \\    C,
         \\};
-        \\pub fn if_none_bool(a: c_int, b: f32, c: [*c]c_void, d: enum_SomeEnum) c_int {
+        \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
         \\    if (a != 0) return 0;
         \\    if (b != 0) return 1;
         \\    if (c != 0) return 2;
@@ -1351,7 +1351,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    return 3;
         \\}
     ,
-        \\pub fn while_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
+        \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
         \\    while (a != 0) return 0;
         \\    while (b != 0) return 1;
         \\    while (c != 0) return 2;
@@ -1367,7 +1367,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    return 3;
         \\}
     ,
-        \\pub fn for_none_bool(a: c_int, b: f32, c: [*c]c_void) c_int {
+        \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
         \\    while (a != 0) return 0;
         \\    while (b != 0) return 1;
         \\    while (c != 0) return 2;