diff --git a/CMakeLists.txt b/CMakeLists.txt index 55b2868455..a745cf1faa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include_directories(${LLVM_INCLUDE_DIRS}) link_directories(${LLVM_LIBDIRS}) find_package(clang) -include_directories(${CLANG_INCLUDE_DIR}) +include_directories(${CLANG_INCLUDE_DIRS}) include_directories( ${CMAKE_SOURCE_DIR} @@ -148,7 +148,7 @@ set_target_properties(zig PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS}) target_link_libraries(zig LINK_PUBLIC ${LLVM_LIBRARIES} - ${CLANG_LIBRARY} + ${CLANG_LIBRARIES} ) install(TARGETS zig DESTINATION bin) diff --git a/README.md b/README.md index e95af528f7..425d309a1f 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ be set to (example below). ``` mkdir build cd build -cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_DIR=$(dirname $(cc -print-file-name=crt1.o)) +cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_DIR=$(dirname $(dirname $(cc -print-file-name=crt1.o))) make make install ./run_tests diff --git a/cmake/Findclang.cmake b/cmake/Findclang.cmake index 13328b3408..7855023510 100644 --- a/cmake/Findclang.cmake +++ b/cmake/Findclang.cmake @@ -1,16 +1,34 @@ -# Copyright (c) 2015 Andrew Kelley +# Copyright (c) 2016 Andrew Kelley # This file is MIT licensed. # See http://opensource.org/licenses/MIT # CLANG_FOUND -# CLANG_INCLUDE_DIR -# CLANG_LIBRARY +# CLANG_INCLUDE_DIRS +# CLANG_LIBRARIES -find_path(CLANG_INCLUDE_DIR NAMES clang-c/Index.h PATHS /usr/lib/llvm-3.7/include/) +find_path(CLANG_INCLUDE_DIRS NAMES clang/Frontend/ASTUnit.h) -find_library(CLANG_LIBRARY NAMES clang PATHS /usr/lib/llvm-3.7/lib/) +macro(FIND_AND_ADD_CLANG_LIB _libname_) + string(TOUPPER ${_libname_} _prettylibname_) + find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}) + if(CLANG_${_prettylibname_}_LIB) + set(CLANG_LIBRARIES ${CLANG_LIBRARIES} ${CLANG_${_prettylibname_}_LIB}) + endif() +endmacro(FIND_AND_ADD_CLANG_LIB) + +FIND_AND_ADD_CLANG_LIB(clangFrontend) +FIND_AND_ADD_CLANG_LIB(clangDriver) +FIND_AND_ADD_CLANG_LIB(clangSerialization) +FIND_AND_ADD_CLANG_LIB(clangSema) +FIND_AND_ADD_CLANG_LIB(clangAnalysis) +FIND_AND_ADD_CLANG_LIB(clangAST) +FIND_AND_ADD_CLANG_LIB(clangParse) +FIND_AND_ADD_CLANG_LIB(clangSema) +FIND_AND_ADD_CLANG_LIB(clangBasic) +FIND_AND_ADD_CLANG_LIB(clangEdit) +FIND_AND_ADD_CLANG_LIB(clangLex) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CLANG DEFAULT_MSG CLANG_LIBRARY CLANG_INCLUDE_DIR) +find_package_handle_standard_args(CLANG DEFAULT_MSG CLANG_LIBRARIES CLANG_INCLUDE_DIRS) -mark_as_advanced(CLANG_INCLUDE_DIR CLANG_LIBRARY) +mark_as_advanced(CLANG_INCLUDE_DIRS CLANG_LIBRARIES) diff --git a/src/all_types.hpp b/src/all_types.hpp index ecf7d9cbde..36f557a946 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -995,6 +995,8 @@ struct CodeGen { bool have_exported_main; bool link_libc; Buf *libc_path; + Buf *libc_lib_path; + Buf *libc_include_path; CodeGenBuildType build_type; LLVMTargetMachineRef target_machine; LLVMZigDIFile *dummy_di_file; @@ -1068,4 +1070,12 @@ struct BlockContext { Buf *c_import_buf; }; +struct ParseH { + ZigList errors; + ZigList fn_list; + ZigList struct_list; + ZigList var_list; + ZigList incomplete_struct_list; +}; + #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index af9a582f5c..78402d6614 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -10,6 +10,8 @@ #include "error.hpp" #include "zig_llvm.hpp" #include "os.hpp" +#include "parseh.hpp" +#include "config.h" static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node); @@ -83,18 +85,12 @@ static AstNode *first_executing_node(AstNode *node) { zig_unreachable(); } -void add_node_error(CodeGen *g, AstNode *node, Buf *msg) { - ErrorMsg *err = allocate(1); - err->line_start = node->line; - err->column_start = node->column; - err->line_end = -1; - err->column_end = -1; - err->msg = msg; - err->path = node->owner->path; - err->source = node->owner->source_code; - err->line_offsets = node->owner->line_offsets; +ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) { + ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column, + node->owner->source_code, node->owner->line_offsets, msg); g->errors.append(err); + return err; } TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { @@ -1036,8 +1032,22 @@ static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *import, AstNode return; } - fprintf(stderr, "c import buf:\n%s\n", buf_ptr(child_context->c_import_buf)); - zig_panic("TODO"); + find_libc_path(g); + int err; + ParseH parse_h = {{0}}; + if ((err = parse_h_buf(&parse_h, child_context->c_import_buf, buf_ptr(g->libc_include_path)))) { + zig_panic("unable to parse h file: %s\n", err_str(err)); + } + + if (parse_h.errors.length > 0) { + ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed")); + for (int i = 0; i < parse_h.errors.length; i += 1) { + ErrorMsg *err_msg = parse_h.errors.at(i); + err_msg_add_note(parent_err_msg, err_msg); + } + } else { + zig_panic("TODO integrate the parsed AST"); + } } static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { @@ -3444,7 +3454,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry return g->builtin_types.entry_void; } - buf_appendf(context->c_import_buf, "#include \""); + buf_appendf(context->c_import_buf, "#include <"); ConstExprValue *ptr_field = const_str_val->data.x_struct.fields[0]; uint64_t len = ptr_field->data.x_ptr.len; for (uint64_t i = 0; i < len; i += 1) { @@ -3454,7 +3464,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry uint8_t c = big_c; buf_appendf(context->c_import_buf, "%c", c); } - buf_appendf(context->c_import_buf, "\"\n"); + buf_appendf(context->c_import_buf, ">\n"); return g->builtin_types.entry_void; } @@ -4976,3 +4986,22 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { } zig_unreachable(); } + +void find_libc_path(CodeGen *g) { + if (!g->libc_path || buf_len(g->libc_path) == 0) { + g->libc_path = buf_create_from_str(ZIG_LIBC_DIR); + if (!g->libc_path || buf_len(g->libc_path) == 0) { + // later we can handle this better by reporting an error via the normal mechanism + zig_panic("Unable to determine libc path. You can use `--libc-path`"); + } + } + if (!g->libc_lib_path) { + g->libc_lib_path = buf_alloc(); + os_path_join(g->libc_path, buf_create_from_str("lib"), g->libc_lib_path); + } + if (!g->libc_include_path) { + g->libc_include_path = buf_alloc(); + os_path_join(g->libc_path, buf_create_from_str("include"), g->libc_include_path); + } +} + diff --git a/src/analyze.hpp b/src/analyze.hpp index e9358e07bd..00b9c0d24c 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -11,7 +11,7 @@ #include "all_types.hpp" void semantic_analyze(CodeGen *g); -void add_node_error(CodeGen *g, AstNode *node, Buf *msg); +ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); VariableTableEntry *find_variable(BlockContext *context, Buf *name); @@ -23,4 +23,6 @@ bool is_node_void_expr(AstNode *node); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits); TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits); bool handle_is_ptr(TypeTableEntry *type_entry); +void find_libc_path(CodeGen *g); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 98dbfd03e5..e99baf66ff 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3051,15 +3051,8 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, tokenize(source_code, &tokenization); if (tokenization.err) { - ErrorMsg *err = allocate(1); - err->line_start = tokenization.err_line; - err->column_start = tokenization.err_column; - err->line_end = -1; - err->column_end = -1; - err->msg = tokenization.err; - err->path = full_path; - err->source = source_code; - err->line_offsets = tokenization.line_offsets; + ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column, + source_code, tokenization.line_offsets, tokenization.err); print_err_msg(err, g->err_color); exit(1); @@ -3401,19 +3394,9 @@ static void generate_h_file(CodeGen *g) { zig_panic("unable to close h file: %s", strerror(errno)); } -static void find_libc_path(CodeGen *g) { - if (g->libc_path && buf_len(g->libc_path)) - return; - g->libc_path = buf_create_from_str(ZIG_LIBC_DIR); - if (g->libc_path && buf_len(g->libc_path)) - return; - fprintf(stderr, "Unable to determine libc path. Consider using `--libc-path [path]`\n"); - exit(1); -} - static const char *get_libc_file(CodeGen *g, const char *file) { Buf *out_buf = buf_alloc(); - os_path_join(g->libc_path, buf_create_from_str(file), out_buf); + os_path_join(g->libc_lib_path, buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } diff --git a/src/errmsg.cpp b/src/errmsg.cpp index e2722f8d50..8780f33a52 100644 --- a/src/errmsg.cpp +++ b/src/errmsg.cpp @@ -15,16 +15,7 @@ void print_err_msg(ErrorMsg *err, ErrColor color) { err->line_start + 1, err->column_start + 1, buf_ptr(err->msg)); - assert(err->source); - assert(err->line_offsets); - - int line_start_offset = err->line_offsets->at(err->line_start); - int end_line = err->line_start + 1; - int line_end_offset = (end_line >= err->line_offsets->length) ? - buf_len(err->source) : err->line_offsets->at(err->line_start + 1); - - fwrite(buf_ptr(err->source) + line_start_offset, 1, line_end_offset - line_start_offset - 1, stderr); - fprintf(stderr, "\n"); + fprintf(stderr, "%s\n", buf_ptr(&err->line_buf)); for (int i = 0; i < err->column_start; i += 1) { fprintf(stderr, " "); } @@ -36,5 +27,62 @@ void print_err_msg(ErrorMsg *err, ErrColor color) { err->line_start + 1, err->column_start + 1, buf_ptr(err->msg)); } + + for (int i = 0; i < err->notes.length; i += 1) { + ErrorMsg *note = err->notes.at(i); + print_err_msg(note, color); + } } +void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) { + parent->notes.append(note); +} + +ErrorMsg *err_msg_create_with_offset(Buf *path, int line, int column, int offset, + const char *source, Buf *msg) +{ + ErrorMsg *err_msg = allocate(1); + err_msg->path = path; + err_msg->line_start = line; + err_msg->column_start = column; + err_msg->msg = msg; + + int line_start_offset = offset; + for (;;) { + if (line_start_offset == 0) { + break; + } else if (source[line_start_offset] == '\n') { + line_start_offset += 1; + break; + } + line_start_offset -= 1; + } + + int line_end_offset = offset; + while (source[line_end_offset] && source[line_end_offset] != '\n') { + line_end_offset += 1; + } + + buf_init_from_mem(&err_msg->line_buf, source + line_start_offset, line_end_offset - line_start_offset); + + return err_msg; +} + +ErrorMsg *err_msg_create_with_line(Buf *path, int line, int column, + Buf *source, ZigList *line_offsets, Buf *msg) +{ + ErrorMsg *err_msg = allocate(1); + err_msg->path = path; + err_msg->line_start = line; + err_msg->column_start = column; + err_msg->msg = msg; + + int line_start_offset = line_offsets->at(line); + int end_line = line + 1; + int line_end_offset = (end_line >= line_offsets->length) ? buf_len(source) : line_offsets->at(line + 1); + + buf_init_from_mem(&err_msg->line_buf, buf_ptr(source) + line_start_offset, + line_end_offset - line_start_offset - 1); + + return err_msg; +} diff --git a/src/errmsg.hpp b/src/errmsg.hpp index 1f31020902..b16249ec0b 100644 --- a/src/errmsg.hpp +++ b/src/errmsg.hpp @@ -20,14 +20,20 @@ enum ErrColor { struct ErrorMsg { int line_start; int column_start; - int line_end; - int column_end; Buf *msg; Buf *path; - Buf *source; - ZigList *line_offsets; + Buf line_buf; + + ZigList notes; }; void print_err_msg(ErrorMsg *msg, ErrColor color); +void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note); +ErrorMsg *err_msg_create_with_offset(Buf *path, int line, int column, int offset, + const char *source, Buf *msg); + +ErrorMsg *err_msg_create_with_line(Buf *path, int line, int column, + Buf *source, ZigList *line_offsets, Buf *msg); + #endif diff --git a/src/main.cpp b/src/main.cpp index f7df0e9cb5..e3094cba7b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -156,6 +156,25 @@ static int build(const char *arg0, int argc, char **argv) { return 0; } +struct ParseHPrint { + ParseH parse_h; + FILE *f; + int cur_indent; +}; + +static const int indent_size = 4; + +static void print_indent(ParseHPrint *p) { + for (int i = 0; i < p->cur_indent; i += 1) { + fprintf(p->f, " "); + } +} + +static const char *type_node_to_name(AstNode *type_node) { + assert(type_node->type == NodeTypeSymbol); + return buf_ptr(&type_node->data.symbol_expr.symbol); +} + static int parseh(const char *arg0, int argc, char **argv) { char *in_file = nullptr; ZigList clang_argv = {0}; @@ -187,7 +206,73 @@ static int parseh(const char *arg0, int argc, char **argv) { return usage(arg0); } - parse_h_file(in_file, &clang_argv, stdout); + clang_argv.append(in_file); + + ParseHPrint parse_h_print = {{{0}}}; + ParseHPrint *p = &parse_h_print; + p->f = stdout; + p->cur_indent = 0; + + parse_h_file(&p->parse_h, &clang_argv); + + if (p->parse_h.errors.length > 0) { + for (int i = 0; i < p->parse_h.errors.length; i += 1) { + ErrorMsg *err_msg = p->parse_h.errors.at(i); + // TODO respect --color arg + print_err_msg(err_msg, ErrColorAuto); + } + return EXIT_FAILURE; + } + + for (int struct_i = 0; struct_i < p->parse_h.struct_list.length; struct_i += 1) { + AstNode *struct_decl = p->parse_h.struct_list.at(struct_i); + assert(struct_decl->type == NodeTypeStructDecl); + const char *struct_name = buf_ptr(&struct_decl->data.struct_decl.name); + print_indent(p); + fprintf(p->f, "struct %s {\n", struct_name); + p->cur_indent += indent_size; + for (int field_i = 0; field_i < struct_decl->data.struct_decl.fields.length; field_i += 1) { + AstNode *field_node = struct_decl->data.struct_decl.fields.at(field_i); + assert(field_node->type == NodeTypeStructField); + const char *field_name = buf_ptr(&field_node->data.struct_field.name); + const char *type_name = type_node_to_name(field_node->data.struct_field.type); + print_indent(p); + fprintf(p->f, "%s: %s,\n", field_name, type_name); + } + + p->cur_indent -= indent_size; + fprintf(p->f, "}\n\n"); + } + + for (int fn_i = 0; fn_i < p->parse_h.fn_list.length; fn_i += 1) { + AstNode *fn_proto = p->parse_h.fn_list.at(fn_i); + assert(fn_proto->type == NodeTypeFnProto); + print_indent(p); + const char *fn_name = buf_ptr(&fn_proto->data.fn_proto.name); + fprintf(p->f, "extern fn %s(", fn_name); + int arg_count = fn_proto->data.fn_proto.params.length; + bool is_var_args = fn_proto->data.fn_proto.is_var_args; + for (int arg_i = 0; arg_i < arg_count; arg_i += 1) { + AstNode *param_decl = fn_proto->data.fn_proto.params.at(arg_i); + assert(param_decl->type == NodeTypeParamDecl); + const char *arg_name = buf_ptr(¶m_decl->data.param_decl.name); + const char *arg_type = type_node_to_name(param_decl->data.param_decl.type); + fprintf(p->f, "%s: %s", arg_name, arg_type); + if (arg_i + 1 < arg_count || is_var_args) { + fprintf(p->f, ", "); + } + } + if (is_var_args) { + fprintf(p->f, "..."); + } + fprintf(p->f, ")"); + const char *return_type_name = type_node_to_name(fn_proto->data.fn_proto.return_type); + if (strcmp(return_type_name, "void") != 0) { + fprintf(p->f, " -> %s", return_type_name); + } + fprintf(p->f, ";\n"); + } + return 0; } diff --git a/src/os.cpp b/src/os.cpp index d099c3482c..7638ec5685 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -208,3 +208,29 @@ int os_get_cwd(Buf *out_cwd) { bool os_stderr_tty(void) { return isatty(STDERR_FILENO); } + +int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path) { + buf_resize(out_tmp_path, 0); + buf_appendf(out_tmp_path, "/tmp/XXXXXX%s", buf_ptr(suffix)); + + int fd = mkstemps(buf_ptr(out_tmp_path), buf_len(suffix)); + if (fd < 0) { + return ErrorFileSystem; + } + + ssize_t amt_written = write(fd, buf_ptr(contents), buf_len(contents)); + if (amt_written != buf_len(contents)) + zig_panic("write failed: %s", strerror(errno)); + if (close(fd) == -1) + zig_panic("close failed"); + + return 0; +} + +int os_delete_file(Buf *path) { + if (remove(buf_ptr(path))) { + return ErrorFileSystem; + } else { + return 0; + } +} diff --git a/src/os.hpp b/src/os.hpp index bc7679464a..172059bda2 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -29,7 +29,9 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents); int os_get_cwd(Buf *out_cwd); - bool os_stderr_tty(void); +int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); +int os_delete_file(Buf *path); + #endif diff --git a/src/parseh.cpp b/src/parseh.cpp index 1e931681ce..ee7d50ede9 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -7,540 +7,52 @@ #include "parseh.hpp" #include "config.h" +#include "os.hpp" +#include "error.hpp" -#include +#include +#include #include -struct TypeDef { - Buf alias; - Buf target; +using namespace clang; + +struct Context { + ParseH *parse_h; }; -struct Arg { - Buf name; - Buf *type; -}; +static bool decl_visitor(void *context, const Decl *decl) { + //Context *c = (Context*)context; -struct Fn { - Buf name; - Buf *return_type; - Arg *args; - int arg_count; - bool is_variadic; -}; - -struct Field { - Buf name; - Buf *type; -}; - -struct Struct { - Buf name; - ZigList fields; - bool have_def; -}; - -struct ParseH { - CXTranslationUnit tu; - FILE *f; - ZigList fn_list; - ZigList struct_list; - ZigList type_def_list; - ZigList incomplete_struct_list; - Fn *cur_fn; - Struct *cur_struct; - int arg_index; - int cur_indent; - CXSourceRange range; - CXSourceLocation location; -}; - -static const int indent_size = 4; - -struct TypeMapping { - const char *c_name; - const char *zig_name; -}; - -static const TypeMapping type_mappings[] = { - { - "int8_t", - "i8", - }, - { - "uint8_t", - "u8", - }, - { - "uint16_t", - "u16", - }, - { - "uint32_t", - "u32", - }, - { - "uint64_t", - "u64", - }, - { - "int16_t", - "i16", - }, - { - "int32_t", - "i32", - }, - { - "int64_t", - "i64", - }, - { - "intptr_t", - "isize", - }, - { - "uintptr_t", - "usize", - }, -}; - -static bool have_struct_def(ParseH *p, Buf *name) { - for (int i = 0; i < p->struct_list.length; i += 1) { - Struct *struc = p->struct_list.at(i); - if (struc->fields.length > 0 && buf_eql_buf(&struc->name, name)) { - return true; - } - } - return false; -} - -static const char *c_to_zig_name(const char *name) { - for (int i = 0; i < array_length(type_mappings); i += 1) { - const TypeMapping *mapping = &type_mappings[i]; - if (strcmp(mapping->c_name, name) == 0) - return mapping->zig_name; - } - return nullptr; -} - -static bool str_has_prefix(const char *str, const char *prefix) { - while (*prefix) { - if (*str && *str == *prefix) { - str += 1; - prefix += 1; - } else { - return false; - } - } - return true; -} - -static const char *prefixes_stripped(CXType type) { - CXString name = clang_getTypeSpelling(type); - const char *c_name = clang_getCString(name); - - static const char *prefixes[] = { - "struct ", - "enum ", - "const ", - }; - -start_over: - - for (int i = 0; i < array_length(prefixes); i += 1) { - const char *prefix = prefixes[i]; - if (str_has_prefix(c_name, prefix)) { - c_name += strlen(prefix); - goto start_over; - } - } - return c_name; -} - -static void print_location(ParseH *p) { - CXFile file; - unsigned line, column, offset; - clang_getFileLocation(p->location, &file, &line, &column, &offset); - CXString file_name = clang_getFileName(file); - - fprintf(stderr, "%s line %u, column %u\n", clang_getCString(file_name), line, column); -} - -static bool resolves_to_void(ParseH *p, CXType raw_type) { - if (raw_type.kind == CXType_Unexposed) { - CXType canonical = clang_getCanonicalType(raw_type); - if (canonical.kind == CXType_Unexposed) - zig_panic("clang C api insufficient"); - else - return resolves_to_void(p, canonical); - } - if (raw_type.kind == CXType_Void) { - return true; - } else if (raw_type.kind == CXType_Typedef) { - CXCursor typedef_cursor = clang_getTypeDeclaration(raw_type); - CXType underlying_type = clang_getTypedefDeclUnderlyingType(typedef_cursor); - return resolves_to_void(p, underlying_type); - } - return false; -} - -static Buf *to_zig_type(ParseH *p, CXType raw_type) { - if (raw_type.kind == CXType_Unexposed) { - CXType canonical = clang_getCanonicalType(raw_type); - if (canonical.kind == CXType_Unexposed) - zig_panic("clang C api insufficient"); - else - return to_zig_type(p, canonical); - } - switch (raw_type.kind) { - case CXType_Invalid: - case CXType_Unexposed: - zig_unreachable(); - case CXType_Void: - zig_panic("void type encountered"); - case CXType_Bool: - return buf_create_from_str("bool"); - case CXType_SChar: - return buf_create_from_str("i8"); - case CXType_UChar: - case CXType_Char_U: - case CXType_Char_S: - return buf_create_from_str("u8"); - case CXType_WChar: - print_location(p); - zig_panic("TODO wchar"); - case CXType_Char16: - print_location(p); - zig_panic("TODO char16"); - case CXType_Char32: - print_location(p); - zig_panic("TODO char32"); - case CXType_UShort: - return buf_create_from_str("c_ushort"); - case CXType_UInt: - return buf_create_from_str("c_uint"); - case CXType_ULong: - return buf_create_from_str("c_ulong"); - case CXType_ULongLong: - return buf_create_from_str("c_ulonglong"); - case CXType_UInt128: - print_location(p); - zig_panic("TODO uint128"); - case CXType_Short: - return buf_create_from_str("c_short"); - case CXType_Int: - return buf_create_from_str("c_int"); - case CXType_Long: - return buf_create_from_str("c_long"); - case CXType_LongLong: - return buf_create_from_str("c_longlong"); - case CXType_Int128: - print_location(p); - zig_panic("TODO int128"); - case CXType_Float: - return buf_create_from_str("f32"); - case CXType_Double: - return buf_create_from_str("f64"); - case CXType_LongDouble: - return buf_create_from_str("f128"); - case CXType_IncompleteArray: - { - CXType pointee_type = clang_getArrayElementType(raw_type); - Buf *pointee_buf = to_zig_type(p, pointee_type); - if (clang_isConstQualifiedType(pointee_type)) { - return buf_sprintf("&const %s", buf_ptr(pointee_buf)); - } else { - return buf_sprintf("&%s", buf_ptr(pointee_buf)); - } - } - case CXType_Pointer: - { - CXType pointee_type = clang_getPointeeType(raw_type); - Buf *pointee_buf; - if (resolves_to_void(p, pointee_type)) { - pointee_buf = buf_create_from_str("u8"); - } else { - pointee_buf = to_zig_type(p, pointee_type); - } - if (clang_isConstQualifiedType(pointee_type)) { - return buf_sprintf("&const %s", buf_ptr(pointee_buf)); - } else { - return buf_sprintf("&%s", buf_ptr(pointee_buf)); - } - } - case CXType_Record: - { - const char *name = prefixes_stripped(raw_type); - return buf_sprintf("%s", name); - } - case CXType_Enum: - { - const char *name = prefixes_stripped(raw_type); - return buf_sprintf("%s", name); - } - case CXType_Typedef: - { - const char *name = prefixes_stripped(raw_type); - const char *zig_name = c_to_zig_name(name); - if (zig_name) { - return buf_create_from_str(zig_name); - } else { - CXCursor typedef_cursor = clang_getTypeDeclaration(raw_type); - CXType underlying_type = clang_getTypedefDeclUnderlyingType(typedef_cursor); - if (resolves_to_void(p, underlying_type)) { - return buf_create_from_str("u8"); - } else { - return buf_create_from_str(name); - } - } - } - case CXType_ConstantArray: - { - CXType child_type = clang_getArrayElementType(raw_type); - Buf *zig_child_type = to_zig_type(p, child_type); - long size = (long)clang_getArraySize(raw_type); - return buf_sprintf("[%s; %ld]", buf_ptr(zig_child_type), size); - } - case CXType_FunctionProto: - fprintf(stderr, "warning: TODO function proto\n"); - print_location(p); - return buf_create_from_str("u8"); - case CXType_FunctionNoProto: - print_location(p); - zig_panic("TODO function no proto"); - case CXType_BlockPointer: - print_location(p); - zig_panic("TODO block pointer"); - case CXType_Vector: - print_location(p); - zig_panic("TODO vector"); - case CXType_LValueReference: - case CXType_RValueReference: - case CXType_VariableArray: - case CXType_DependentSizedArray: - case CXType_MemberPointer: - case CXType_ObjCInterface: - case CXType_ObjCObjectPointer: - case CXType_NullPtr: - case CXType_Overload: - case CXType_Dependent: - case CXType_ObjCId: - case CXType_ObjCClass: - case CXType_ObjCSel: - case CXType_Complex: - print_location(p); - zig_panic("TODO"); - } - - zig_unreachable(); -} - -static bool is_storage_class_export(CX_StorageClass storage_class) { - switch (storage_class) { - case CX_SC_Invalid: - zig_unreachable(); - case CX_SC_None: - case CX_SC_Extern: - case CX_SC_Auto: - return true; - case CX_SC_Static: - case CX_SC_PrivateExtern: - case CX_SC_OpenCLWorkGroupLocal: - case CX_SC_Register: - return false; - } - zig_unreachable(); -} - -static enum CXChildVisitResult visit_fn_children(CXCursor cursor, CXCursor parent, CXClientData client_data) { - ParseH *p = (ParseH*)client_data; - enum CXCursorKind kind = clang_getCursorKind(cursor); - - switch (kind) { - case CXCursor_ParmDecl: - { - assert(p->cur_fn); - assert(p->arg_index < p->cur_fn->arg_count); - CXString name = clang_getCursorSpelling(cursor); - Buf *arg_name = &p->cur_fn->args[p->arg_index].name; - buf_init_from_str(arg_name, clang_getCString(name)); - if (buf_len(arg_name) == 0) { - buf_appendf(arg_name, "arg%d", p->arg_index); - } - - p->arg_index += 1; - return CXChildVisit_Continue; - } - default: - return CXChildVisit_Recurse; - } -} - -static enum CXChildVisitResult visit_struct_children(CXCursor cursor, CXCursor parent, CXClientData client_data) { - ParseH *p = (ParseH*)client_data; - enum CXCursorKind kind = clang_getCursorKind(cursor); - - switch (kind) { - case CXCursor_FieldDecl: - { - assert(p->cur_struct); - CXString name = clang_getCursorSpelling(cursor); - Field *field = allocate(1); - buf_init_from_str(&field->name, clang_getCString(name)); - CXType cursor_type = clang_getCursorType(cursor); - field->type = to_zig_type(p, cursor_type); - - p->cur_struct->fields.append(field); - - return CXChildVisit_Continue; - } - default: - return CXChildVisit_Recurse; - } -} - -static bool handle_struct_cursor(ParseH *p, CXCursor cursor, const char *name, bool expect_name) { - p->cur_struct = allocate(1); - - buf_init_from_str(&p->cur_struct->name, name); - - bool got_name = (buf_len(&p->cur_struct->name) != 0); - if (expect_name != got_name) - return false; - - clang_visitChildren(cursor, visit_struct_children, p); - - if (p->cur_struct->fields.length > 0) { - p->struct_list.append(p->cur_struct); - } else { - p->incomplete_struct_list.append(p->cur_struct); - } - - p->cur_struct = nullptr; + fprintf(stderr, "got top level decl\n"); return true; } - -static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXClientData client_data) { - ParseH *p = (ParseH*)client_data; - enum CXCursorKind kind = clang_getCursorKind(cursor); - CXString name = clang_getCursorSpelling(cursor); - - p->range = clang_getCursorExtent(cursor); - p->location = clang_getRangeStart(p->range); - - switch (kind) { - case CXCursor_FunctionDecl: - { - CX_StorageClass storage_class = clang_Cursor_getStorageClass(cursor); - if (!is_storage_class_export(storage_class)) - return CXChildVisit_Continue; - - CXType fn_type = clang_getCursorType(cursor); - if (clang_getFunctionTypeCallingConv(fn_type) != CXCallingConv_C) { - print_location(p); - fprintf(stderr, "warning: skipping non c calling convention function, not yet supported\n"); - return CXChildVisit_Continue; - } - - assert(!p->cur_fn); - p->cur_fn = allocate(1); - - p->cur_fn->is_variadic = clang_isFunctionTypeVariadic(fn_type); - - CXType return_type = clang_getResultType(fn_type); - if (!resolves_to_void(p, return_type)) { - p->cur_fn->return_type = to_zig_type(p, return_type); - } - - buf_init_from_str(&p->cur_fn->name, clang_getCString(name)); - - p->cur_fn->arg_count = clang_getNumArgTypes(fn_type); - p->cur_fn->args = allocate(p->cur_fn->arg_count); - - for (int i = 0; i < p->cur_fn->arg_count; i += 1) { - CXType param_type = clang_getArgType(fn_type, i); - p->cur_fn->args[i].type = to_zig_type(p, param_type); - } - - p->arg_index = 0; - - clang_visitChildren(cursor, visit_fn_children, p); - - p->fn_list.append(p->cur_fn); - p->cur_fn = nullptr; - - return CXChildVisit_Recurse; - } - case CXCursor_CompoundStmt: - case CXCursor_FieldDecl: - case CXCursor_TypedefDecl: - { - CXType underlying_type = clang_getTypedefDeclUnderlyingType(cursor); - - if (resolves_to_void(p, underlying_type)) { - return CXChildVisit_Continue; - } - - if (underlying_type.kind == CXType_Unexposed) { - underlying_type = clang_getCanonicalType(underlying_type); - } - bool skip_typedef; - if (underlying_type.kind == CXType_Unexposed) { - fprintf(stderr, "warning: unexposed type\n"); - print_location(p); - skip_typedef = true; - } else if (underlying_type.kind == CXType_Record) { - CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type); - skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false); - } else if (underlying_type.kind == CXType_Invalid) { - fprintf(stderr, "warning: invalid type\n"); - print_location(p); - skip_typedef = true; - } else { - skip_typedef = false; - } - - CXType typedef_type = clang_getCursorType(cursor); - const char *name_str = prefixes_stripped(typedef_type); - if (!skip_typedef && c_to_zig_name(name_str)) { - skip_typedef = true; - } - - if (!skip_typedef) { - TypeDef *type_def = allocate(1); - buf_init_from_str(&type_def->alias, name_str); - buf_init_from_buf(&type_def->target, to_zig_type(p, underlying_type)); - p->type_def_list.append(type_def); - } - - return CXChildVisit_Continue; - } - case CXCursor_StructDecl: - { - handle_struct_cursor(p, cursor, clang_getCString(name), true); - - return CXChildVisit_Continue; - } - default: - return CXChildVisit_Recurse; +int parse_h_buf(ParseH *parse_h, Buf *source, const char *libc_include_path) { + int err; + Buf tmp_file_path = BUF_INIT; + if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) { + return err; } + ZigList clang_argv = {0}; + clang_argv.append(buf_ptr(&tmp_file_path)); + + clang_argv.append("-isystem"); + clang_argv.append(libc_include_path); + + err = parse_h_file(parse_h, &clang_argv); + + os_delete_file(&tmp_file_path); + + return err; + // write to temp file, parse it, delete it } -static void print_indent(ParseH *p) { - for (int i = 0; i < p->cur_indent; i += 1) { - fprintf(p->f, " "); - } -} - -void parse_h_file(const char *target_path, ZigList *clang_argv, FILE *f) { - ParseH parse_h = {0}; - ParseH *p = &parse_h; - p->f = f; - CXIndex index = clang_createIndex(1, 0); +int parse_h_file(ParseH *parse_h, ZigList *clang_argv) { + Context context = {0}; + Context *c = &context; + c->parse_h = parse_h; char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); if (ZIG_PARSEH_CFLAGS) { @@ -558,105 +70,78 @@ void parse_h_file(const char *target_path, ZigList *clang_argv, FI buf_init_from_str(&tmp_buf, start); clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf))); } + clang_argv->append("-isystem"); clang_argv->append(ZIG_HEADERS_DIR); + // we don't need spell checking and it slows things down + clang_argv->append("-fno-spell-checking"); + // to make the end argument work clang_argv->append(nullptr); - enum CXErrorCode err_code; - if ((err_code = clang_parseTranslationUnit2(index, target_path, - clang_argv->items, clang_argv->length - 1, - NULL, 0, CXTranslationUnit_None, &p->tu))) - { - zig_panic("parse translation unit failure"); + IntrusiveRefCntPtr diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + + std::shared_ptr pch_container_ops = std::make_shared(); + + bool skip_function_bodies = true; + bool only_local_decls = true; + bool capture_diagnostics = true; + bool user_files_are_volatile = true; + bool allow_pch_with_compiler_errors = false; + const char *resources_path = ZIG_HEADERS_DIR; + std::unique_ptr err_unit; + std::unique_ptr ast_unit(ASTUnit::LoadFromCommandLine( + &clang_argv->at(0), &clang_argv->last(), + pch_container_ops, diags, resources_path, + only_local_decls, capture_diagnostics, None, true, false, TU_Complete, + false, false, allow_pch_with_compiler_errors, skip_function_bodies, + user_files_are_volatile, false, &err_unit)); + + + // Early failures in LoadFromCommandLine may return with ErrUnit unset. + if (!ast_unit && !err_unit) { + return ErrorFileSystem; } - - unsigned diag_count = clang_getNumDiagnostics(p->tu); - - if (diag_count > 0) { - for (unsigned i = 0; i < diag_count; i += 1) { - CXDiagnostic diagnostic = clang_getDiagnostic(p->tu, i); - CXSourceLocation location = clang_getDiagnosticLocation(diagnostic); - - CXFile file; - unsigned line, column, offset; - clang_getSpellingLocation(location, &file, &line, &column, &offset); - CXString text = clang_getDiagnosticSpelling(diagnostic); - CXString file_name = clang_getFileName(file); - fprintf(stderr, "%s line %u, column %u: %s\n", clang_getCString(file_name), - line, column, clang_getCString(text)); + if (diags->getClient()->getNumErrors() > 0) { + if (ast_unit) { + err_unit = std::move(ast_unit); } - exit(1); - } - - - CXCursor cursor = clang_getTranslationUnitCursor(p->tu); - clang_visitChildren(cursor, fn_visitor, p); - - for (int struct_i = 0; struct_i < p->struct_list.length; struct_i += 1) { - Struct *struc = p->struct_list.at(struct_i); - fprintf(f, "struct %s {\n", buf_ptr(&struc->name)); - p->cur_indent += indent_size; - for (int field_i = 0; field_i < struc->fields.length; field_i += 1) { - Field *field = struc->fields.at(field_i); - print_indent(p); - fprintf(f, "%s: %s,\n", buf_ptr(&field->name), buf_ptr(field->type)); - } - - p->cur_indent -= indent_size; - fprintf(f, "}\n\n"); - } - - int total_typedef_count = p->type_def_list.length; - for (int i = 0; i < p->incomplete_struct_list.length; i += 1) { - Struct *struc = p->incomplete_struct_list.at(i); - struc->have_def = have_struct_def(p, &struc->name); - total_typedef_count += (int)!struc->have_def; - } - - if (total_typedef_count) { - for (int i = 0; i < p->incomplete_struct_list.length; i += 1) { - Struct *struc = p->incomplete_struct_list.at(i); - if (struc->have_def) - continue; - - fprintf(f, "struct %s;\n", buf_ptr(&struc->name)); - } - - for (int type_def_i = 0; type_def_i < p->type_def_list.length; type_def_i += 1) { - TypeDef *type_def = p->type_def_list.at(type_def_i); - fprintf(f, "type %s = %s;\n", buf_ptr(&type_def->alias), buf_ptr(&type_def->target)); - } - - fprintf(f, "\n"); - } - - if (p->fn_list.length) { - fprintf(f, "extern {\n"); - p->cur_indent += indent_size; - for (int fn_i = 0; fn_i < p->fn_list.length; fn_i += 1) { - Fn *fn = p->fn_list.at(fn_i); - print_indent(p); - fprintf(p->f, "fn %s(", buf_ptr(&fn->name)); - for (int arg_i = 0; arg_i < fn->arg_count; arg_i += 1) { - Arg *arg = &fn->args[arg_i]; - fprintf(p->f, "%s: %s", buf_ptr(&arg->name), buf_ptr(arg->type)); - if (arg_i + 1 < fn->arg_count || fn->is_variadic) { - fprintf(p->f, ", "); - } + for (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 DiagnosticsEngine::Ignored: + case DiagnosticsEngine::Note: + case DiagnosticsEngine::Remark: + case DiagnosticsEngine::Warning: + continue; + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + break; } - if (fn->is_variadic) { - fprintf(p->f, "..."); - } - fprintf(p->f, ")"); - if (fn->return_type) { - fprintf(p->f, " -> %s", buf_ptr(fn->return_type)); - } - fprintf(p->f, ";\n"); + StringRef msg_str_ref = it->getMessage(); + FullSourceLoc fsl = it->getLocation(); + FileID file_id = fsl.getManager().getFileID(fsl); + 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 *msg = buf_create_from_str((const char *)msg_str_ref.bytes_begin()); + Buf *path = buf_create_from_str((const char *)filename.bytes_begin()); + + ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg); + + parse_h->errors.append(err_msg); } - p->cur_indent -= indent_size; - fprintf(f, "}\n"); + + return 0; } + + ast_unit->visitLocalTopLevelDecls(c, decl_visitor); + + return 0; } diff --git a/src/parseh.hpp b/src/parseh.hpp index b035b3c7cc..356bbf50cf 100644 --- a/src/parseh.hpp +++ b/src/parseh.hpp @@ -9,10 +9,9 @@ #ifndef ZIG_PARSEH_HPP #define ZIG_PARSEH_HPP -#include "buffer.hpp" +#include "all_types.hpp" -#include - -void parse_h_file(const char *target_path, ZigList *clang_argv, FILE *f); +int parse_h_file(ParseH *parse_h, ZigList *clang_argv); +int parse_h_buf(ParseH *parse_h, Buf *buf, const char *libc_include_path); #endif diff --git a/src/parser.cpp b/src/parser.cpp index 17e37db1e2..be5acd9a95 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -488,23 +488,16 @@ __attribute__ ((noreturn)) static void ast_asm_error(ParseContext *pc, AstNode *node, int offset, const char *format, ...) { assert(node->type == NodeTypeAsmExpr); - ErrorMsg *err = allocate(1); SrcPos pos = node->data.asm_expr.offset_map.at(offset); - err->line_start = pos.line; - err->column_start = pos.column; - err->line_end = -1; - err->column_end = -1; - va_list ap; va_start(ap, format); - err->msg = buf_vprintf(format, ap); + Buf *msg = buf_vprintf(format, ap); va_end(ap); - err->path = pc->owner->path; - err->source = pc->owner->source_code; - err->line_offsets = pc->owner->line_offsets; + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, pos.line, pos.column, + pc->owner->source_code, pc->owner->line_offsets, msg); print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); @@ -513,20 +506,16 @@ static void ast_asm_error(ParseContext *pc, AstNode *node, int offset, const cha __attribute__ ((format (printf, 3, 4))) __attribute__ ((noreturn)) static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { - ErrorMsg *err = allocate(1); - err->line_start = token->start_line; - err->column_start = token->start_column; - err->line_end = -1; - err->column_end = -1; - va_list ap; va_start(ap, format); - err->msg = buf_vprintf(format, ap); + Buf *msg = buf_vprintf(format, ap); va_end(ap); - err->path = pc->owner->path; - err->source = pc->owner->source_code; - err->line_offsets = pc->owner->line_offsets; + + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, + pc->owner->source_code, pc->owner->line_offsets, msg); + err->line_start = token->start_line; + err->column_start = token->start_column; print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 5cd32f6299..8adc44b5df 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1673,6 +1673,13 @@ fn f(s: []u8) -> []u8 { } )SOURCE", 1, ".tmp_source.zig:3:5: error: string concatenation requires constant expression"); + add_compile_fail_case("c_import with bogus include", R"SOURCE( +c_import { + @c_include("bogus.h"); +} + )SOURCE", 2, ".tmp_source.zig:2:1: error: C import failed", + ".h:1:10: error: 'bogus.h' file not found"); + } static void print_compiler_invocation(TestCase *test_case) {