diff --git a/doc/langref.html.in b/doc/langref.html.in index 465f3c56a7..495b19580c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7057,6 +7057,61 @@ const c = @cImport({ }); {#code_end#} {#see_also|@cImport|@cInclude|@cDefine|@cUndef|@import#} + {#header_close#} + {#header_open|Exporting a C Library#} +
+ One of the primary use cases for Zig is exporting a library with the C ABI for other programming languages
+ to call into. The export keyword in front of functions, variables, and types causes them to
+ be part of the library API:
+
mathtest.zig
+ {#code_begin|syntax#} +export fn add(a: i32, b: i32) i32 { + return a + b; +} + {#code_end#} +To make a shared library:
+$ zig build-lib mathtest.zig
+
+ To make a static library:
+$ zig build-lib mathtest.zig --static
+
+ Here is an example with the {#link|Zig Build System#}:
+test.c
+// 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;
+}
+ build.zig
+ {#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#} +terminal
+$ zig build
+$ ./test
+$ echo $?
+0
{#header_close#}
{#header_open|Mixing Object Files#}
diff --git a/src/ir.cpp b/src/ir.cpp index 710456179c..3117410cd2 100644 --- 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 index df37bd1dd9..cc5e4b4394 100644 --- 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}; + 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 + 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; + } +}