commit b92fac329e73280ea5d49af675fea84e777b7f0a (tree)
parent ecc54640243ba84ffa3656b73aa7dd6b53474462
Author: Andrew Kelley <superjoe30@gmail.com>
Date: Mon, 27 Aug 2018 18:31:41 -0400
Merge branch 'raulgrell-CastToCVoid'
Diffstat:
3 files changed, 103 insertions(+), 14 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
@@ -7058,6 +7058,61 @@ const c = @cImport({
{#code_end#}
{#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#}
{#header_close#}
+ {#header_open|Exporting a C Library#}
+ <p>
+ One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages
+ to call into. The <code>export</code> keyword in front of functions, variables, and types causes them to
+ be part of the library API:
+ </p>
+ <p class="file">mathtest.zig</p>
+ {#code_begin|syntax#}
+export fn add(a: i32, b: i32) i32 {
+ return a + b;
+}
+ {#code_end#}
+ <p>To make a shared library:</p>
+ <pre><code class="shell">$ zig build-lib mathtest.zig
+</code></pre>
+ <p>To make a static library:</p>
+ <pre><code class="shell">$ zig build-lib mathtest.zig --static
+</code></pre>
+ <p>Here is an example with the {#link|Zig Build System#}:</p>
+ <p class="file">test.c</p>
+ <pre><code class="cpp">// This header is generated by zig from mathtest.zig
+#include "mathtest.h"
+#include <assert.h>
+
+int main(int argc, char **argv) {
+ assert(add(42, 1337) == 1379);
+ return 0;
+}</code></pre>
+ <p class="file">build.zig</p>
+ {#code_begin|syntax#}
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+ const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
+
+ const exe = b.addCExecutable("test");
+ exe.addCompileFlags([][]const u8{"-std=c99"});
+ exe.addSourceFile("test.c");
+ exe.linkLibrary(lib);
+
+ b.default_step.dependOn(&exe.step);
+
+ const run_cmd = b.addCommand(".", b.env_map, [][]const u8{exe.getOutputPath()});
+ run_cmd.step.dependOn(&exe.step);
+
+ const test_step = b.step("test", "Test the program");
+ test_step.dependOn(&run_cmd.step);
+}
+ {#code_end#}
+ <p class="file">terminal</p>
+ <pre><code class="shell">$ zig build
+$ ./test
+$ echo $?
+0</code></pre>
+ {#header_close#}
{#header_open|Mixing Object Files#}
<p>
You can mix Zig object files with any other object files that respect the C ABI. Example:
diff --git a/src/ir.cpp b/src/ir.cpp
@@ -60,7 +60,7 @@ enum ConstCastResultId {
ConstCastResultIdType,
ConstCastResultIdUnresolvedInferredErrSet,
ConstCastResultIdAsyncAllocatorType,
- ConstCastResultIdNullWrapPtr,
+ ConstCastResultIdNullWrapPtr
};
struct ConstCastOnly;
@@ -8471,9 +8471,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
if (wanted_type == actual_type)
return result;
- // * and [*] can do a const-cast-only to ?* and ?[*], respectively
- // but not if there is a mutable parent pointer
- // and not if the pointer is zero bits
+ // *T and [*]T may const-cast-only to ?*U and ?[*]U, respectively
+ // but not if we want a mutable pointer
+ // and not if the actual pointer has zero bits
if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional &&
wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
actual_type->id == TypeTableEntryIdPointer && type_has_bits(actual_type))
@@ -8488,6 +8488,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
return result;
}
+ // *T and [*]T can always cast to *c_void
+ if (wanted_type->id == TypeTableEntryIdPointer &&
+ wanted_type->data.pointer.ptr_len == PtrLenSingle &&
+ wanted_type->data.pointer.child_type == g->builtin_types.entry_c_void &&
+ actual_type->id == TypeTableEntryIdPointer &&
+ (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
+ (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile))
+ {
+ assert(actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment);
+ return result;
+ }
+
// pointer const
if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) {
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
diff --git a/test/cases/cast.zig b/test/cases/cast.zig
@@ -487,12 +487,35 @@ fn MakeType(comptime T: type) type {
}
test "implicit cast from *[N]T to ?[*]T" {
- var x: ?[*]u16 = null;
- var y: [4]u16 = [4]u16 {0, 1, 2, 3};
-
- x = &y;
- assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
- x.?[0] = 8;
- y[3] = 6;
- assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
-}
-\ No newline at end of file
+ var x: ?[*]u16 = null;
+ var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
+
+ x = &y;
+ assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
+ x.?[0] = 8;
+ y[3] = 6;
+ assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
+}
+
+test "implicit cast from *T to ?*c_void" {
+ var a: u8 = 1;
+ incrementVoidPtrValue(&a);
+ std.debug.assert(a == 2);
+}
+
+fn incrementVoidPtrValue(value: ?*c_void) void {
+ @ptrCast(*u8, value.?).* += 1;
+}
+
+test "implicit cast from [*]T to ?*c_void" {
+ var a = []u8{ 3, 2, 1 };
+ incrementVoidPtrArray(a[0..].ptr, 3);
+ assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 }));
+}
+
+fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
+ var n: usize = 0;
+ while (n < len) : (n += 1) {
+ @ptrCast([*]u8, array.?)[n] += 1;
+ }
+}