From 31cf43de54c012c3b6f4fa7a516e2aac0ae18b56 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 30 Nov 2015 23:06:29 -0700 Subject: analyze looks for root export decl only in the root source file --- src/analyze.cpp | 82 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 44 insertions(+), 38 deletions(-) (limited to 'src/analyze.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index 37c3950ac2..e1a960a698 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -175,41 +175,46 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, } break; case NodeTypeRootExportDecl: - for (int i = 0; i < node->data.root_export_decl.directives->length; i += 1) { - AstNode *directive_node = node->data.root_export_decl.directives->at(i); - Buf *name = &directive_node->data.directive.name; - Buf *param = &directive_node->data.directive.param; - if (buf_eql_str(name, "version")) { - set_root_export_version(g, param, directive_node); - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + if (import == g->root_import) { + for (int i = 0; i < node->data.root_export_decl.directives->length; i += 1) { + AstNode *directive_node = node->data.root_export_decl.directives->at(i); + Buf *name = &directive_node->data.directive.name; + Buf *param = &directive_node->data.directive.param; + if (buf_eql_str(name, "version")) { + set_root_export_version(g, param, directive_node); + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + } } - } - if (g->root_export_decl) { - add_node_error(g, node, - buf_sprintf("only one root export declaration allowed")); - } else { - g->root_export_decl = node; - - if (!g->root_out_name) - g->root_out_name = &node->data.root_export_decl.name; - - Buf *out_type = &node->data.root_export_decl.type; - OutType export_out_type; - if (buf_eql_str(out_type, "executable")) { - export_out_type = OutTypeExe; - } else if (buf_eql_str(out_type, "library")) { - export_out_type = OutTypeLib; - } else if (buf_eql_str(out_type, "object")) { - export_out_type = OutTypeObj; - } else { + if (g->root_export_decl) { add_node_error(g, node, - buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); + buf_sprintf("only one root export declaration allowed")); + } else { + g->root_export_decl = node; + + if (!g->root_out_name) + g->root_out_name = &node->data.root_export_decl.name; + + Buf *out_type = &node->data.root_export_decl.type; + OutType export_out_type; + if (buf_eql_str(out_type, "executable")) { + export_out_type = OutTypeExe; + } else if (buf_eql_str(out_type, "library")) { + export_out_type = OutTypeLib; + } else if (buf_eql_str(out_type, "object")) { + export_out_type = OutTypeObj; + } else { + add_node_error(g, node, + buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); + } + if (g->out_type == OutTypeUnknown) + g->out_type = export_out_type; } - if (g->out_type == OutTypeUnknown) - g->out_type = export_out_type; + } else { + add_node_error(g, node, + buf_sprintf("root export declaration only valid in root source file")); } break; case NodeTypeUse: @@ -428,13 +433,6 @@ static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { analyze_top_level_declaration(g, child); } - if (!g->root_out_name) { - add_node_error(g, node, - buf_sprintf("missing export declaration and output name not provided")); - } else if (g->out_type == OutTypeUnknown) { - add_node_error(g, node, - buf_sprintf("missing export declaration and export type not provided")); - } } void semantic_analyze(CodeGen *g) { @@ -447,4 +445,12 @@ void semantic_analyze(CodeGen *g) { ImportTableEntry *import = entry->value; analyze_root(g, import, import->root); } + + if (!g->root_out_name) { + add_node_error(g, g->root_import->root, + buf_sprintf("missing export declaration and output name not provided")); + } else if (g->out_type == OutTypeUnknown) { + add_node_error(g, g->root_import->root, + buf_sprintf("missing export declaration and export type not provided")); + } } -- cgit v1.2.3 From 257cf09472ce5f4a51bf39808e119717fa0e4280 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Dec 2015 00:50:11 -0700 Subject: colored error messages that tell the source file --- CMakeLists.txt | 1 + src/analyze.cpp | 18 +++++--- src/buffer.cpp | 14 ++++-- src/buffer.hpp | 2 + src/codegen.cpp | 33 ++++++++++---- src/codegen.hpp | 9 ---- src/errmsg.cpp | 38 ++++++++++++++++ src/errmsg.hpp | 27 ++++++++++++ src/os.cpp | 4 ++ src/os.hpp | 2 + src/parser.cpp | 118 +++++++++++++++++++++++++++----------------------- src/parser.hpp | 5 ++- src/semantic_info.hpp | 5 ++- src/tokenizer.cpp | 43 ++++++++++-------- src/tokenizer.hpp | 12 ++++- test/run_tests.cpp | 22 +++++----- 16 files changed, 240 insertions(+), 113 deletions(-) create mode 100644 src/errmsg.cpp create mode 100644 src/errmsg.hpp (limited to 'src/analyze.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index e8c78213ec..0f980bd16a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/main.cpp" "${CMAKE_SOURCE_DIR}/src/os.cpp" "${CMAKE_SOURCE_DIR}/src/util.cpp" + "${CMAKE_SOURCE_DIR}/src/errmsg.cpp" "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" ) diff --git a/src/analyze.cpp b/src/analyze.cpp index e1a960a698..fccbad8bb7 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -12,13 +12,17 @@ #include "os.hpp" static void add_node_error(CodeGen *g, AstNode *node, Buf *msg) { - g->errors.add_one(); - ErrorMsg *last_msg = &g->errors.last(); - last_msg->line_start = node->line; - last_msg->column_start = node->column; - last_msg->line_end = -1; - last_msg->column_end = -1; - last_msg->msg = 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; + + g->errors.append(err); } static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) { diff --git a/src/buffer.cpp b/src/buffer.cpp index 1978371034..a03ab333f3 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -3,9 +3,8 @@ #include #include -Buf *buf_sprintf(const char *format, ...) { - va_list ap, ap2; - va_start(ap, format); +Buf *buf_vprintf(const char *format, va_list ap) { + va_list ap2; va_copy(ap2, ap); int len1 = vsnprintf(nullptr, 0, format, ap); @@ -19,11 +18,18 @@ Buf *buf_sprintf(const char *format, ...) { assert(len2 == len1); va_end(ap2); - va_end(ap); return buf; } +Buf *buf_sprintf(const char *format, ...) { + va_list ap; + va_start(ap, format); + Buf *result = buf_vprintf(format, ap); + va_end(ap); + return result; +} + void buf_appendf(Buf *buf, const char *format, ...) { assert(buf->list.length); va_list ap, ap2; diff --git a/src/buffer.hpp b/src/buffer.hpp index f9b2497548..3670332038 100644 --- a/src/buffer.hpp +++ b/src/buffer.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #define BUF_INIT {{0}} @@ -24,6 +25,7 @@ struct Buf { Buf *buf_sprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +Buf *buf_vprintf(const char *format, va_list ap); static inline int buf_len(Buf *buf) { assert(buf->list.length); diff --git a/src/codegen.cpp b/src/codegen.cpp index ee15e2a2e5..3717fb63d4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -13,6 +13,7 @@ #include "error.hpp" #include "semantic_info.hpp" #include "analyze.hpp" +#include "errmsg.hpp" #include #include @@ -678,24 +679,42 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou fprintf(stderr, "---------\n"); } - ZigList *tokens = tokenize(source_code); + Tokenization tokenization = {0}; + 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 = source_path; + err->source = source_code; + err->line_offsets = tokenization.line_offsets; + + print_err_msg(err); + exit(1); + } if (g->verbose) { - print_tokens(source_code, tokens); + print_tokens(source_code, tokenization.tokens); fprintf(stderr, "\nAST:\n"); fprintf(stderr, "------\n"); } ImportTableEntry *import_entry = allocate(1); + import_entry->source_code = source_code; + import_entry->line_offsets = tokenization.line_offsets; + import_entry->path = source_path; import_entry->fn_table.init(32); - import_entry->root = ast_parse(source_code, tokens); + import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry); assert(import_entry->root); if (g->verbose) { ast_print(import_entry->root, 0); } - import_entry->path = source_path; import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(&basename), buf_ptr(&dirname)); g->import_table.put(source_path, import_entry); @@ -736,10 +755,8 @@ void codegen_add_root_code(CodeGen *g, Buf *source_path, Buf *source_code) { } } else { for (int i = 0; i < g->errors.length; i += 1) { - ErrorMsg *err = &g->errors.at(i); - fprintf(stderr, "Error: Line %d, column %d: %s\n", - err->line_start + 1, err->column_start + 1, - buf_ptr(err->msg)); + ErrorMsg *err = g->errors.at(i); + print_err_msg(err); } exit(1); } diff --git a/src/codegen.hpp b/src/codegen.hpp index 4476017840..5795a5a946 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -20,15 +20,6 @@ enum OutType { }; -struct ErrorMsg { - int line_start; - int column_start; - int line_end; - int column_end; - Buf *msg; -}; - - CodeGen *codegen_create(Buf *root_source_dir); enum CodeGenBuildType { diff --git a/src/errmsg.cpp b/src/errmsg.cpp new file mode 100644 index 0000000000..36d203a48f --- /dev/null +++ b/src/errmsg.cpp @@ -0,0 +1,38 @@ +#include "errmsg.hpp" +#include "os.hpp" + +#include + +#define RED "\x1b[31;1m" +#define WHITE "\x1b[37;1m" +#define GREEN "\x1b[32;1m" +#define RESET "\x1b[0m" + +void print_err_msg(ErrorMsg *err) { + if (os_stderr_tty()) { + fprintf(stderr, WHITE "%s:%d:%d: " RED "error:" WHITE " %s" RESET "\n", + buf_ptr(err->path), + 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 line_end_offset = 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"); + for (int i = 0; i < err->column_start; i += 1) { + fprintf(stderr, " "); + } + fprintf(stderr, GREEN "^" RESET "\n"); + + } else { + fprintf(stderr, "%s:%d:%d: error: %s\n", + buf_ptr(err->path), + err->line_start + 1, err->column_start + 1, + buf_ptr(err->msg)); + } +} + diff --git a/src/errmsg.hpp b/src/errmsg.hpp new file mode 100644 index 0000000000..fa851d44bc --- /dev/null +++ b/src/errmsg.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_ERRMSG_HPP +#define ZIG_ERRMSG_HPP + +#include "buffer.hpp" +#include "list.hpp" + +struct ErrorMsg { + int line_start; + int column_start; + int line_end; + int column_end; + Buf *msg; + Buf *path; + Buf *source; + ZigList *line_offsets; +}; + +void print_err_msg(ErrorMsg *msg); + +#endif diff --git a/src/os.cpp b/src/os.cpp index 61dd1c01ee..bfc8eaa776 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -180,3 +180,7 @@ int os_get_cwd(Buf *out_cwd) { return 0; } + +bool os_stderr_tty(void) { + return isatty(STDERR_FILENO); +} diff --git a/src/os.hpp b/src/os.hpp index 8bb5d8ea4f..7b71437852 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -29,4 +29,6 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents); int os_get_cwd(Buf *out_cwd); +bool os_stderr_tty(void); + #endif diff --git a/src/parser.cpp b/src/parser.cpp index 613c559235..a6809a2777 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6,6 +6,8 @@ */ #include "parser.hpp" +#include "errmsg.hpp" +#include "semantic_info.hpp" #include #include @@ -45,21 +47,6 @@ static const char *prefix_op_str(PrefixOp prefix_op) { zig_unreachable(); } -__attribute__ ((format (printf, 2, 3))) -__attribute__ ((noreturn)) -static void ast_error(Token *token, const char *format, ...) { - int line = token->start_line + 1; - int column = token->start_column + 1; - - va_list ap; - va_start(ap, format); - fprintf(stderr, "Error: Line %d, column %d: ", line, column); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - va_end(ap); - exit(EXIT_FAILURE); -} - const char *node_type_str(NodeType node_type) { switch (node_type) { case NodeTypeRoot: @@ -254,11 +241,35 @@ struct ParseContext { AstNode *root; ZigList *tokens; ZigList *directive_list; + ImportTableEntry *owner; }; -static AstNode *ast_create_node_no_line_info(NodeType type) { +__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); + va_end(ap); + + err->path = pc->owner->path; + err->source = pc->owner->source_code; + err->line_offsets = pc->owner->line_offsets; + + print_err_msg(err); + exit(EXIT_FAILURE); +} + +static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { AstNode *node = allocate(1); node->type = type; + node->owner = pc->owner; return node; } @@ -267,21 +278,21 @@ static void ast_update_node_line_info(AstNode *node, Token *first_token) { node->column = first_token->start_column; } -static AstNode *ast_create_node(NodeType type, Token *first_token) { - AstNode *node = ast_create_node_no_line_info(type); +static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) { + AstNode *node = ast_create_node_no_line_info(pc, type); ast_update_node_line_info(node, first_token); return node; } -static AstNode *ast_create_node_with_node(NodeType type, AstNode *other_node) { - AstNode *node = ast_create_node_no_line_info(type); +static AstNode *ast_create_node_with_node(ParseContext *pc, NodeType type, AstNode *other_node) { + AstNode *node = ast_create_node_no_line_info(pc, type); node->line = other_node->line; node->column = other_node->column; return node; } static AstNode *ast_create_void_type_node(ParseContext *pc, Token *token) { - AstNode *node = ast_create_node(NodeTypeType, token); + AstNode *node = ast_create_node(pc, NodeTypeType, token); node->data.type.type = AstNodeTypeTypePrimitive; buf_init_from_str(&node->data.type.primitive_name, "void"); return node; @@ -331,7 +342,7 @@ __attribute__ ((noreturn)) static void ast_invalid_token_error(ParseContext *pc, Token *token) { Buf token_value = BUF_INIT; ast_buf_from_token(pc, token, &token_value); - ast_error(token, "invalid token: '%s'", buf_ptr(&token_value)); + ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); } static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory); @@ -349,7 +360,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_ token_index += 1; ast_expect_token(pc, number_sign, TokenIdNumberSign); - AstNode *node = ast_create_node(NodeTypeDirective, number_sign); + AstNode *node = ast_create_node(pc, NodeTypeDirective, number_sign); Token *name_symbol = &pc->tokens->at(token_index); token_index += 1; @@ -399,7 +410,7 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token Token *token = &pc->tokens->at(token_index); token_index += 1; - AstNode *node = ast_create_node(NodeTypeType, token); + AstNode *node = ast_create_node(pc, NodeTypeType, token); if (token->id == TokenIdKeywordUnreachable) { node->data.type.type = AstNodeTypeTypePrimitive; @@ -437,7 +448,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new token_index += 1; ast_expect_token(pc, param_name, TokenIdSymbol); - AstNode *node = ast_create_node(NodeTypeParamDecl, param_name); + AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name); ast_buf_from_token(pc, param_name, &node->data.param_decl.name); @@ -544,21 +555,21 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdNumberLiteral) { - AstNode *node = ast_create_node(NodeTypeNumberLiteral, token); + AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token); ast_buf_from_token(pc, token, &node->data.number); *token_index += 1; return node; } else if (token->id == TokenIdStringLiteral) { - AstNode *node = ast_create_node(NodeTypeStringLiteral, token); + AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token); parse_string_literal(pc, token, &node->data.string); *token_index += 1; return node; } else if (token->id == TokenIdKeywordUnreachable) { - AstNode *node = ast_create_node(NodeTypeUnreachable, token); + AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token); *token_index += 1; return node; } else if (token->id == TokenIdSymbol) { - AstNode *node = ast_create_node(NodeTypeSymbol, token); + AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); ast_buf_from_token(pc, token, &node->data.symbol); *token_index += 1; return node; @@ -592,7 +603,7 @@ static AstNode *ast_parse_fn_call_expr(ParseContext *pc, int *token_index, bool if (l_paren->id != TokenIdLParen) return primary_expr; - AstNode *node = ast_create_node_with_node(NodeTypeFnCallExpr, primary_expr); + AstNode *node = ast_create_node_with_node(pc, NodeTypeFnCallExpr, primary_expr); node->data.fn_call_expr.fn_ref_expr = primary_expr; ast_parse_fn_call_param_list(pc, *token_index, token_index, &node->data.fn_call_expr.params); @@ -635,7 +646,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo return ast_parse_fn_call_expr(pc, token_index, mandatory); AstNode *primary_expr = ast_parse_fn_call_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypePrefixOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); node->data.prefix_op_expr.primary_expr = primary_expr; node->data.prefix_op_expr.prefix_op = prefix_op; @@ -656,7 +667,7 @@ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bo return prefix_op_expr; *token_index += 1; - AstNode *node = ast_create_node(NodeTypeCastExpr, as_kw); + AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw); node->data.cast_expr.prefix_op_expr = prefix_op_expr; node->data.cast_expr.type = ast_parse_type(pc, *token_index, token_index); @@ -705,7 +716,7 @@ static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool man AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = mult_op; node->data.bin_op_expr.op2 = operand_2; @@ -753,7 +764,7 @@ static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mand AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = add_op; node->data.bin_op_expr.op2 = operand_2; @@ -801,7 +812,7 @@ static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, int *token_index, boo AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = bit_shift_op; node->data.bin_op_expr.op2 = operand_2; @@ -825,7 +836,7 @@ static AstNode *ast_parse_bin_and_expr(ParseContext *pc, int *token_index, bool AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = BinOpTypeBinAnd; node->data.bin_op_expr.op2 = operand_2; @@ -848,7 +859,7 @@ static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, int *token_index, bool AstNode *operand_2 = ast_parse_bin_and_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = BinOpTypeBinXor; node->data.bin_op_expr.op2 = operand_2; @@ -871,7 +882,7 @@ static AstNode *ast_parse_bin_or_expr(ParseContext *pc, int *token_index, bool m AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = BinOpTypeBinOr; node->data.bin_op_expr.op2 = operand_2; @@ -920,7 +931,7 @@ static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bo AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = cmp_op; node->data.bin_op_expr.op2 = operand_2; @@ -943,7 +954,7 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool AstNode *operand_2 = ast_parse_comparison_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd; node->data.bin_op_expr.op2 = operand_2; @@ -958,7 +969,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m Token *return_tok = &pc->tokens->at(*token_index); if (return_tok->id == TokenIdKeywordReturn) { *token_index += 1; - AstNode *node = ast_create_node(NodeTypeReturnExpr, return_tok); + AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, return_tok); node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); return node; } else if (mandatory) { @@ -983,7 +994,7 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool AstNode *operand_2 = ast_parse_bool_and_expr(pc, token_index, true); - AstNode *node = ast_create_node(NodeTypeBinOpExpr, token); + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = BinOpTypeBoolOr; node->data.bin_op_expr.op2 = operand_2; @@ -1046,7 +1057,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato } *token_index += 1; - AstNode *node = ast_create_node(NodeTypeBlock, l_brace); + AstNode *node = ast_create_node(pc, NodeTypeBlock, l_brace); for (;;) { Token *token = &pc->tokens->at(*token_index); @@ -1092,7 +1103,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand return nullptr; } - AstNode *node = ast_create_node(NodeTypeFnProto, token); + AstNode *node = ast_create_node(pc, NodeTypeFnProto, token); node->data.fn_proto.visib_mod = visib_mod; node->data.fn_proto.directives = pc->directive_list; pc->directive_list = nullptr; @@ -1125,7 +1136,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory); if (!fn_proto) return nullptr; - AstNode *node = ast_create_node_with_node(NodeTypeFnDef, fn_proto); + AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto); node->data.fn_def.fn_proto = fn_proto; node->data.fn_def.body = ast_parse_block(pc, token_index, true); @@ -1138,7 +1149,7 @@ FnDecl : FnProto token(Semicolon) */ static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_token_index) { AstNode *fn_proto = ast_parse_fn_proto(pc, &token_index, true); - AstNode *node = ast_create_node_with_node(NodeTypeFnDecl, fn_proto); + AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDecl, fn_proto); node->data.fn_decl.fn_proto = fn_proto; @@ -1166,7 +1177,7 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool } *token_index += 1; - AstNode *node = ast_create_node(NodeTypeExternBlock, extern_kw); + AstNode *node = ast_create_node(pc, NodeTypeExternBlock, extern_kw); node->data.extern_block.directives = pc->directive_list; pc->directive_list = nullptr; @@ -1184,7 +1195,7 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdRBrace) { if (pc->directive_list->length > 0) { - ast_error(directive_token, "invalid directive"); + ast_error(pc, directive_token, "invalid directive"); } pc->directive_list = nullptr; @@ -1216,7 +1227,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b *token_index += 2; - AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw); + AstNode *node = ast_create_node(pc, NodeTypeRootExportDecl, export_kw); node->data.root_export_decl.directives = pc->directive_list; pc->directive_list = nullptr; @@ -1254,7 +1265,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index, bool mandatory *token_index += 1; ast_expect_token(pc, semicolon, TokenIdSemicolon); - AstNode *node = ast_create_node(NodeTypeUse, use_kw); + AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); parse_string_literal(pc, use_name, &node->data.use.path); @@ -1299,7 +1310,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis } if (pc->directive_list->length > 0) { - ast_error(directive_token, "invalid directive"); + ast_error(pc, directive_token, "invalid directive"); } pc->directive_list = nullptr; @@ -1312,7 +1323,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis Root : many(TopLevelDecl) token(EOF) */ static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { - AstNode *node = ast_create_node(NodeTypeRoot, &pc->tokens->at(*token_index)); + AstNode *node = ast_create_node(pc, NodeTypeRoot, &pc->tokens->at(*token_index)); ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); @@ -1323,8 +1334,9 @@ static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { return node; } -AstNode *ast_parse(Buf *buf, ZigList *tokens) { +AstNode *ast_parse(Buf *buf, ZigList *tokens, ImportTableEntry *owner) { ParseContext pc = {0}; + pc.owner = owner; pc.buf = buf; pc.tokens = tokens; int token_index = 0; diff --git a/src/parser.hpp b/src/parser.hpp index cc7b3dd0b6..3346763acb 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -14,6 +14,7 @@ struct AstNode; struct CodeGenNode; +struct ImportTableEntry; enum NodeType { NodeTypeRoot, @@ -166,10 +167,10 @@ struct AstNodeUse { struct AstNode { enum NodeType type; - AstNode *parent; int line; int column; CodeGenNode *codegen_node; + ImportTableEntry *owner; union { AstNodeRoot root; AstNodeRootExportDecl root_export_decl; @@ -198,7 +199,7 @@ void ast_token_error(Token *token, const char *format, ...); // This function is provided by generated code, generated by parsergen.cpp -AstNode * ast_parse(Buf *buf, ZigList *tokens); +AstNode * ast_parse(Buf *buf, ZigList *tokens, ImportTableEntry *owner); const char *node_type_str(NodeType node_type); diff --git a/src/semantic_info.hpp b/src/semantic_info.hpp index 89ecfabea4..5a8ec35cb6 100644 --- a/src/semantic_info.hpp +++ b/src/semantic_info.hpp @@ -11,6 +11,7 @@ #include "codegen.hpp" #include "hash_map.hpp" #include "zig_llvm.hpp" +#include "errmsg.hpp" struct FnTableEntry; @@ -30,6 +31,8 @@ struct ImportTableEntry { AstNode *root; Buf *path; // relative to root_source_dir LLVMZigDIFile *di_file; + Buf *source_code; + ZigList *line_offsets; // reminder: hash tables must be initialized before use HashMap fn_table; @@ -47,7 +50,7 @@ struct FnTableEntry { struct CodeGen { LLVMModuleRef module; - ZigList errors; + ZigList errors; LLVMBuilderRef builder; LLVMZigDIBuilder *dbuilder; LLVMZigDICompileUnit *compile_unit; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 94f3966f0f..0a61bb9168 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -104,6 +104,7 @@ enum TokenizeState { TokenizeStateBang, TokenizeStateLessThan, TokenizeStateGreaterThan, + TokenizeStateError, }; @@ -116,27 +117,25 @@ struct Tokenize { int column; Token *cur_tok; int multi_line_comment_count; + Tokenization *out; }; __attribute__ ((format (printf, 2, 3))) static void tokenize_error(Tokenize *t, const char *format, ...) { - int line; - int column; + t->state = TokenizeStateError; + if (t->cur_tok) { - line = t->cur_tok->start_line + 1; - column = t->cur_tok->start_column + 1; + t->out->err_line = t->cur_tok->start_line; + t->out->err_column = t->cur_tok->start_column; } else { - line = t->line + 1; - column = t->column + 1; + t->out->err_line = t->line; + t->out->err_column = t->column; } va_list ap; va_start(ap, format); - fprintf(stderr, "Error: Line %d, column %d: ", line, column); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); + t->out->err = buf_vprintf(format, ap); va_end(ap); - exit(EXIT_FAILURE); } static void begin_token(Tokenize *t, TokenId id) { @@ -187,13 +186,20 @@ static void end_token(Tokenize *t) { t->cur_tok = nullptr; } -ZigList *tokenize(Buf *buf) { +void tokenize(Buf *buf, Tokenization *out) { Tokenize t = {0}; - t.tokens = allocate>(1); + t.out = out; + t.tokens = out->tokens = allocate>(1); t.buf = buf; + + out->line_offsets = allocate>(1); + + out->line_offsets->append(0); for (t.pos = 0; t.pos < buf_len(t.buf); t.pos += 1) { uint8_t c = buf_ptr(t.buf)[t.pos]; switch (t.state) { + case TokenizeStateError: + break; case TokenizeStateStart: switch (c) { case WHITESPACE: @@ -509,6 +515,7 @@ ZigList *tokenize(Buf *buf) { break; } if (c == '\n') { + out->line_offsets->append(t.pos + 1); t.line += 1; t.column = 0; } else { @@ -518,6 +525,7 @@ ZigList *tokenize(Buf *buf) { // EOF switch (t.state) { case TokenizeStateStart: + case TokenizeStateError: break; case TokenizeStateString: tokenize_error(&t, "unterminated string"); @@ -544,11 +552,12 @@ ZigList *tokenize(Buf *buf) { tokenize_error(&t, "unterminated multi-line comment"); break; } - t.pos = -1; - begin_token(&t, TokenIdEof); - end_token(&t); - assert(!t.cur_tok); - return t.tokens; + if (t.state != TokenizeStateError) { + t.pos = -1; + begin_token(&t, TokenIdEof); + end_token(&t); + assert(!t.cur_tok); + } } static const char * token_name(Token *token) { diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 94cbb15d43..45bc011e03 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -65,7 +65,17 @@ struct Token { int start_column; }; -ZigList *tokenize(Buf *buf); +struct Tokenization { + ZigList *tokens; + ZigList *line_offsets; + + // if an error occurred + Buf *err; + int err_line; + int err_column; +}; + +void tokenize(Buf *buf, Tokenization *out_tokenization); void print_tokens(Buf *buf, ZigList *tokens); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index b54f0bd4c8..34a1668675 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -139,7 +139,7 @@ static void add_compile_failure_test_cases(void) { add_compile_fail_case("multiple function definitions", R"SOURCE( fn a() {} fn a() {} - )SOURCE", 1, "Line 3, column 1: redefinition of 'a'"); + )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'a'"); add_compile_fail_case("bad directive", R"SOURCE( #bogus1("") @@ -148,37 +148,37 @@ extern { } #bogus2("") fn a() {} - )SOURCE", 2, "Line 2, column 1: invalid directive: 'bogus1'", - "Line 6, column 1: invalid directive: 'bogus2'"); + )SOURCE", 2, ".tmp_source.zig:2:1: error: invalid directive: 'bogus1'", + ".tmp_source.zig:6:1: error: invalid directive: 'bogus2'"); add_compile_fail_case("unreachable with return", R"SOURCE( fn a() -> unreachable {return;} - )SOURCE", 1, "Line 2, column 24: return statement in function with unreachable return type"); + )SOURCE", 1, ".tmp_source.zig:2:24: error: return statement in function with unreachable return type"); add_compile_fail_case("control reaches end of non-void function", R"SOURCE( fn a() -> i32 {} - )SOURCE", 1, "Line 2, column 1: control reaches end of non-void function"); + )SOURCE", 1, ".tmp_source.zig:2:1: error: control reaches end of non-void function"); add_compile_fail_case("undefined function call", R"SOURCE( fn a() { b(); } - )SOURCE", 1, "Line 3, column 5: undefined function: 'b'"); + )SOURCE", 1, ".tmp_source.zig:3:5: error: undefined function: 'b'"); add_compile_fail_case("wrong number of arguments", R"SOURCE( fn a() { b(1); } fn b(a: i32, b: i32, c: i32) { } - )SOURCE", 1, "Line 3, column 5: wrong number of arguments. Expected 3, got 1."); + )SOURCE", 1, ".tmp_source.zig:3:5: error: wrong number of arguments. Expected 3, got 1."); add_compile_fail_case("invalid type", R"SOURCE( fn a() -> bogus {} - )SOURCE", 1, "Line 2, column 11: invalid type name: 'bogus'"); + )SOURCE", 1, ".tmp_source.zig:2:11: error: invalid type name: 'bogus'"); add_compile_fail_case("pointer to unreachable", R"SOURCE( fn a() -> *mut unreachable {} - )SOURCE", 1, "Line 2, column 11: pointer to unreachable not allowed"); + )SOURCE", 1, ".tmp_source.zig:2:11: error: pointer to unreachable not allowed"); add_compile_fail_case("unreachable code", R"SOURCE( fn a() { @@ -187,12 +187,12 @@ fn a() { } fn b() {} - )SOURCE", 1, "Line 4, column 5: unreachable code"); + )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); add_compile_fail_case("bad version string", R"SOURCE( #version("aoeu") export executable "test"; - )SOURCE", 1, "Line 2, column 1: invalid version string"); + )SOURCE", 1, ".tmp_source.zig:2:1: error: invalid version string"); } static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { -- cgit v1.2.3 From 58e375d0a1423c04f4d3faabe4d84bfc11028d56 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Dec 2015 02:08:58 -0700 Subject: support multiple files --- README.md | 1 - example/multiple_files/foo.zig | 2 +- example/multiple_files/libc.zig | 4 +- example/multiple_files/main.zig | 2 +- src/analyze.cpp | 103 ++++++++++++++++++++++++++++------------ src/codegen.cpp | 24 +++++----- src/semantic_info.hpp | 7 +++ test/run_tests.cpp | 78 +++++++++++++++++++++++++++--- 8 files changed, 168 insertions(+), 53 deletions(-) (limited to 'src/analyze.cpp') diff --git a/README.md b/README.md index 6311010156..fd3396620a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ make ## Roadmap * variable declarations and assignment expressions - * Multiple files * Type checking * inline assembly and syscalls * running code at compile time diff --git a/example/multiple_files/foo.zig b/example/multiple_files/foo.zig index a42e58397c..021bf71e5b 100644 --- a/example/multiple_files/foo.zig +++ b/example/multiple_files/foo.zig @@ -6,6 +6,6 @@ fn private_function() { puts("it works!"); } -fn print_text() { +pub fn print_text() { private_function(); } diff --git a/example/multiple_files/libc.zig b/example/multiple_files/libc.zig index 7d1a5bebd9..19c1106fd6 100644 --- a/example/multiple_files/libc.zig +++ b/example/multiple_files/libc.zig @@ -1,5 +1,5 @@ #link("c") extern { - fn puts(s: *mut u8) -> i32; - fn exit(code: i32) -> unreachable; + pub fn puts(s: *mut u8) -> i32; + pub fn exit(code: i32) -> unreachable; } diff --git a/example/multiple_files/main.zig b/example/multiple_files/main.zig index 9bc489e925..c7cb2412e2 100644 --- a/example/multiple_files/main.zig +++ b/example/multiple_files/main.zig @@ -3,7 +3,7 @@ export executable "test"; use "libc.zig"; use "foo.zig"; -fn _start() -> unreachable { +export fn _start() -> unreachable { private_function(); } diff --git a/src/analyze.cpp b/src/analyze.cpp index fccbad8bb7..d7d5aeb5b0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -137,6 +137,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); assert(fn_decl->type == NodeTypeFnDecl); AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto; + bool is_pub = (fn_proto->data.fn_proto.visib_mod == FnProtoVisibModPub); resolve_function_proto(g, fn_proto); Buf *name = &fn_proto->data.fn_proto.name; @@ -145,7 +146,12 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, fn_table_entry->is_extern = true; fn_table_entry->calling_convention = LLVMCCallConv; fn_table_entry->import_entry = import; - g->fn_table.put(name, fn_table_entry); + + g->fn_protos.append(fn_table_entry); + import->fn_table.put(name, fn_table_entry); + if (is_pub) { + g->fn_table.put(name, fn_table_entry); + } } break; case NodeTypeFnDef: @@ -153,27 +159,44 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *proto_node = node->data.fn_def.fn_proto; assert(proto_node->type == NodeTypeFnProto); Buf *proto_name = &proto_node->data.fn_proto.name; - auto entry = g->fn_table.maybe_get(proto_name); + auto entry = import->fn_table.maybe_get(proto_name); + bool skip = false; + bool is_internal = (proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport); + bool is_pub = (proto_node->data.fn_proto.visib_mod == FnProtoVisibModPub); if (entry) { add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); assert(!node->codegen_node); node->codegen_node = allocate(1); node->codegen_node->data.fn_def_node.skip = true; - } else { + skip = true; + } else if (is_pub) { + auto entry = g->fn_table.maybe_get(proto_name); + if (entry) { + add_node_error(g, node, + buf_sprintf("redefinition of '%s'", buf_ptr(proto_name))); + assert(!node->codegen_node); + node->codegen_node = allocate(1); + node->codegen_node->data.fn_def_node.skip = true; + skip = true; + } + } + if (!skip) { FnTableEntry *fn_table_entry = allocate(1); fn_table_entry->import_entry = import; fn_table_entry->proto_node = proto_node; fn_table_entry->fn_def_node = node; - fn_table_entry->internal_linkage = proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport; - if (fn_table_entry->internal_linkage) { - fn_table_entry->calling_convention = LLVMFastCallConv; - } else { - fn_table_entry->calling_convention = LLVMCCallConv; - } - g->fn_table.put(proto_name, fn_table_entry); + fn_table_entry->internal_linkage = is_internal; + fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; + + g->fn_protos.append(fn_table_entry); g->fn_defs.append(fn_table_entry); + import->fn_table.put(proto_name, fn_table_entry); + if (is_pub) { + g->fn_table.put(proto_name, fn_table_entry); + } + resolve_function_proto(g, proto_node); } } @@ -297,28 +320,31 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) { } } -static void analyze_expression(CodeGen *g, AstNode *node) { +static void analyze_expression(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeBlock: for (int i = 0; i < node->data.block.statements.length; i += 1) { AstNode *child = node->data.block.statements.at(i); - analyze_expression(g, child); + analyze_expression(g, import, child); } break; case NodeTypeReturnExpr: if (node->data.return_expr.expr) { - analyze_expression(g, node->data.return_expr.expr); + analyze_expression(g, import, node->data.return_expr.expr); } break; case NodeTypeBinOpExpr: - analyze_expression(g, node->data.bin_op_expr.op1); - analyze_expression(g, node->data.bin_op_expr.op2); + analyze_expression(g, import, node->data.bin_op_expr.op1); + analyze_expression(g, import, node->data.bin_op_expr.op2); break; case NodeTypeFnCallExpr: { Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); - auto entry = g->fn_table.maybe_get(name); + auto entry = import->fn_table.maybe_get(name); + if (!entry) + entry = g->fn_table.maybe_get(name); + if (!entry) { add_node_error(g, node, buf_sprintf("undefined function: '%s'", buf_ptr(name))); @@ -336,7 +362,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) { AstNode *child = node->data.fn_call_expr.params.at(i); - analyze_expression(g, child); + analyze_expression(g, import, child); } break; } @@ -366,7 +392,7 @@ static void analyze_expression(CodeGen *g, AstNode *node) { } } -static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { +static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { case NodeTypeFnDef: { @@ -387,7 +413,7 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { } check_fn_def_control_flow(g, node); - analyze_expression(g, node->data.fn_def.body); + analyze_expression(g, import, node->data.fn_def.body); } break; @@ -423,33 +449,50 @@ static void analyze_top_level_declaration(CodeGen *g, AstNode *node) { } } -static void analyze_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { +static void find_function_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { assert(node->type == NodeTypeRoot); - // find function declarations for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); preview_function_declarations(g, import, child); } +} + +static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { + assert(node->type == NodeTypeRoot); + for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); - analyze_top_level_declaration(g, child); + analyze_top_level_declaration(g, import, child); } - } void semantic_analyze(CodeGen *g) { - auto it = g->import_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; + { + auto it = g->import_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; - ImportTableEntry *import = entry->value; - analyze_root(g, import, import->root); + ImportTableEntry *import = entry->value; + find_function_declarations_root(g, import, import->root); + } + } + { + auto it = g->import_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + ImportTableEntry *import = entry->value; + analyze_top_level_decls_root(g, import, import->root); + } } + if (!g->root_out_name) { add_node_error(g, g->root_import->root, buf_sprintf("missing export declaration and output name not provided")); diff --git a/src/codegen.cpp b/src/codegen.cpp index 2f9bf47383..5ce95023e1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -125,7 +125,13 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { Buf *name = hack_get_fn_call_name(g, node->data.fn_call_expr.fn_ref_expr); - FnTableEntry *fn_table_entry = g->fn_table.get(name); + FnTableEntry *fn_table_entry; + auto entry = g->cur_fn->import_entry->fn_table.maybe_get(name); + if (entry) + fn_table_entry = entry->value; + else + fn_table_entry = g->fn_table.get(name); + assert(fn_table_entry->proto_node->type == NodeTypeFnProto); int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length; int actual_param_count = node->data.fn_call_expr.params.length; @@ -478,13 +484,8 @@ static void do_code_gen(CodeGen *g) { // Generate function prototypes - auto it = g->fn_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - FnTableEntry *fn_table_entry = entry->value; + for (int i = 0; i < g->fn_protos.length; i += 1) { + FnTableEntry *fn_table_entry = g->fn_protos.at(i); AstNode *proto_node = fn_table_entry->proto_node; assert(proto_node->type == NodeTypeFnProto); @@ -547,6 +548,7 @@ static void do_code_gen(CodeGen *g) { assert(codegen_node); FnDefNode *codegen_fn_def = &codegen_node->data.fn_def_node; + assert(codegen_fn_def); codegen_fn_def->params = allocate(LLVMCountParams(fn)); LLVMGetParams(fn, codegen_fn_def->params); @@ -733,9 +735,9 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou if (!entry) { Buf full_path = BUF_INIT; os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); - Buf import_code = BUF_INIT; - os_fetch_file_path(&full_path, &import_code); - codegen_add_code(g, &top_level_decl->data.use.path, &import_code); + Buf *import_code = buf_alloc(); + os_fetch_file_path(&full_path, import_code); + codegen_add_code(g, &top_level_decl->data.use.path, import_code); } } diff --git a/src/semantic_info.hpp b/src/semantic_info.hpp index 075b1c8bdc..f0a79d685d 100644 --- a/src/semantic_info.hpp +++ b/src/semantic_info.hpp @@ -80,7 +80,14 @@ struct CodeGen { Buf *root_source_dir; Buf *root_out_name; ZigList block_scopes; + + // The function definitions this module includes. There must be a corresponding + // fn_protos entry. ZigList fn_defs; + // The function prototypes this module includes. In the case of external declarations, + // there will not be a corresponding fn_defs entry. + ZigList fn_protos; + OutType out_type; FnTableEntry *cur_fn; bool c_stdint_used; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 69a99590ce..7597ba761a 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -14,13 +14,13 @@ struct TestSourceFile { const char *relative_path; - const char *text; + const char *source_code; }; struct TestCase { const char *case_name; const char *output; - const char *source; + ZigList source_files; ZigList compile_errors; ZigList compiler_args; ZigList program_args; @@ -31,11 +31,20 @@ static const char *tmp_source_path = ".tmp_source.zig"; static const char *tmp_exe_path = "./.tmp_exe"; static const char *zig_exe = "./zig"; -static void add_simple_case(const char *case_name, const char *source, const char *output) { +static void add_source_file(TestCase *test_case, const char *path, const char *source) { + test_case->source_files.add_one(); + test_case->source_files.last().relative_path = path; + test_case->source_files.last().source_code = source; +} + +static TestCase *add_simple_case(const char *case_name, const char *source, const char *output) { TestCase *test_case = allocate(1); test_case->case_name = case_name; test_case->output = output; - test_case->source = source; + + test_case->source_files.resize(1); + test_case->source_files.at(0).relative_path = tmp_source_path; + test_case->source_files.at(0).source_code = source; test_case->compiler_args.append("build"); test_case->compiler_args.append(tmp_source_path); @@ -52,15 +61,19 @@ static void add_simple_case(const char *case_name, const char *source, const cha test_case->compiler_args.append("on"); test_cases.append(test_case); + + return test_case; } -static void add_compile_fail_case(const char *case_name, const char *source, int count, ...) { +static TestCase *add_compile_fail_case(const char *case_name, const char *source, int count, ...) { va_list ap; va_start(ap, count); TestCase *test_case = allocate(1); test_case->case_name = case_name; - test_case->source = source; + test_case->source_files.resize(1); + test_case->source_files.at(0).relative_path = tmp_source_path; + test_case->source_files.at(0).source_code = source; for (int i = 0; i < count; i += 1) { const char *arg = va_arg(ap, const char *); @@ -78,6 +91,8 @@ static void add_compile_fail_case(const char *case_name, const char *source, int test_cases.append(test_case); va_end(ap); + + return test_case; } static void add_compiling_test_cases(void) { @@ -135,6 +150,45 @@ static void add_compiling_test_cases(void) { exit(0); } )SOURCE", "OK\n"); + + { + TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE( + use "libc.zig"; + use "foo.zig"; + + export fn _start() -> unreachable { + private_function(); + } + + fn private_function() -> unreachable { + print_text(); + exit(0); + } + )SOURCE", "OK\n"); + + add_source_file(tc, "libc.zig", R"SOURCE( + #link("c") + extern { + pub fn puts(s: *mut u8) -> i32; + pub fn exit(code: i32) -> unreachable; + } + )SOURCE"); + + add_source_file(tc, "foo.zig", R"SOURCE( + use "libc.zig"; + + // purposefully conflicting function with main source file + // but it's private so it should be OK + fn private_function() { + puts("OK"); + } + + pub fn print_text() { + private_function(); + } + )SOURCE"); + } + } static void add_compile_failure_test_cases(void) { @@ -207,7 +261,12 @@ static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { } static void run_test(TestCase *test_case) { - os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source)); + for (int i = 0; i < test_case->source_files.length; i += 1) { + TestSourceFile *test_source = &test_case->source_files.at(i); + os_write_file( + buf_create_from_str(test_source->relative_path), + buf_create_from_str(test_source->source_code)); + } Buf zig_stderr = BUF_INIT; Buf zig_stdout = BUF_INIT; @@ -265,6 +324,11 @@ static void run_test(TestCase *test_case) { printf("=======================================\n"); exit(1); } + + for (int i = 0; i < test_case->source_files.length; i += 1) { + TestSourceFile *test_source = &test_case->source_files.at(i); + remove(test_source->relative_path); + } } static void run_all_tests(void) { -- cgit v1.2.3 From dfb6682089ad758b7ba72733778a9aa8c544c164 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 1 Dec 2015 02:29:21 -0700 Subject: add test for bad import --- example/multiple_files/main.zig | 2 +- src/analyze.cpp | 2 +- src/analyze.hpp | 3 +++ src/codegen.cpp | 7 +++++- src/error.cpp | 6 +++++ src/error.hpp | 6 +++++ src/os.cpp | 56 +++++++++++++++++++++++++++++++++++------ src/util.hpp | 2 -- test/run_tests.cpp | 4 +++ 9 files changed, 75 insertions(+), 13 deletions(-) (limited to 'src/analyze.cpp') diff --git a/example/multiple_files/main.zig b/example/multiple_files/main.zig index c7cb2412e2..f5332316ad 100644 --- a/example/multiple_files/main.zig +++ b/example/multiple_files/main.zig @@ -1,4 +1,4 @@ -export executable "test"; +export executable "test-multiple-files"; use "libc.zig"; use "foo.zig"; diff --git a/src/analyze.cpp b/src/analyze.cpp index d7d5aeb5b0..5239835f0f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -11,7 +11,7 @@ #include "zig_llvm.hpp" #include "os.hpp" -static void add_node_error(CodeGen *g, AstNode *node, Buf *msg) { +void add_node_error(CodeGen *g, AstNode *node, Buf *msg) { ErrorMsg *err = allocate(1); err->line_start = node->line; err->column_start = node->column; diff --git a/src/analyze.hpp b/src/analyze.hpp index 0dca23194d..110a3614e0 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -9,7 +9,10 @@ #define ZIG_ANALYZE_HPP struct CodeGen; +struct AstNode; +struct Buf; void semantic_analyze(CodeGen *g); +void add_node_error(CodeGen *g, AstNode *node, Buf *msg); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 5ce95023e1..baae91a206 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -669,6 +669,7 @@ static void init(CodeGen *g, Buf *source_path) { } static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *source_code) { + int err; Buf full_path = BUF_INIT; os_path_join(g->root_source_dir, source_path, &full_path); @@ -736,7 +737,11 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *source_path, Buf *sou Buf full_path = BUF_INIT; os_path_join(g->root_source_dir, &top_level_decl->data.use.path, &full_path); Buf *import_code = buf_alloc(); - os_fetch_file_path(&full_path, import_code); + if ((err = os_fetch_file_path(&full_path, import_code))) { + add_node_error(g, top_level_decl, + buf_sprintf("unable to open \"%s\": %s", buf_ptr(&full_path), err_str(err))); + break; + } codegen_add_code(g, &top_level_decl->data.use.path, import_code); } } diff --git a/src/error.cpp b/src/error.cpp index 7c3064c143..7690dd0776 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -6,6 +6,12 @@ const char *err_str(int err) { case ErrorNoMem: return "out of memory"; case ErrorInvalidFormat: return "invalid format"; case ErrorSemanticAnalyzeFail: return "semantic analyze failed"; + case ErrorAccess: return "access denied"; + case ErrorInterrupted: return "interrupted"; + case ErrorSystemResources: return "lack of system resources"; + case ErrorFileNotFound: return "file not found"; + case ErrorFileSystem: return "file system error"; + case ErrorFileTooBig: return "file too big"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 7da2cf8322..742f30700e 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -13,6 +13,12 @@ enum Error { ErrorNoMem, ErrorInvalidFormat, ErrorSemanticAnalyzeFail, + ErrorAccess, + ErrorInterrupted, + ErrorSystemResources, + ErrorFileNotFound, + ErrorFileSystem, + ErrorFileTooBig, }; const char *err_str(int err); diff --git a/src/os.cpp b/src/os.cpp index bfc8eaa776..be0b01f8a7 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -7,6 +7,7 @@ #include "os.hpp" #include "util.hpp" +#include "error.hpp" #include #include @@ -143,26 +144,65 @@ void os_write_file(Buf *full_path, Buf *contents) { int os_fetch_file(FILE *f, Buf *out_contents) { int fd = fileno(f); struct stat st; - if (fstat(fd, &st)) - zig_panic("unable to stat file: %s", strerror(errno)); + if (fstat(fd, &st)) { + switch (errno) { + case EACCES: + return ErrorAccess; + case ENOENT: + return ErrorFileNotFound; + case ENOMEM: + return ErrorSystemResources; + case EINTR: + return ErrorInterrupted; + case EINVAL: + zig_unreachable(); + default: + return ErrorFileSystem; + } + } off_t big_size = st.st_size; - if (big_size > INT_MAX) - zig_panic("file too big"); + if (big_size > INT_MAX) { + return ErrorFileTooBig; + } int size = (int)big_size; buf_resize(out_contents, size); ssize_t ret = read(fd, buf_ptr(out_contents), size); - if (ret != size) - zig_panic("unable to read file: %s", strerror(errno)); + if (ret != size) { + switch (errno) { + case EINTR: + return ErrorInterrupted; + case EINVAL: + case EISDIR: + zig_unreachable(); + default: + return ErrorFileSystem; + } + } return 0; } int os_fetch_file_path(Buf *full_path, Buf *out_contents) { FILE *f = fopen(buf_ptr(full_path), "rb"); - if (!f) - zig_panic("unable to open %s: %s\n", buf_ptr(full_path), strerror(errno)); + if (!f) { + switch (errno) { + case EACCES: + return ErrorAccess; + case EINTR: + return ErrorInterrupted; + case EINVAL: + zig_unreachable(); + case ENFILE: + case ENOMEM: + return ErrorSystemResources; + case ENOENT: + return ErrorFileNotFound; + default: + return ErrorFileSystem; + } + } int result = os_fetch_file(f, out_contents); fclose(f); return result; diff --git a/src/util.hpp b/src/util.hpp index b3180528dd..74fcf85020 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -16,8 +16,6 @@ #define BREAKPOINT __asm("int $0x03") -static const int COMPILE_FAILED_ERR_CODE = 10; // chosen with a random number generator - void zig_panic(const char *format, ...) __attribute__((cold)) __attribute__ ((noreturn)) diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 7597ba761a..979ad38997 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -249,6 +249,10 @@ fn b() {} #version("aoeu") export executable "test"; )SOURCE", 1, ".tmp_source.zig:2:1: error: invalid version string"); + + add_compile_fail_case("bad import", R"SOURCE( +use "bogus-does-not-exist.zig"; + )SOURCE", 1, ".tmp_source.zig:2:1: error: unable to open \"./bogus-does-not-exist.zig\": file not found"); } static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { -- cgit v1.2.3