Commit 712274997e

Andrew Kelley <andrew@ziglang.org>
2019-04-22 01:37:39
translate-c: unify API for self-hosted and C++ translate-c
See #1964
1 parent 9760804
src/codegen.cpp
@@ -8298,31 +8298,42 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file, bool use_us
 
     clang_argv.append(nullptr); // to make the [start...end] argument work
 
+    const char *resources_path = buf_ptr(g->zig_c_headers_dir);
+    Stage2ErrorMsg *errors_ptr;
+    size_t errors_len;
+    Stage2Ast *ast;
+    AstNode *root_node;
+
     if (use_userland_implementation) {
-        Stage2Ast *ast;
-        if ((err = stage2_translate_c(&ast, &clang_argv.at(0), &clang_argv.last(), trans_mode))) {
-            zig_panic("TODO");
-        }
-        stage2_render_ast(ast, out_file);
+        err = stage2_translate_c(&ast, &errors_ptr, &errors_len,
+                        &clang_argv.at(0), &clang_argv.last(), trans_mode, resources_path);
     } else {
-        ZigList<ErrorMsg *> errors = {0};
-        AstNode *root_node;
-
-        err = parse_h_file(g, &root_node, &clang_argv.at(0), &clang_argv.last(), trans_mode, &errors);
+        err = parse_h_file(g, &root_node, &errors_ptr, &errors_len, &clang_argv.at(0), &clang_argv.last(),
+                trans_mode, resources_path);
+    }
 
-        if (err == ErrorCCompileErrors && errors.length > 0) {
-            for (size_t i = 0; i < errors.length; i += 1) {
-                ErrorMsg *err_msg = errors.at(i);
-                print_err_msg(err_msg, g->err_color);
-            }
-            exit(1);
+    if (err == ErrorCCompileErrors && errors_len > 0) {
+        for (size_t i = 0; i < errors_len; i += 1) {
+            Stage2ErrorMsg *clang_err = &errors_ptr[i];
+            ErrorMsg *err_msg = err_msg_create_with_offset(
+                    clang_err->filename_ptr ?
+                        buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
+                    clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
+                    buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
+            print_err_msg(err_msg, g->err_color);
         }
+        exit(1);
+    }
 
-        if (err) {
-            fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
-            exit(1);
-        }
+    if (err) {
+        fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
+        exit(1);
+    }
 
+
+    if (use_userland_implementation) {
+        stage2_render_ast(ast, out_file);
+    } else {
         ast_render(out_file, root_node, 4);
     }
 }
src/ir.cpp
@@ -19103,25 +19103,32 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct
 
         clang_argv.append(nullptr); // to make the [start...end] argument work
 
-        ZigList<ErrorMsg *> errors = {0};
         AstNode *root_node;
+        Stage2ErrorMsg *errors_ptr;
+        size_t errors_len;
 
-        if ((err = parse_h_file(ira->codegen, &root_node, &clang_argv.at(0), &clang_argv.last(),
-                        Stage2TranslateModeImport, &errors)))
+        const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir);
+
+        if ((err = parse_h_file(ira->codegen, &root_node, &errors_ptr, &errors_len,
+            &clang_argv.at(0), &clang_argv.last(), Stage2TranslateModeImport, resources_path)))
         {
             if (err != ErrorCCompileErrors) {
                 ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
                 return ira->codegen->invalid_instruction;
             }
-            assert(errors.length > 0);
 
             ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
             if (ira->codegen->libc_link_lib == nullptr) {
                 add_error_note(ira->codegen, parent_err_msg, node,
                     buf_sprintf("libc headers not available; compilation does not link against libc"));
             }
-            for (size_t i = 0; i < errors.length; i += 1) {
-                ErrorMsg *err_msg = errors.at(i);
+            for (size_t i = 0; i < errors_len; i += 1) {
+                Stage2ErrorMsg *clang_err = &errors_ptr[i];
+                ErrorMsg *err_msg = err_msg_create_with_offset(
+                    clang_err->filename_ptr ?
+                        buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
+                    clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
+                    buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
                 err_msg_add_note(parent_err_msg, err_msg);
             }
 
src/translate_c.cpp
@@ -76,10 +76,9 @@ struct TransScopeWhile {
 };
 
 struct Context {
-    ZigList<ErrorMsg *> *errors;
+    AstNode *root;
     VisibMod visib_mod;
     bool want_export;
-    AstNode *root;
     HashMap<const void *, AstNode *, ptr_hash, ptr_eq> decl_table;
     HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> macro_table;
     HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> global_table;
@@ -5018,13 +5017,14 @@ static void process_preprocessor_entities(Context *c, ZigClangASTUnit *zunit) {
     }
 }
 
-Error parse_h_file(CodeGen *codegen, AstNode **out_root_node, const char **args_begin, const char **args_end,
-        Stage2TranslateMode mode, ZigList<ErrorMsg *> *errors)
+Error parse_h_file(CodeGen *codegen, AstNode **out_root_node,
+        Stage2ErrorMsg **errors_ptr, size_t *errors_len,
+        const char **args_begin, const char **args_end,
+        Stage2TranslateMode mode, const char *resources_path)
 {
     Context context = {0};
     Context *c = &context;
     c->warnings_on = codegen->verbose_cimport;
-    c->errors = errors;
     if (mode == Stage2TranslateModeImport) {
         c->visib_mod = VisibModPub;
         c->want_export = false;
@@ -5039,78 +5039,10 @@ Error parse_h_file(CodeGen *codegen, AstNode **out_root_node, const char **args_
     c->codegen = codegen;
     c->global_scope = trans_scope_root_create(c);
 
-
-    clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions));
-
-    std::shared_ptr<clang::PCHContainerOperations> pch_container_ops = std::make_shared<clang::PCHContainerOperations>();
-
-    bool only_local_decls = true;
-    bool capture_diagnostics = true;
-    bool user_files_are_volatile = true;
-    bool allow_pch_with_compiler_errors = false;
-    bool single_file_parse = false;
-    bool for_serialization = false;
-    const char *resources_path = buf_ptr(codegen->zig_c_headers_dir);
-    std::unique_ptr<clang::ASTUnit> err_unit;
-    ZigClangASTUnit *ast_unit = reinterpret_cast<ZigClangASTUnit *>(clang::ASTUnit::LoadFromCommandLine(
-            args_begin, args_end,
-            pch_container_ops, diags, resources_path,
-            only_local_decls, capture_diagnostics, clang::None, true, 0, clang::TU_Complete,
-            false, false, allow_pch_with_compiler_errors, clang::SkipFunctionBodiesScope::None,
-            single_file_parse, user_files_are_volatile, for_serialization, clang::None, &err_unit,
-            nullptr));
-
-    // Early failures in LoadFromCommandLine may return with ErrUnit unset.
-    if (!ast_unit && !err_unit) {
-        return ErrorFileSystem;
-    }
-
-    if (diags->getClient()->getNumErrors() > 0) {
-        if (ast_unit) {
-            err_unit = std::unique_ptr<clang::ASTUnit>(reinterpret_cast<clang::ASTUnit *>(ast_unit));
-        }
-
-        for (clang::ASTUnit::stored_diag_iterator it = err_unit->stored_diag_begin(),
-                it_end = err_unit->stored_diag_end();
-                it != it_end; ++it)
-        {
-            switch (it->getLevel()) {
-                case clang::DiagnosticsEngine::Ignored:
-                case clang::DiagnosticsEngine::Note:
-                case clang::DiagnosticsEngine::Remark:
-                case clang::DiagnosticsEngine::Warning:
-                    continue;
-                case clang::DiagnosticsEngine::Error:
-                case clang::DiagnosticsEngine::Fatal:
-                    break;
-            }
-            llvm::StringRef msg_str_ref = it->getMessage();
-            Buf *msg = string_ref_to_buf(msg_str_ref);
-            clang::FullSourceLoc fsl = it->getLocation();
-            if (fsl.hasManager()) {
-                clang::FileID file_id = fsl.getFileID();
-                clang::StringRef filename = fsl.getManager().getFilename(fsl);
-                unsigned line = fsl.getSpellingLineNumber() - 1;
-                unsigned column = fsl.getSpellingColumnNumber() - 1;
-                unsigned offset = fsl.getManager().getFileOffset(fsl);
-                const char *source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin();
-                Buf *path;
-                if (filename.empty()) {
-                    path = buf_alloc();
-                } else {
-                    path = string_ref_to_buf(filename);
-                }
-
-                ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg);
-
-                c->errors->append(err_msg);
-            } else {
-                // NOTE the only known way this gets triggered right now is if you have a lot of errors
-                // clang emits "too many errors emitted, stopping now"
-                fprintf(stderr, "unexpected error from clang: %s\n", buf_ptr(msg));
-            }
-        }
-
+    ZigClangASTUnit *ast_unit = ZigClangLoadFromCommandLine(args_begin, args_end, errors_ptr, errors_len,
+            resources_path);
+    if (ast_unit == nullptr) {
+        if (*errors_len == 0) return ErrorNoMem;
         return ErrorCCompileErrors;
     }
 
src/translate_c.hpp
@@ -11,7 +11,9 @@
 
 #include "all_types.hpp"
 
-Error parse_h_file(CodeGen *codegen, AstNode **out_root_node, const char **args_begin, const char **args_end,
-        Stage2TranslateMode mode, ZigList<ErrorMsg *> *errors);
+Error parse_h_file(CodeGen *codegen, AstNode **out_root_node,
+        Stage2ErrorMsg **errors_ptr, size_t *errors_len,
+        const char **args_begin, const char **args_end,
+        Stage2TranslateMode mode, const char *resources_path);
 
 #endif
src/userland.cpp
@@ -8,12 +8,19 @@
 #include <string.h>
 
 Error stage2_translate_c(struct Stage2Ast **out_ast,
-        const char **args_begin, const char **args_end, enum Stage2TranslateMode mode)
+        struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len,
+        const char **args_begin, const char **args_end, enum Stage2TranslateMode mode,
+        const char *resources_path)
 {
     const char *msg = "stage0 called stage2_translate_c";
     stage2_panic(msg, strlen(msg));
 }
 
+void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) {
+    const char *msg = "stage0 called stage2_free_clang_errors";
+    stage2_panic(msg, strlen(msg));
+}
+
 void stage2_zen(const char **ptr, size_t *len) {
     const char *msg = "stage0 called stage2_zen";
     stage2_panic(msg, strlen(msg));
src/userland.h
@@ -12,20 +12,21 @@
 #include <stdio.h>
 
 #ifdef __cplusplus
-#define ZIG_USERLAND_EXTERN_C extern "C"
+#define ZIG_EXTERN_C extern "C"
 #else
-#define ZIG_USERLAND_EXTERN_C
+#define ZIG_EXTERN_C
 #endif
 
 #if defined(_MSC_VER)
-#define ZIG_USERLAND_ATTRIBUTE_NORETURN __declspec(noreturn)
+#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn)
 #else
-#define ZIG_USERLAND_ATTRIBUTE_NORETURN __attribute__((noreturn))
+#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn))
 #endif
 
-// The types and declarations in this file must match both those in userland.cpp and
-// src-self-hosted/stage1.zig.
+// ABI warning: the types and declarations in this file must match both those in
+// userland.cpp and src-self-hosted/stage1.zig.
 
+// ABI warning
 enum Error {
     ErrorNone,
     ErrorNoMem,
@@ -74,20 +75,43 @@ enum Error {
     ErrorNoSpaceLeft,
 };
 
+// ABI warning
 enum Stage2TranslateMode {
     Stage2TranslateModeImport,
     Stage2TranslateModeTranslate,
 };
 
+// ABI warning
+struct Stage2ErrorMsg {
+    const char *filename_ptr; // can be null
+    size_t filename_len;
+    const char *msg_ptr;
+    size_t msg_len;
+    const char *source; // valid until the ASTUnit is freed. can be null
+    unsigned line; // 0 based
+    unsigned column; // 0 based
+    unsigned offset; // byte offset into source
+};
+
+// ABI warning
 struct Stage2Ast;
 
-ZIG_USERLAND_EXTERN_C Error stage2_translate_c(struct Stage2Ast **out_ast,
-        const char **args_begin, const char **args_end, enum Stage2TranslateMode mode);
+// ABI warning
+ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast,
+        struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len,
+        const char **args_begin, const char **args_end, enum Stage2TranslateMode mode,
+        const char *resources_path);
+
+// ABI warning
+ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len);
 
-ZIG_USERLAND_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file);
+// ABI warning
+ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file);
 
-ZIG_USERLAND_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
+// ABI warning
+ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
 
-ZIG_USERLAND_EXTERN_C ZIG_USERLAND_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len);
+// ABI warning
+ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len);
 
 #endif
src/zig_clang.cpp
@@ -1147,3 +1147,115 @@ ZigClangAPValueLValueBase ZigClangAPValue_getLValueBase(const ZigClangAPValue *s
     clang::APValue::LValueBase lval_base = casted->getLValueBase();
     return bitcast(lval_base);
 }
+
+ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char **args_end,
+    struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path)
+{
+    clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(clang::CompilerInstance::createDiagnostics(new clang::DiagnosticOptions));
+
+    std::shared_ptr<clang::PCHContainerOperations> pch_container_ops = std::make_shared<clang::PCHContainerOperations>();
+
+    bool only_local_decls = true;
+    bool capture_diagnostics = true;
+    bool user_files_are_volatile = true;
+    bool allow_pch_with_compiler_errors = false;
+    bool single_file_parse = false;
+    bool for_serialization = false;
+    std::unique_ptr<clang::ASTUnit> *err_unit = new std::unique_ptr<clang::ASTUnit>();
+    clang::ASTUnit *ast_unit = clang::ASTUnit::LoadFromCommandLine(
+            args_begin, args_end,
+            pch_container_ops, diags, resources_path,
+            only_local_decls, capture_diagnostics, clang::None, true, 0, clang::TU_Complete,
+            false, false, allow_pch_with_compiler_errors, clang::SkipFunctionBodiesScope::None,
+            single_file_parse, user_files_are_volatile, for_serialization, clang::None, err_unit,
+            nullptr);
+
+    // Early failures in LoadFromCommandLine may return with ErrUnit unset.
+    if (!ast_unit && !err_unit) {
+        return nullptr;
+    }
+
+    if (diags->getClient()->getNumErrors() > 0) {
+        if (ast_unit) {
+            *err_unit = std::unique_ptr<clang::ASTUnit>(ast_unit);
+        }
+
+        size_t cap = 4;
+        *errors_len = 0;
+        *errors_ptr = reinterpret_cast<Stage2ErrorMsg*>(malloc(cap * sizeof(Stage2ErrorMsg)));
+        if (*errors_ptr == nullptr) {
+            return nullptr;
+        }
+
+        for (clang::ASTUnit::stored_diag_iterator it = (*err_unit)->stored_diag_begin(),
+                it_end = (*err_unit)->stored_diag_end();
+                it != it_end; ++it)
+        {
+            switch (it->getLevel()) {
+                case clang::DiagnosticsEngine::Ignored:
+                case clang::DiagnosticsEngine::Note:
+                case clang::DiagnosticsEngine::Remark:
+                case clang::DiagnosticsEngine::Warning:
+                    continue;
+                case clang::DiagnosticsEngine::Error:
+                case clang::DiagnosticsEngine::Fatal:
+                    break;
+            }
+            llvm::StringRef msg_str_ref = it->getMessage();
+            if (*errors_len >= cap) {
+                cap *= 2;
+                Stage2ErrorMsg *new_errors = reinterpret_cast<Stage2ErrorMsg *>(
+                        realloc(*errors_ptr, cap * sizeof(Stage2ErrorMsg)));
+                if (new_errors == nullptr) {
+                    free(*errors_ptr);
+                    *errors_ptr = nullptr;
+                    *errors_len = 0;
+                    return nullptr;
+                }
+                *errors_ptr = new_errors;
+            }
+            Stage2ErrorMsg *msg = *errors_ptr + *errors_len;
+            *errors_len += 1;
+            msg->msg_ptr = (const char *)msg_str_ref.bytes_begin();
+            msg->msg_len = msg_str_ref.size();
+
+            clang::FullSourceLoc fsl = it->getLocation();
+            if (fsl.hasManager()) {
+                clang::FileID file_id = fsl.getFileID();
+                clang::StringRef filename = fsl.getManager().getFilename(fsl);
+                if (filename.empty()) {
+                    msg->filename_ptr = nullptr;
+                } else {
+                    msg->filename_ptr = (const char *)filename.bytes_begin();
+                    msg->filename_len = filename.size();
+                }
+                msg->source = (const char *)fsl.getManager().getBufferData(file_id).bytes_begin();
+                msg->line = fsl.getSpellingLineNumber() - 1;
+                msg->column = fsl.getSpellingColumnNumber() - 1;
+                msg->offset = fsl.getManager().getFileOffset(fsl);
+            } else {
+                // The only known way this gets triggered right now is if you have a lot of errors
+                // clang emits "too many errors emitted, stopping now"
+                msg->filename_ptr = nullptr;
+                msg->source = nullptr;
+            }
+        }
+
+        if (*errors_len == 0) {
+            free(*errors_ptr);
+            *errors_ptr = nullptr;
+        }
+
+        return nullptr;
+    }
+
+    return reinterpret_cast<ZigClangASTUnit *>(ast_unit);
+}
+
+void ZigClangErrorMsg_delete(Stage2ErrorMsg *ptr, size_t len) {
+    free(ptr);
+}
+
+void ZigClangASTUnit_delete(struct ZigClangASTUnit *self) {
+    delete reinterpret_cast<clang::ASTUnit *>(self);
+}
src/zig_clang.h
@@ -8,15 +8,10 @@
 #ifndef ZIG_ZIG_CLANG_H
 #define ZIG_ZIG_CLANG_H
 
+#include "userland.h"
 #include <inttypes.h>
 #include <stdbool.h>
 
-#ifdef __cplusplus
-#define ZIG_EXTERN_C extern "C"
-#else
-#define ZIG_EXTERN_C
-#endif
-
 // ATTENTION: If you modify this file, be sure to update the corresponding
 // extern function declarations in the self-hosted compiler file
 // src-self-hosted/clang.zig.
@@ -500,6 +495,13 @@ ZIG_EXTERN_C const char* ZigClangSourceManager_getCharacterData(const struct Zig
 
 ZIG_EXTERN_C struct ZigClangQualType ZigClangASTContext_getPointerType(const struct ZigClangASTContext*, struct ZigClangQualType T);
 
+
+// Can return null.
+ZIG_EXTERN_C struct ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char **args_end,
+        struct Stage2ErrorMsg **errors_ptr, size_t *errors_len, const char *resources_path);
+ZIG_EXTERN_C void ZigClangASTUnit_delete(struct ZigClangASTUnit *);
+ZIG_EXTERN_C void ZigClangErrorMsg_delete(struct Stage2ErrorMsg *ptr, size_t len);
+
 ZIG_EXTERN_C struct ZigClangASTContext *ZigClangASTUnit_getASTContext(struct ZigClangASTUnit *);
 ZIG_EXTERN_C struct ZigClangSourceManager *ZigClangASTUnit_getSourceManager(struct ZigClangASTUnit *);
 ZIG_EXTERN_C bool ZigClangASTUnit_visitLocalTopLevelDecls(struct ZigClangASTUnit *, void *context, 
src-self-hosted/clang.zig
@@ -862,6 +862,7 @@ pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void;
 pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*c]const u64;
 pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint;
 pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr;
+pub extern fn ZigClangASTUnit_delete(arg0: ?*struct_ZigClangASTUnit) void;
 pub const ZigClangSourceLocation = struct_ZigClangSourceLocation;
 pub const ZigClangQualType = struct_ZigClangQualType;
 pub const ZigClangAPValueLValueBase = struct_ZigClangAPValueLValueBase;
@@ -942,3 +943,19 @@ pub const ZigClangTypeClass = enum_ZigClangTypeClass;
 pub const ZigClangStmtClass = enum_ZigClangStmtClass;
 pub const ZigClangCK = enum_ZigClangCK;
 pub const ZigClangAPValueKind = enum_ZigClangAPValueKind;
+
+pub const Stage2ErrorMsg = extern struct {
+    filename_ptr: ?[*]const u8,
+    filename_len: usize,
+    msg_ptr: [*]const u8,
+    msg_len: usize,
+    // valid until the ASTUnit is freed
+    source: ?[*]const u8,
+    // 0 based
+    line: c_uint,
+    // 0 based
+    column: c_uint,
+    // byte offset into source
+    offset: c_uint,
+};
+pub extern fn ZigClangErrorMsg_delete(ptr: [*c]Stage2ErrorMsg, len: usize) void;
src-self-hosted/main.zig
@@ -858,7 +858,23 @@ fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
     try stdout.write(usage);
 }
 
-const info_zen = @import("stage1.zig").info_zen;
+pub const info_zen =
+    \\
+    \\ * Communicate intent precisely.
+    \\ * Edge cases matter.
+    \\ * Favor reading code over writing code.
+    \\ * Only one obvious way to do things.
+    \\ * Runtime crashes are better than bugs.
+    \\ * Compile errors are better than runtime crashes.
+    \\ * Incremental improvements.
+    \\ * Avoid local maximums.
+    \\ * Reduce the amount one must remember.
+    \\ * Minimize energy spent on coding style.
+    \\ * Together we serve end users.
+    \\
+    \\
+;
+
 fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
     try stdout.write(info_zen);
 }
src-self-hosted/stage1.zig
@@ -3,37 +3,25 @@
 
 const std = @import("std");
 
-pub const info_zen =
-    \\
-    \\ * Communicate intent precisely.
-    \\ * Edge cases matter.
-    \\ * Favor reading code over writing code.
-    \\ * Only one obvious way to do things.
-    \\ * Runtime crashes are better than bugs.
-    \\ * Compile errors are better than runtime crashes.
-    \\ * Incremental improvements.
-    \\ * Avoid local maximums.
-    \\ * Reduce the amount one must remember.
-    \\ * Minimize energy spent on coding style.
-    \\ * Together we serve end users.
-    \\
-    \\
-;
-
+// ABI warning
 export fn stage2_zen(ptr: *[*]const u8, len: *usize) void {
+    const info_zen = @import("main.zig").info_zen;
     ptr.* = &info_zen;
     len.* = info_zen.len;
 }
 
+// ABI warning
 export fn stage2_panic(ptr: [*]const u8, len: usize) void {
     @panic(ptr[0..len]);
 }
 
+// ABI warning
 const TranslateMode = extern enum {
     import,
     translate,
 };
 
+// ABI warning
 const Error = extern enum {
     None,
     OutOfMemory,
@@ -84,24 +72,33 @@ const Error = extern enum {
 
 const FILE = std.c.FILE;
 const ast = std.zig.ast;
+const translate_c = @import("translate_c.zig");
 
 /// Args should have a null terminating last arg.
 export fn stage2_translate_c(
     out_ast: **ast.Tree,
+    out_errors_ptr: *[*]translate_c.ClangErrMsg,
+    out_errors_len: *usize,
     args_begin: [*]?[*]const u8,
     args_end: [*]?[*]const u8,
     mode: TranslateMode,
+    resources_path: [*]const u8,
 ) Error {
-    const translate_c = @import("translate_c.zig");
+    var errors: []translate_c.ClangErrMsg = undefined;
     out_ast.* = translate_c.translate(args_begin, args_end, switch (mode) {
         .import => translate_c.Mode.import,
         .translate => translate_c.Mode.translate,
-    }) catch |err| switch (err) {
+    }, &errors) catch |err| switch (err) {
         error.Unimplemented => return Error.Unimplemented,
     };
+
     return Error.None;
 }
 
+export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void {
+    translate_c.freeErrors(errors_ptr[0..errors_len]);
+}
+
 export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
     const c_out_stream = &std.io.COutStream.init(output_file).stream;
     _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
src-self-hosted/translate_c.zig
@@ -10,6 +10,17 @@ pub const Mode = enum {
     translate,
 };
 
-pub fn translate(args_begin: [*]?[*]const u8, args_end: [*]?[*]const u8, mode: Mode) !*ast.Tree {
+pub const ClangErrMsg = Stage2ErrorMsg;
+
+pub fn translate(
+    args_begin: [*]?[*]const u8,
+    args_end: [*]?[*]const u8,
+    mode: Mode,
+    errors: *[]ClangErrMsg,
+) !*ast.Tree {
     return error.Unimplemented;
 }
+
+pub fn freeErrors(errors: []ClangErrMsg) void {
+    ZigClangErrorMsg_delete(errors.ptr, errors.len);
+}