From 60ef4fda4dd71e5474bb2598c4e0d18c58924e99 Mon Sep 17 00:00:00 2001 From: joakin Date: Wed, 3 Apr 2024 14:03:56 +0200 Subject: Recognize dynamic library names like libraylib.so.5.0.0 --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index f4d3dc48d..2bf25c768 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5710,7 +5710,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node // working directory of the exe to the library search paths. // Static libraries can be linked directly with the full pathname // - if (node->kind == Ast_ForeignImportDecl && string_ends_with(file_str, str_lit(".so"))) { + if (node->kind == Ast_ForeignImportDecl && (string_ends_with(file_str, str_lit(".so")) || string_contains_string(file_str, str_lit(".so.")))) { *path = file_str; return true; } -- cgit v1.2.3 From 0da6a3e214c66e2955307e2aad12d844a051f8d8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 7 May 2024 11:42:48 +0100 Subject: Fix #3530 --- src/check_type.cpp | 9 +++++++-- src/parser.cpp | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 457375779..6efac54d6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -797,11 +797,11 @@ gb_internal void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *nam enum_type->Enum.scope = ctx->scope; Type *base_type = t_int; - if (et->base_type != nullptr) { + if (unparen_expr(et->base_type) != nullptr) { base_type = check_type(ctx, et->base_type); } - if (base_type == nullptr || !is_type_integer(base_type)) { + if (base_type == nullptr || base_type == t_invalid || !is_type_integer(base_type)) { error(node, "Base type for enumeration must be an integer"); return; } @@ -3265,6 +3265,11 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_end; case_ast_node(pe, ParenExpr, e); + if (pe->expr == nullptr) { + error(e, "Expected an expression or type within the parentheses"); + *type = t_invalid; + return true; + } *type = check_type_expr(ctx, pe->expr, named_type); set_base_type(named_type, *type); return true; diff --git a/src/parser.cpp b/src/parser.cpp index 2bf25c768..04505cbd7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3499,6 +3499,10 @@ gb_internal Ast *parse_type(AstFile *f) { Token token = advance_token(f); syntax_error(token, "Expected a type"); return ast_bad_expr(f, token, f->curr_token); + } else if (type->kind == Ast_ParenExpr && + unparen_expr(type) == nullptr) { + syntax_error(type, "Expected a type within the parentheses"); + return ast_bad_expr(f, type->ParenExpr.open, type->ParenExpr.close); } return type; } -- cgit v1.2.3 From f54977336b27c32eab52b77d94e7b1610f4350cf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 9 May 2024 15:56:00 +0100 Subject: With `-vet-style`, give suggestion of separating where clauses with a comma rather than '&&' This improves the error messages --- src/check_expr.cpp | 14 ++++++++++++++ src/check_type.cpp | 2 +- src/parser.cpp | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 013638e63..98aebfe4e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6193,6 +6193,20 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco } return false; } + + if (ast_file_vet_style(ctx->file)) { + Ast *c = unparen_expr(clause); + if (c->kind == Ast_BinaryExpr && c->BinaryExpr.op.kind == Token_CmpAnd) { + ERROR_BLOCK(); + error(c, "Prefer to separate 'where' clauses with a comma rather than '&&'"); + gbString x = expr_to_string(c->BinaryExpr.left); + gbString y = expr_to_string(c->BinaryExpr.right); + error_line("\tSuggestion: '%s, %s'", x, y); + gb_string_free(y); + gb_string_free(x); + } + } + } } diff --git a/src/check_type.cpp b/src/check_type.cpp index 3d11b5012..11e332757 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1166,7 +1166,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, } } if (all_ones && all_booleans) { - if (build_context.vet_flags & VetFlag_Style) { + if (ast_file_vet_style(ctx->file)) { char const *msg = "This 'bit_field' is better expressed as a 'bit_set' since all of the fields are booleans, of 1-bit in size, and the backing type is an integer (-vet-style)"; error(node, msg); } else { diff --git a/src/parser.cpp b/src/parser.cpp index 04505cbd7..6e859fe32 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,7 +1,7 @@ #include "parser_pos.cpp" gb_internal u64 ast_file_vet_flags(AstFile *f) { - if (f->vet_flags_set) { + if (f != nullptr && f->vet_flags_set) { return f->vet_flags; } return build_context.vet_flags; -- cgit v1.2.3 From 710bb4369f45ee6dfa8b66e67e25d91a15000a65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 10 May 2024 13:55:15 +0100 Subject: Fix #3567 --- src/parser.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 6e859fe32..ee3c56daf 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3883,10 +3883,12 @@ gb_internal Ast *parse_proc_type(AstFile *f, Token proc_token) { expect_token(f, Token_OpenParen); + f->expr_level += 1; params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true); if (file_allow_newline(f)) { skip_possible_newline(f); } + f->expr_level -= 1; expect_token_after(f, Token_CloseParen, "parameter list"); results = parse_results(f, &diverging); -- cgit v1.2.3 From 1935811b2c6118e633dd5c4cef2e9e70f7b962e5 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 12 May 2024 19:51:19 -0400 Subject: Suggest `-all-packages` if testing empty directory --- src/parser.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index ee3c56daf..ad962f53e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5508,11 +5508,15 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const } } if (files_with_ext == 0 || files_to_reserve == 1) { + ERROR_BLOCK(); if (files_with_ext != 0) { syntax_error(pos, "Directory contains no .odin files for the specified platform: %.*s", LIT(rel_path)); } else { syntax_error(pos, "Empty directory that contains no .odin files: %.*s", LIT(rel_path)); } + if (build_context.command_kind == Command_test) { + error_line("\tSuggestion: Make an .odin file that imports packages to test and use the `-all-packages` flag."); + } return nullptr; } -- cgit v1.2.3 From 215ef3d985724fa0ef7092fa8f672cc502db87be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 13:26:47 +0100 Subject: Make `core:runtime` etc a warning, and an error with `-vet` --- src/build_settings.cpp | 5 ++++- src/parser.cpp | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c6ef33af2..ec7d03a84 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -730,10 +730,11 @@ enum VetFlags : u64 { VetFlag_Semicolon = 1u<<4, VetFlag_UnusedVariables = 1u<<5, VetFlag_UnusedImports = 1u<<6, + VetFlag_Deprecated = 1u<<7, VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, - VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt, + VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated, VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam, }; @@ -755,6 +756,8 @@ u64 get_vet_flag_from_name(String const &name) { return VetFlag_Style; } else if (name == "semicolon") { return VetFlag_Semicolon; + } else if (name == "deprecated") { + return VetFlag_Deprecated; } return VetFlag_NONE; } diff --git a/src/parser.cpp b/src/parser.cpp index ad962f53e..66e175765 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -11,6 +11,9 @@ gb_internal bool ast_file_vet_style(AstFile *f) { return (ast_file_vet_flags(f) & VetFlag_Style) != 0; } +gb_internal bool ast_file_vet_deprecated(AstFile *f) { + return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0; +} gb_internal bool file_allow_newline(AstFile *f) { bool is_strict = build_context.strict_style || ast_file_vet_style(f); @@ -5694,8 +5697,26 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node if (collection_name.len > 0) { // NOTE(bill): `base:runtime` == `core:runtime` - if (collection_name == "core" && string_starts_with(file_str, str_lit("runtime"))) { - collection_name = str_lit("base"); + if (collection_name == "core") { + bool replace_with_base = false; + if (string_starts_with(file_str, str_lit("runtime"))) { + replace_with_base = true; + } else if (string_starts_with(file_str, str_lit("intrinsics"))) { + replace_with_base = true; + } if (string_starts_with(file_str, str_lit("builtin"))) { + replace_with_base = true; + } + + if (replace_with_base) { + collection_name = str_lit("base"); + } + if (replace_with_base) { + if (ast_file_vet_deprecated(node->file())) { + syntax_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + } else { + syntax_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + } + } } if (collection_name == "system") { -- cgit v1.2.3 From 0cf9dcd31441a56856a60e4f73db4b005a1407ee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 18:15:29 +0100 Subject: Make `..` ranges a complete error rather than a warning now. This should have been an error years ago. --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 66e175765..e296e6935 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1572,7 +1572,7 @@ gb_internal Token expect_operator(AstFile *f) { LIT(p)); } if (prev.kind == Token_Ellipsis) { - syntax_warning(prev, "'..' for ranges has now been deprecated, prefer '..='"); + syntax_error(prev, "'..' for ranges are not allowed, did you mean '..<' or '..='?"); f->tokens[f->curr_token_index].flags |= TokenFlag_Replace; } -- cgit v1.2.3 From 542c3d7561df82486310f355ca84a88fa5af3f9f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 20 May 2024 13:32:16 +0100 Subject: Improve "Expected a type" syntax error --- src/parser.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index e296e6935..5aa11b5d0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3499,8 +3499,14 @@ gb_internal Array parse_ident_list(AstFile *f, bool allow_poly_names) { gb_internal Ast *parse_type(AstFile *f) { Ast *type = parse_type_or_ident(f); if (type == nullptr) { - Token token = advance_token(f); - syntax_error(token, "Expected a type"); + Token prev_token = f->curr_token; + Token token = {}; + if (f->curr_token.kind == Token_OpenBrace) { + token = f->curr_token; + } else { + token = advance_token(f); + } + syntax_error(token, "Expected a type, got '%.*s'", LIT(prev_token.string)); return ast_bad_expr(f, token, f->curr_token); } else if (type->kind == Ast_ParenExpr && unparen_expr(type) == nullptr) { -- cgit v1.2.3 From 38fffff06a76004a02bedc34172aa5107784c03f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 27 May 2024 23:51:43 +0100 Subject: Begin moving `foreign import` import paths to be evaluated in the semantic phase rather than parsing. --- src/checker.cpp | 39 +++++++++++++++++++++++++++++++- src/parser.cpp | 65 ++++++++++++++++++++++++------------------------------ src/parser.hpp | 2 +- src/parser_pos.cpp | 2 +- 4 files changed, 69 insertions(+), 39 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 9d44c34dc..4e5aed2bf 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4883,7 +4883,44 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { Scope *parent_scope = ctx->scope; GB_ASSERT(parent_scope->flags&ScopeFlag_File); - GB_ASSERT(fl->fullpaths.count > 0); + String base_dir = dir_from_path(decl->file()->fullpath); + + auto fullpaths = array_make(permanent_allocator(), 0, fl->filepaths.count); + + for (Ast *fp_node : fl->filepaths) { + Operand op = {}; + check_expr(ctx, &op, fp_node); + if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) { + gbString s = expr_to_string(op.expr); + error(fp_node, "Expected a constant string value, got '%s'", s); + gb_string_free(s); + continue; + } + if (!is_type_string(op.type)) { + gbString s = type_to_string(op.type); + error(fp_node, "Expected a constant string value, got value of type '%s'", s); + gb_string_free(s); + continue; + } + + String file_str = op.value.value_string; + file_str = string_trim_whitespace(file_str); + + String fullpath = file_str; + if (allow_check_foreign_filepath()) { + String foreign_path = {}; + bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path); + gb_unused(ok); + fullpath = foreign_path; + } + array_add(&fullpaths, fullpath); + } + fl->fullpaths = slice_from_array(fullpaths); + + + if (fl->fullpaths.count == 0) { + return; + } String fullpath = fl->fullpaths[0]; String library_name = path_to_entity_name(fl->library_name.string, fullpath); if (is_blank_ident(library_name)) { diff --git a/src/parser.cpp b/src/parser.cpp index 5aa11b5d0..be0d68177 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1284,7 +1284,7 @@ gb_internal Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token i return result; } -gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, Token library_name, +gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, Token library_name, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl); result->ForeignImportDecl.token = token; @@ -4882,14 +4882,14 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { if (is_blank_ident(lib_name)) { syntax_error(lib_name, "Illegal foreign import name: '_'"); } - Array filepaths = {}; + Array filepaths = {}; if (allow_token(f, Token_OpenBrace)) { array_init(&filepaths, ast_allocator(f)); while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { - Token path = expect_token(f, Token_String); + Ast *path = parse_expr(f, true); array_add(&filepaths, path); if (!allow_field_separator(f)) { @@ -4898,9 +4898,10 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { } expect_closing_brace_of_field_list(f); } else { - filepaths = array_make(ast_allocator(f), 0, 1); + filepaths = array_make(ast_allocator(f), 0, 1); Token path = expect_token(f, Token_String); - array_add(&filepaths, path); + Ast *lit = ast_basic_lit(f, path); + array_add(&filepaths, lit); } Ast *s = nullptr; @@ -4909,7 +4910,7 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { s = ast_bad_decl(f, lib_name, f->curr_token); } else if (f->curr_proc != nullptr) { syntax_error(lib_name, "You cannot use foreign import within a procedure. This must be done at the file scope"); - s = ast_bad_decl(f, lib_name, filepaths[0]); + s = ast_bad_decl(f, lib_name, ast_token(filepaths[0])); } else { s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment); } @@ -5648,9 +5649,19 @@ gb_internal bool is_package_name_reserved(String const &name) { } -gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path) { +gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path, bool use_check_errors=false) { GB_ASSERT(path != nullptr); + void (*do_error)(Ast *, char const *, ...); + void (*do_warning)(Token const &, char const *, ...); + + do_error = &syntax_error; + do_warning = &syntax_warning; + if (use_check_errors) { + do_error = &error; + do_error = &warning; + } + // NOTE(bill): if file_mutex == nullptr, this means that the code is used within the semantics stage String collection_name = {}; @@ -5677,7 +5688,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node String file_str = {}; if (colon_pos == 0) { - syntax_error(node, "Expected a collection name"); + do_error(node, "Expected a collection name"); return false; } @@ -5692,11 +5703,11 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node if (has_windows_drive) { String sub_file_path = substring(file_str, 3, file_str.len); if (!is_import_path_valid(sub_file_path)) { - syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); + do_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } } else if (!is_import_path_valid(file_str)) { - syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); + do_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } @@ -5718,16 +5729,16 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node } if (replace_with_base) { if (ast_file_vet_deprecated(node->file())) { - syntax_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + do_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); } else { - syntax_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + do_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); } } } if (collection_name == "system") { if (node->kind != Ast_ForeignImportDecl) { - syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'"); + do_error(node, "The library collection 'system' is restrict for 'foreign import'"); return false; } else { *path = file_str; @@ -5735,7 +5746,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node } } else if (!find_library_collection_path(collection_name, &base_dir)) { // NOTE(bill): It's a naughty name - syntax_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); + do_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); return false; } } else { @@ -5759,7 +5770,7 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node if (collection_name == "core" || collection_name == "base") { return true; } else { - syntax_error(node, "The package '%.*s' must be imported with the 'base' library collection: 'base:%.*s'", LIT(file_str), LIT(file_str)); + do_error(node, "The package '%.*s' must be imported with the 'base' library collection: 'base:%.*s'", LIT(file_str), LIT(file_str)); return false; } } @@ -5844,30 +5855,12 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas } else if (node->kind == Ast_ForeignImportDecl) { ast_node(fl, ForeignImportDecl, node); - auto fullpaths = array_make(permanent_allocator(), 0, fl->filepaths.count); - - for (Token const &fp : fl->filepaths) { - String file_str = string_trim_whitespace(string_value_from_token(f, fp)); - String fullpath = file_str; - if (allow_check_foreign_filepath()) { - String foreign_path = {}; - bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path); - if (!ok) { - decls[i] = ast_bad_decl(f, fp, fl->filepaths[fl->filepaths.count-1]); - goto end; - } - fullpath = foreign_path; - } - array_add(&fullpaths, fullpath); - } - if (fullpaths.count == 0) { + if (fl->filepaths.count == 0) { syntax_error(decls[i], "No foreign paths found"); - decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); + decls[i] = ast_bad_decl(f, ast_token(fl->filepaths[0]), ast_end_token(fl->filepaths[fl->filepaths.count-1])); goto end; - } - - fl->fullpaths = slice_from_array(fullpaths); + } } else if (node->kind == Ast_WhenStmt) { ast_node(ws, WhenStmt, node); diff --git a/src/parser.hpp b/src/parser.hpp index 5820275c8..1e07cfd59 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -631,7 +631,7 @@ AST_KIND(_DeclBegin, "", bool) \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ - Slice filepaths; \ + Slice filepaths; \ Token library_name; \ String collection_name; \ Slice fullpaths; \ diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index b2e12999b..1ffd3a82f 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -278,7 +278,7 @@ Token ast_end_token(Ast *node) { case Ast_ImportDecl: return node->ImportDecl.relpath; case Ast_ForeignImportDecl: if (node->ForeignImportDecl.filepaths.count > 0) { - return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]; + return ast_end_token(node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1]); } if (node->ForeignImportDecl.library_name.kind != Token_Invalid) { return node->ForeignImportDecl.library_name; -- cgit v1.2.3 From a1b8749e74639875467cba56b0ab02c342870338 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 28 May 2024 00:23:23 +0100 Subject: Delay checking foreign import paths until after global scope is checked --- src/checker.cpp | 168 +++++++++++++++++++++++++++++--------------------------- src/checker.hpp | 3 + src/entity.cpp | 1 + src/parser.cpp | 28 +++++++++- src/parser.hpp | 1 + 5 files changed, 118 insertions(+), 83 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 4e5aed2bf..1ded6ea6e 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1283,6 +1283,7 @@ gb_internal void init_checker_info(CheckerInfo *i) { mpsc_init(&i->definition_queue, a); //); // 1<<20); mpsc_init(&i->required_global_variable_queue, a); // 1<<10); mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10); + mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10); mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used string_map_init(&i->load_directory_cache); @@ -1307,6 +1308,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) { mpsc_destroy(&i->definition_queue); mpsc_destroy(&i->required_global_variable_queue); mpsc_destroy(&i->required_foreign_imports_through_force_queue); + mpsc_destroy(&i->foreign_imports_to_check_fullpaths); map_destroy(&i->objc_msgSend_types); string_map_destroy(&i->load_file_cache); @@ -4874,105 +4876,112 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { return false; } -gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { - if (decl->state_flags & StateFlag_BeenHandled) return; - decl->state_flags |= StateFlag_BeenHandled; +gb_internal void check_foreign_import_fullpaths(Checker *c) { + CheckerContext ctx = make_checker_context(c); - ast_node(fl, ForeignImportDecl, decl); + UntypedExprInfoMap untyped = {}; + defer (map_destroy(&untyped)); - Scope *parent_scope = ctx->scope; - GB_ASSERT(parent_scope->flags&ScopeFlag_File); + for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_imports_to_check_fullpaths, &e); /**/) { + GB_ASSERT(e != nullptr); + GB_ASSERT(e->kind == Entity_LibraryName); + Ast *decl = e->LibraryName.decl; + ast_node(fl, ForeignImportDecl, decl); - String base_dir = dir_from_path(decl->file()->fullpath); + AstFile *f = decl->file(); - auto fullpaths = array_make(permanent_allocator(), 0, fl->filepaths.count); + reset_checker_context(&ctx, f, &untyped); + ctx.collect_delayed_decls = false; - for (Ast *fp_node : fl->filepaths) { - Operand op = {}; - check_expr(ctx, &op, fp_node); - if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) { - gbString s = expr_to_string(op.expr); - error(fp_node, "Expected a constant string value, got '%s'", s); - gb_string_free(s); - continue; - } - if (!is_type_string(op.type)) { - gbString s = type_to_string(op.type); - error(fp_node, "Expected a constant string value, got value of type '%s'", s); - gb_string_free(s); - continue; - } + GB_ASSERT(ctx.scope == e->scope); - String file_str = op.value.value_string; - file_str = string_trim_whitespace(file_str); + if (fl->fullpaths.count == 0) { + String base_dir = dir_from_path(decl->file()->fullpath); - String fullpath = file_str; - if (allow_check_foreign_filepath()) { - String foreign_path = {}; - bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path); - gb_unused(ok); - fullpath = foreign_path; - } - array_add(&fullpaths, fullpath); - } - fl->fullpaths = slice_from_array(fullpaths); + auto fullpaths = array_make(permanent_allocator(), 0, fl->filepaths.count); + for (Ast *fp_node : fl->filepaths) { + Operand op = {}; + check_expr(&ctx, &op, fp_node); + if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) { + gbString s = expr_to_string(op.expr); + error(fp_node, "Expected a constant string value, got '%s'", s); + gb_string_free(s); + continue; + } + if (!is_type_string(op.type)) { + gbString s = type_to_string(op.type); + error(fp_node, "Expected a constant string value, got value of type '%s'", s); + gb_string_free(s); + continue; + } - if (fl->fullpaths.count == 0) { - return; - } - String fullpath = fl->fullpaths[0]; - String library_name = path_to_entity_name(fl->library_name.string, fullpath); - if (is_blank_ident(library_name)) { - error(fl->token, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string)); - return; - } + String file_str = op.value.value_string; + file_str = string_trim_whitespace(file_str); - for (String const &path : fl->fullpaths) { - String ext = path_extension(path); - if (str_eq_ignore_case(ext, ".c") || - str_eq_ignore_case(ext, ".cpp") || - str_eq_ignore_case(ext, ".cxx") || - str_eq_ignore_case(ext, ".h") || - str_eq_ignore_case(ext, ".hpp") || - str_eq_ignore_case(ext, ".hxx") || - false - ) { - error(fl->token, "With 'foreign import', you cannot import a %.*s file directory, you must precompile the library and link against that", LIT(ext)); - break; + String fullpath = file_str; + if (allow_check_foreign_filepath()) { + String foreign_path = {}; + bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true); + if (ok) { + fullpath = foreign_path; + } + } + array_add(&fullpaths, fullpath); + } + fl->fullpaths = slice_from_array(fullpaths); + } + + for (String const &path : fl->fullpaths) { + String ext = path_extension(path); + if (str_eq_ignore_case(ext, ".c") || + str_eq_ignore_case(ext, ".cpp") || + str_eq_ignore_case(ext, ".cxx") || + str_eq_ignore_case(ext, ".h") || + str_eq_ignore_case(ext, ".hpp") || + str_eq_ignore_case(ext, ".hxx") || + false + ) { + error(fl->token, "With 'foreign import', you cannot import a %.*s file/directory, you must precompile the library and link against that", LIT(ext)); + break; + } } + + add_untyped_expressions(ctx.info, &untyped); + + e->LibraryName.paths = fl->fullpaths; } +} + +gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { + if (decl->state_flags & StateFlag_BeenHandled) return; + decl->state_flags |= StateFlag_BeenHandled; + ast_node(fl, ForeignImportDecl, decl); - // if (fl->collection_name != "system") { - // char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); - // defer (gb_free(heap_allocator(), c_str)); - // gb_memmove(c_str, fullpath.text, fullpath.len); - // c_str[fullpath.len] = '\0'; + Scope *parent_scope = ctx->scope; + GB_ASSERT(parent_scope->flags&ScopeFlag_File); - // gbFile f = {}; - // gbFileError file_err = gb_file_open(&f, c_str); - // defer (gb_file_close(&f)); + String library_name = fl->library_name.string; + if (library_name.len == 0 && fl->fullpaths.count != 0) { + String fullpath = fl->fullpaths[0]; + library_name = path_to_entity_name(fl->library_name.string, fullpath); + } + if (library_name.len == 0 || is_blank_ident(library_name)) { + error(fl->token, "File name, '%.*s', cannot be as a library name as it is not a valid identifier", LIT(library_name)); + return; + } - // switch (file_err) { - // case gbFileError_Invalid: - // error(decl, "Invalid file or cannot be found ('%.*s')", LIT(fullpath)); - // return; - // case gbFileError_NotExists: - // error(decl, "File cannot be found ('%.*s')", LIT(fullpath)); - // return; - // } - // } GB_ASSERT(fl->library_name.pos.line != 0); fl->library_name.string = library_name; Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid, fl->fullpaths, library_name); + e->LibraryName.decl = decl; add_entity_flags_from_file(ctx, e, parent_scope); add_entity(ctx, parent_scope, nullptr, e); - AttributeContext ac = {}; check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac); if (ac.require_declaration) { @@ -4987,12 +4996,8 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { e->LibraryName.extra_linker_flags = extra_linker_flags; } - if (has_asm_extension(fullpath)) { - if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.os != TargetOs_darwin) { - error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s", - LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch])); - } - } + mpsc_enqueue(&ctx->info->foreign_imports_to_check_fullpaths, e); + } // Returns true if a new package is present @@ -6354,6 +6359,9 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check procedure bodies"); check_procedure_bodies(c); + TIME_SECTION("check foreign import fullpaths"); + check_foreign_import_fullpaths(c); + TIME_SECTION("add entities from procedure bodies"); check_merge_queues_into_arrays(c); diff --git a/src/checker.hpp b/src/checker.hpp index 2ade9312e..6ae7b90e2 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -414,6 +414,7 @@ struct CheckerInfo { MPSCQueue entity_queue; MPSCQueue required_global_variable_queue; MPSCQueue required_foreign_imports_through_force_queue; + MPSCQueue foreign_imports_to_check_fullpaths; MPSCQueue intrinsics_entry_point_usage; @@ -434,6 +435,8 @@ struct CheckerInfo { BlockingMutex load_directory_mutex; StringMap load_directory_cache; PtrMap load_directory_map; // Key: Ast_CallExpr * + + }; struct CheckerContext { diff --git a/src/entity.cpp b/src/entity.cpp index 8a7417006..1461b96d7 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -266,6 +266,7 @@ struct Entity { Scope *scope; } ImportName; struct { + Ast *decl; Slice paths; String name; i64 priority_index; diff --git a/src/parser.cpp b/src/parser.cpp index be0d68177..7e72f3c21 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1285,13 +1285,15 @@ gb_internal Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token i } gb_internal Ast *ast_foreign_import_decl(AstFile *f, Token token, Array filepaths, Token library_name, - CommentGroup *docs, CommentGroup *comment) { + bool multiple_filepaths, + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ForeignImportDecl); result->ForeignImportDecl.token = token; result->ForeignImportDecl.filepaths = slice_from_array(filepaths); result->ForeignImportDecl.library_name = library_name; result->ForeignImportDecl.docs = docs; result->ForeignImportDecl.comment = comment; + result->ForeignImportDecl.multiple_filepaths = multiple_filepaths; result->ForeignImportDecl.attributes.allocator = ast_allocator(f); return result; @@ -4882,8 +4884,11 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { if (is_blank_ident(lib_name)) { syntax_error(lib_name, "Illegal foreign import name: '_'"); } + bool multiple_filepaths = false; + Array filepaths = {}; if (allow_token(f, Token_OpenBrace)) { + multiple_filepaths = true; array_init(&filepaths, ast_allocator(f)); while (f->curr_token.kind != Token_CloseBrace && @@ -4912,7 +4917,7 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { syntax_error(lib_name, "You cannot use foreign import within a procedure. This must be done at the file scope"); s = ast_bad_decl(f, lib_name, ast_token(filepaths[0])); } else { - s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment); + s = ast_foreign_import_decl(f, token, filepaths, lib_name, multiple_filepaths, docs, f->line_comment); } expect_semicolon(f); return s; @@ -5859,7 +5864,24 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas syntax_error(decls[i], "No foreign paths found"); decls[i] = ast_bad_decl(f, ast_token(fl->filepaths[0]), ast_end_token(fl->filepaths[fl->filepaths.count-1])); goto end; - + } else if (!fl->multiple_filepaths && + fl->filepaths.count == 1) { + Ast *fp = fl->filepaths[0]; + GB_ASSERT(fp->kind == Ast_BasicLit); + Token fp_token = fp->BasicLit.token; + String file_str = string_trim_whitespace(string_value_from_token(f, fp_token)); + String fullpath = file_str; + if (allow_check_foreign_filepath()) { + String foreign_path = {}; + bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path); + if (!ok) { + decls[i] = ast_bad_decl(f, fp_token, fp_token); + goto end; + } + fullpath = foreign_path; + } + fl->fullpaths = slice_make(permanent_allocator(), 1); + fl->fullpaths[0] = fullpath; } } else if (node->kind == Ast_WhenStmt) { diff --git a/src/parser.hpp b/src/parser.hpp index 1e07cfd59..0e411d9ac 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -632,6 +632,7 @@ AST_KIND(_DeclBegin, "", bool) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ Slice filepaths; \ + bool multiple_filepaths; \ Token library_name; \ String collection_name; \ Slice fullpaths; \ -- cgit v1.2.3 From d91054b615e5e185ded106e4903cdd66b2c4f582 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 28 May 2024 00:27:13 +0100 Subject: Change parser to use `^Expr` rather than `string` for the foreign import paths --- core/odin/ast/ast.odin | 2 +- core/odin/parser/parser.odin | 10 ++++++---- src/parser.cpp | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index be541befa..7891fb12d 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -538,7 +538,7 @@ Foreign_Import_Decl :: struct { import_tok: tokenizer.Token, name: ^Ident, collection_name: string, - fullpaths: []string, + fullpaths: []^Expr, comment: ^Comment_Group, } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index e32fbdced..813585ba4 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1190,12 +1190,12 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { error(p, name.pos, "illegal foreign import name: '_'") } - fullpaths: [dynamic]string + fullpaths: [dynamic]^ast.Expr if allow_token(p, .Open_Brace) { for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { - path := expect_token(p, .String) - append(&fullpaths, path.text) + path := parse_expr(p, false) + append(&fullpaths, path) allow_token(p, .Comma) or_break } @@ -1203,7 +1203,9 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { } else { path := expect_token(p, .String) reserve(&fullpaths, 1) - append(&fullpaths, path.text) + bl := ast.new(ast.Basic_Lit, path.pos, end_pos(path)) + bl.tok = tok + append(&fullpaths, bl) } if len(fullpaths) == 0 { diff --git a/src/parser.cpp b/src/parser.cpp index 7e72f3c21..c004a8f65 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4894,7 +4894,7 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) { while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { - Ast *path = parse_expr(f, true); + Ast *path = parse_expr(f, false); array_add(&filepaths, path); if (!allow_field_separator(f)) { -- cgit v1.2.3 From 9d28f2e18c80b1537b9a71b907839b20973feb21 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 9 Jun 2024 22:46:45 -0400 Subject: Fix `or_or_` error messages --- src/parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index c004a8f65..eff5e0c6c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -555,7 +555,7 @@ gb_internal Ast *ast_unary_expr(AstFile *f, Token op, Ast *expr) { syntax_error_with_verbose(expr, "'or_return' within an unary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(expr, "'or_%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); + syntax_error_with_verbose(expr, "'%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); break; } @@ -583,7 +583,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) { syntax_error_with_verbose(left, "'or_return' within a binary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(left, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); + syntax_error_with_verbose(left, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); break; } if (right) switch (right->kind) { @@ -591,7 +591,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) { syntax_error_with_verbose(right, "'or_return' within a binary expression not wrapped in parentheses (...)"); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(right, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); + syntax_error_with_verbose(right, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); break; } @@ -3102,7 +3102,7 @@ gb_internal void parse_check_or_return(Ast *operand, char const *msg) { syntax_error_with_verbose(operand, "'or_return' use within %s is not wrapped in parentheses (...)", msg); break; case Ast_OrBranchExpr: - syntax_error_with_verbose(operand, "'or_%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); + syntax_error_with_verbose(operand, "'%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); break; } } -- cgit v1.2.3 From 1945218f6df814ea95233035d0b51585e2522b2e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 14:18:33 +0100 Subject: Improve parsing for `label: #reverse for` and `label: #partial switch` --- core/odin/parser/parser.odin | 37 ++++++++++++++++++++++++++++++++++++- src/parser.cpp | 6 ++++-- 2 files changed, 40 insertions(+), 3 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index ad5ee9087..6b0aa2888 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1455,7 +1455,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case "unroll": return parse_unrolled_for_loop(p, tag) case "reverse": - stmt := parse_for_stmt(p) + stmt := parse_stmt(p) if range, is_range := stmt.derived.(^ast.Range_Stmt); is_range { if range.reverse { @@ -3515,6 +3515,25 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { case op.kind == .Colon: expect_token_after(p, .Colon, "identifier list") if .Label in flags && len(lhs) == 1 { + is_partial := false + is_reverse := false + + partial_token: tokenizer.Token + if p.curr_tok.kind == .Hash { + name := peek_token(p) + if name.kind == .Ident && name.text == "partial" && + peek_token(p, 1).kind == .Switch { + partial_token = expect_token(p, .Hash) + expect_token(p, .Ident) + is_partial = true + } else if name.kind == .Ident && name.text == "reverse" && + peek_token(p, 1).kind == .For { + partial_token = expect_token(p, .Hash) + expect_token(p, .Ident) + is_reverse = true + } + } + #partial switch p.curr_tok.kind { case .Open_Brace, .If, .For, .Switch: label := lhs[0] @@ -3529,6 +3548,22 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { case ^ast.Type_Switch_Stmt: n.label = label case ^ast.Range_Stmt: n.label = label } + + if is_partial { + #partial switch n in stmt.derived_stmt { + case ^ast.Switch_Stmt: n.partial = true + case ^ast.Type_Switch_Stmt: n.partial = true + case: + error(p, partial_token.pos, "incorrect use of directive, use '%s: #partial switch'", partial_token.text) + } + } + if is_reverse { + #partial switch n in stmt.derived_stmt { + case ^ast.Range_Stmt: n.reverse = true + case: + error(p, partial_token.pos, "incorrect use of directive, use '%s: #reverse for'", partial_token.text) + } + } } return stmt diff --git a/src/parser.cpp b/src/parser.cpp index eff5e0c6c..2cdcea417 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3747,8 +3747,10 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) { case Ast_TypeSwitchStmt: stmt->TypeSwitchStmt.partial = true; break; + default: + syntax_error(partial_token, "Incorrect use of directive, use '%.*s: #partial switch'", LIT(ast_token(name).string)); + break; } - syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string)); } else if (is_reverse) { switch (stmt->kind) { case Ast_RangeStmt: @@ -5176,7 +5178,7 @@ gb_internal Ast *parse_stmt(AstFile *f) { } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); } else if (tag == "reverse") { - Ast *for_stmt = parse_for_stmt(f); + Ast *for_stmt = parse_stmt(f); if (for_stmt->kind == Ast_RangeStmt) { if (for_stmt->RangeStmt.reverse) { syntax_error(token, "#reverse already applied to a 'for in' statement"); -- cgit v1.2.3 From f1779c85dedb8bb309a9afa8cfa7e35ad727d237 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 18:50:53 +0100 Subject: Fix #3727 --- src/parser.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 2cdcea417..0cd96f5b5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2091,6 +2091,9 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) { gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { Ast *expr = parse_unary_expr(f, false); Ast *e = strip_or_return_expr(expr); + if (e == nullptr) { + return expr; + } if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); return ast_bad_expr(f, token, f->curr_token); -- cgit v1.2.3 From ca481dc52d86a6856abba7481bc849a864f0183d Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:51:03 -0400 Subject: Fix displaying error on wrong line with token at EOL Previously, this would get a token on text like "\n*\n" where `*` is the token's position, and it would advance off that line. --- src/parser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 0cd96f5b5..468f4749f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -51,6 +51,12 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) u8 *line_start = pos_offset; u8 *line_end = pos_offset; + + if (offset > 0 && *line_start == '\n') { + // Prevent an error token that starts at the boundary of a line that + // leads to an empty line from advancing off its line. + line_start -= 1; + } while (line_start >= start) { if (*line_start == '\n') { line_start += 1; -- cgit v1.2.3 From 8626d38db10f134f97ea36d34ceda072a137e779 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 14 Jun 2024 13:02:54 -0400 Subject: Fix displaying emptiness when error is on first line --- src/parser.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 468f4749f..2f764c753 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -64,6 +64,11 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) } line_start -= 1; } + if (line_start == start - 1) { + // Prevent an error on the first line from stepping behind the boundary + // of the text. + line_start += 1; + } while (line_end < end) { if (*line_end == '\n') { -- cgit v1.2.3 From dab3c832e00a3186bddc79585d9ba7ee10d39eaf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 20 Jun 2024 15:32:30 +0100 Subject: Add `#warning()` builtin compile time procedure --- core/odin/parser/parser.odin | 2 +- src/check_builtin.cpp | 20 ++++++++++++++++++++ src/parser.cpp | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 6b0aa2888..7edd509d0 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1438,7 +1438,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case: error(p, stmt.pos, "#partial can only be applied to a switch statement") } return stmt - case "assert", "panic": + case "assert", "panic", "warning": bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(tag)) bd.tok = tok bd.name = name diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 47abd42cf..e85981911 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1714,6 +1714,26 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_untyped_bool; operand->mode = Addressing_Constant; + } else if (name == "warning") { + ERROR_BLOCK(); + if (ce->args.count != 1) { + error(call, "'#warning' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + warning(call, "%.*s", LIT(operand->value.value_string)); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + operand->type = t_invalid; + operand->mode = Addressing_NoValue; } else if (name == "panic") { ERROR_BLOCK(); if (ce->args.count != 1) { diff --git a/src/parser.cpp b/src/parser.cpp index 0cd96f5b5..7383c3360 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5167,7 +5167,7 @@ gb_internal Ast *parse_stmt(AstFile *f) { break; } return s; - } else if (tag == "assert" || tag == "panic") { + } else if (tag == "assert" || tag == "panic" || tag == "warning") { Ast *t = ast_basic_directive(f, hash_token, name); Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t)); expect_semicolon(f); -- cgit v1.2.3 From c0987394840d63fae627c1623ea2661c31adc9b9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 25 Jun 2024 09:36:59 +0100 Subject: Remove `@(warning)` and `#warning(...)` --- core/odin/parser/parser.odin | 2 +- src/check_builtin.cpp | 20 -------------------- src/checker.cpp | 14 -------------- src/parser.cpp | 2 +- 4 files changed, 2 insertions(+), 36 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 7edd509d0..6b0aa2888 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1438,7 +1438,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case: error(p, stmt.pos, "#partial can only be applied to a switch statement") } return stmt - case "assert", "panic", "warning": + case "assert", "panic": bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(tag)) bd.tok = tok bd.name = name diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e85981911..47abd42cf 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1714,26 +1714,6 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_untyped_bool; operand->mode = Addressing_Constant; - } else if (name == "warning") { - ERROR_BLOCK(); - if (ce->args.count != 1) { - error(call, "'#warning' expects 1 argument, got %td", ce->args.count); - return false; - } - if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { - gbString str = expr_to_string(ce->args[0]); - error(call, "'%s' is not a constant string", str); - gb_string_free(str); - return false; - } - warning(call, "%.*s", LIT(operand->value.value_string)); - if (c->proc_name != "") { - gbString str = type_to_string(c->curr_proc_sig); - error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); - gb_string_free(str); - } - operand->type = t_invalid; - operand->mode = Addressing_NoValue; } else if (name == "panic") { ERROR_BLOCK(); if (ce->args.count != 1) { diff --git a/src/checker.cpp b/src/checker.cpp index 4945b3810..fdf5a8b7d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3493,20 +3493,6 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; - } else if (name == "warning") { - ExactValue ev = check_decl_attribute_value(c, value); - - if (ev.kind == ExactValue_String) { - String msg = ev.value_string; - if (msg.len == 0) { - error(elem, "Warning message cannot be an empty string"); - } else { - ac->warning_message = msg; - } - } else { - error(elem, "Expected a string value for '%.*s'", LIT(name)); - } - return true; } else if (name == "require_results") { if (value != nullptr) { error(elem, "Expected no value for '%.*s'", LIT(name)); diff --git a/src/parser.cpp b/src/parser.cpp index 7383c3360..0cd96f5b5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5167,7 +5167,7 @@ gb_internal Ast *parse_stmt(AstFile *f) { break; } return s; - } else if (tag == "assert" || tag == "panic" || tag == "warning") { + } else if (tag == "assert" || tag == "panic") { Ast *t = ast_basic_directive(f, hash_token, name); Ast *stmt = ast_expr_stmt(f, parse_call_expr(f, t)); expect_semicolon(f); -- cgit v1.2.3 From 862a04376f589b23c34700d8e7c746048741e19f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 28 Jun 2024 09:16:01 +0100 Subject: Improve tokenizing wrong number literals --- src/parser.cpp | 12 +++++++++++- src/tokenizer.cpp | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 0cd96f5b5..0364e2c2b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -718,7 +718,17 @@ gb_internal ExactValue exact_value_from_token(AstFile *f, Token const &token) { } ExactValue value = exact_value_from_basic_literal(token.kind, s); if (value.kind == ExactValue_Invalid) { - syntax_error(token, "Invalid token literal"); + switch (token.kind) { + case Token_Integer: + syntax_error(token, "Invalid integer literal"); + break; + case Token_Float: + syntax_error(token, "Invalid float literal"); + break; + default: + syntax_error(token, "Invalid token literal"); + break; + } } return value; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f7751d840..1a37043d7 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -433,6 +433,7 @@ gb_internal gb_inline i32 digit_value(Rune r) { } gb_internal gb_inline void scan_mantissa(Tokenizer *t, i32 base) { + base = 16; // always check for any possible letter while (digit_value(t->curr_rune) < base || t->curr_rune == '_') { advance_to_next_rune(t); } -- cgit v1.2.3 From 67e9a6fd9bedf92498f41aa206d042566ad43595 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 28 Jun 2024 10:04:08 +0100 Subject: Improve error reporting on "Failed to parse fail" and show the line error if possible --- src/parser.cpp | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 0364e2c2b..0e971f792 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -35,18 +35,38 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) if (file == nullptr) { return nullptr; } - isize offset = pos.offset; - u8 *start = file->tokenizer.start; u8 *end = file->tokenizer.end; if (start == end) { return nullptr; } + + isize offset = pos.offset; + if (pos.line != 0 && offset == 0) { + for (i32 i = 1; i < pos.line; i++) { + while (start+offset < end) { + u8 c = start[offset++]; + if (c == '\n') { + break; + } + } + } + for (i32 i = 1; i < pos.column; i++) { + u8 *ptr = start+offset; + u8 c = *ptr; + if (c & 0x80) { + offset += utf8_decode(ptr, end-ptr, nullptr); + } else { + offset++; + } + } + } + + isize len = end-start; if (len < offset) { return nullptr; } - u8 *pos_offset = start+offset; u8 *line_start = pos_offset; @@ -70,6 +90,7 @@ gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) if (offset_) *offset_ = cast(i32)(pos_offset - the_line.text); + return gb_string_make_length(heap_allocator(), the_line.text, the_line.len); } @@ -5417,6 +5438,7 @@ gb_internal WORKER_TASK_PROC(parser_worker_proc) { gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; + f.pos.file_id = cast(i32)(f.index+1); auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData); wd->parser = p; wd->imported_file = f; @@ -5453,6 +5475,7 @@ gb_internal WORKER_TASK_PROC(foreign_file_worker_proc) { gb_internal void parser_add_foreign_file_to_process(Parser *p, AstPackage *pkg, AstForeignFileKind kind, FileInfo fi, TokenPos pos) { // TODO(bill): Use a better allocator ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; + f.pos.file_id = cast(i32)(f.index+1); auto wd = gb_alloc_item(permanent_allocator(), ForeignFileWorkerData); wd->parser = p; wd->imported_file = f; -- cgit v1.2.3 From 906afa41544c692f65467f1dba546a6ca4e71f1d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Jun 2024 10:13:15 +0100 Subject: Allow for `when x in y {` (minor oversight in syntax) --- core/odin/parser/parser.odin | 5 +++++ src/parser.cpp | 3 +++ 2 files changed, 8 insertions(+) (limited to 'src/parser.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 03fb4d66d..715b96d84 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -689,7 +689,12 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { prev_level := p.expr_level p.expr_level = -1 + prev_allow_in_expr := p.allow_in_expr + p.allow_in_expr = true + cond = parse_expr(p, false) + + p.allow_in_expr = prev_allow_in_expr p.expr_level = prev_level if cond == nil { diff --git a/src/parser.cpp b/src/parser.cpp index 0e971f792..b1a179573 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4573,9 +4573,12 @@ gb_internal Ast *parse_when_stmt(AstFile *f) { isize prev_level = f->expr_level; f->expr_level = -1; + bool prev_allow_in_expr = f->allow_in_expr; + f->allow_in_expr = true; cond = parse_expr(f, false); + f->allow_in_expr = prev_allow_in_expr; f->expr_level = prev_level; if (cond == nullptr) { -- cgit v1.2.3 From 2187f3e7ff076ea6375b1a09e32908a00e479dc8 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Jun 2024 19:14:24 +0100 Subject: `-strict-style` enforce 1TBS (mostly) --- src/parser.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index b1a179573..9b27ae156 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1486,7 +1486,7 @@ gb_internal bool skip_possible_newline(AstFile *f) { return false; } -gb_internal bool skip_possible_newline_for_literal(AstFile *f) { +gb_internal bool skip_possible_newline_for_literal(AstFile *f, bool seen_where=false) { Token curr = f->curr_token; if (token_is_newline(curr)) { Token next = peek_token(f); @@ -1494,6 +1494,10 @@ gb_internal bool skip_possible_newline_for_literal(AstFile *f) { switch (next.kind) { case Token_OpenBrace: case Token_else: + if (build_context.strict_style && !seen_where) { + syntax_error(next, "With '-strict-style' the attached brace style (1TBS) is enforced"); + } + /*fallthrough*/ case Token_where: advance_token(f); return true; @@ -2517,7 +2521,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { return type; } - skip_possible_newline_for_literal(f); + skip_possible_newline_for_literal(f, where_token.kind == Token_where); if (allow_token(f, Token_Uninit)) { if (where_token.kind != Token_Invalid) { @@ -4499,6 +4503,9 @@ gb_internal bool parse_control_statement_semicolon_separator(AstFile *f) { } + + + gb_internal Ast *parse_if_stmt(AstFile *f) { if (f->curr_proc == nullptr) { syntax_error(f->curr_token, "You cannot use an if statement in the file scope"); -- cgit v1.2.3 From b1a1da6618ce8ea5ffc8b7fca520895f01d36929 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Jun 2024 19:54:31 +0100 Subject: Add `-vet-tabs` --- src/build_settings.cpp | 3 +++ src/main.cpp | 7 +++++++ src/parser.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index d79343a8b..c3b4f2506 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -282,6 +282,7 @@ enum VetFlags : u64 { VetFlag_UnusedImports = 1u<<6, VetFlag_Deprecated = 1u<<7, VetFlag_Cast = 1u<<8, + VetFlag_Tabs = 1u<<9, VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, @@ -311,6 +312,8 @@ u64 get_vet_flag_from_name(String const &name) { return VetFlag_Deprecated; } else if (name == "cast") { return VetFlag_Cast; + } else if (name == "tabs") { + return VetFlag_Tabs; } return VetFlag_NONE; } diff --git a/src/main.cpp b/src/main.cpp index ea82651f5..53e631bb0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -301,6 +301,7 @@ enum BuildFlagKind { BuildFlag_VetStyle, BuildFlag_VetSemicolon, BuildFlag_VetCast, + BuildFlag_VetTabs, BuildFlag_CustomAttribute, BuildFlag_IgnoreUnknownAttributes, @@ -502,6 +503,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_VetStyle, str_lit("vet-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); @@ -1157,6 +1159,7 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break; case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; + case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; case BuildFlag_CustomAttribute: { @@ -2256,6 +2259,10 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-vet-cast"); print_usage_line(2, "Errs on casting a value to its own type or using `transmute` rather than `cast`."); print_usage_line(0, ""); + + print_usage_line(1, "-vet-tabs"); + print_usage_line(2, "Errs when the use of tabs has not been used for indentation."); + print_usage_line(0, ""); } if (check) { diff --git a/src/parser.cpp b/src/parser.cpp index 9b27ae156..51dc03085 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5286,12 +5286,53 @@ gb_internal Ast *parse_stmt(AstFile *f) { return ast_bad_stmt(f, token, f->curr_token); } + + +gb_internal u64 check_vet_flags(AstFile *file) { + if (file && file->vet_flags_set) { + return file->vet_flags; + } + return build_context.vet_flags; +} + + +gb_internal void parse_enforce_tabs(AstFile *f) { + Token prev = f->prev_token; + Token curr = f->curr_token; + if (prev.pos.line < curr.pos.line) { + u8 *start = f->tokenizer.start+prev.pos.offset; + u8 *end = f->tokenizer.start+curr.pos.offset; + u8 *it = end; + while (it > start) { + if (*it == '\n') { + it++; + break; + } + it--; + } + + isize len = end-it; + for (isize i = 0; i < len; i++) { + if (it[i] == ' ') { + syntax_error(curr, "With '-vet-tabs', tabs must be used for indentation"); + break; + } + } + } +} + gb_internal Array parse_stmt_list(AstFile *f) { auto list = array_make(ast_allocator(f)); while (f->curr_token.kind != Token_case && f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { + + // Checks to see if tabs have been used for indentation + if (check_vet_flags(f) & VetFlag_Tabs) { + parse_enforce_tabs(f); + } + Ast *stmt = parse_stmt(f); if (stmt && stmt->kind != Ast_EmptyStmt) { array_add(&list, stmt); -- cgit v1.2.3 From 34fce83d668f1f1754e88c29a8f0e3df71c60057 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 29 Jun 2024 20:04:34 +0100 Subject: Improve `-strict-style` rules for `if-else` statements --- src/parser.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 51dc03085..583f4a57d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1486,7 +1486,7 @@ gb_internal bool skip_possible_newline(AstFile *f) { return false; } -gb_internal bool skip_possible_newline_for_literal(AstFile *f, bool seen_where=false) { +gb_internal bool skip_possible_newline_for_literal(AstFile *f, bool ignore_strict_style=false) { Token curr = f->curr_token; if (token_is_newline(curr)) { Token next = peek_token(f); @@ -1494,7 +1494,7 @@ gb_internal bool skip_possible_newline_for_literal(AstFile *f, bool seen_where=f switch (next.kind) { case Token_OpenBrace: case Token_else: - if (build_context.strict_style && !seen_where) { + if (build_context.strict_style && !ignore_strict_style) { syntax_error(next, "With '-strict-style' the attached brace style (1TBS) is enforced"); } /*fallthrough*/ @@ -4548,7 +4548,11 @@ gb_internal Ast *parse_if_stmt(AstFile *f) { body = parse_block_stmt(f, false); } - skip_possible_newline_for_literal(f); + bool ignore_strict_style = false; + if (token.pos.line == ast_end_token(body).pos.line) { + ignore_strict_style = true; + } + skip_possible_newline_for_literal(f, ignore_strict_style); if (f->curr_token.kind == Token_else) { Token else_token = expect_token(f, Token_else); switch (f->curr_token.kind) { @@ -4600,7 +4604,11 @@ gb_internal Ast *parse_when_stmt(AstFile *f) { body = parse_block_stmt(f, true); } - skip_possible_newline_for_literal(f); + bool ignore_strict_style = false; + if (token.pos.line == ast_end_token(body).pos.line) { + ignore_strict_style = true; + } + skip_possible_newline_for_literal(f, ignore_strict_style); if (f->curr_token.kind == Token_else) { Token else_token = expect_token(f, Token_else); switch (f->curr_token.kind) { -- cgit v1.2.3 From 6f1cc8071c3ff49c5431cc8ad078d12883f91545 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 2 Jul 2024 15:28:08 +0200 Subject: wasm: add foreign import and linking of wasm object files --- src/build_settings.cpp | 9 --------- src/check_decl.cpp | 7 +++++-- src/checker.cpp | 3 +-- src/linker.cpp | 14 ++++++++++++++ src/llvm_backend_utility.cpp | 6 +++++- src/parser.cpp | 3 +-- 6 files changed, 26 insertions(+), 16 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c3b4f2506..9c93a5b69 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -860,15 +860,6 @@ gb_internal bool is_arch_x86(void) { return false; } -gb_internal bool allow_check_foreign_filepath(void) { - switch (build_context.metrics.arch) { - case TargetArch_wasm32: - case TargetArch_wasm64p32: - return false; - } - return true; -} - // TODO(bill): OS dependent versions for the BuildContext // join_path // is_dir diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 883cfcba9..3c4a4b3de 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1178,9 +1178,12 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { if (foreign_library->LibraryName.paths.count >= 1) { module_name = foreign_library->LibraryName.paths[0]; } - name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + + if (!string_ends_with(module_name, str_lit(".o"))) { + name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name); + } } - + e->Procedure.is_foreign = true; e->Procedure.link_name = name; diff --git a/src/checker.cpp b/src/checker.cpp index 734659510..c3d2ae5eb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5000,9 +5000,8 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) { String file_str = op.value.value_string; file_str = string_trim_whitespace(file_str); - String fullpath = file_str; - if (allow_check_foreign_filepath()) { + if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) { String foreign_path = {}; bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true); if (ok) { diff --git a/src/linker.cpp b/src/linker.cpp index 371736743..34c0af7e5 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -85,6 +85,20 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (extra_linker_flags.len != 0) { lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags)); } + + for_array(i, e->LibraryName.paths) { + String lib = e->LibraryName.paths[i]; + + if (lib.len == 0) { + continue; + } + + if (!string_ends_with(lib, str_lit(".o"))) { + continue; + } + + inputs = gb_string_append_fmt(inputs, " \"%.*s\"", LIT(lib)); + } } if (build_context.metrics.os == TargetOs_orca) { diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 98ed0c57e..1165476be 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -2029,7 +2029,11 @@ gb_internal void lb_set_wasm_procedure_import_attributes(LLVMValueRef value, Ent GB_ASSERT(foreign_library->LibraryName.paths.count == 1); module_name = foreign_library->LibraryName.paths[0]; - + + if (string_ends_with(module_name, str_lit(".o"))) { + return; + } + if (string_starts_with(import_name, module_name)) { import_name = substring(import_name, module_name.len+WASM_MODULE_NAME_SEPARATOR.len, import_name.len); } diff --git a/src/parser.cpp b/src/parser.cpp index 583f4a57d..997d32fa5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5813,7 +5813,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node return false; } - if (collection_name.len > 0) { // NOTE(bill): `base:runtime` == `core:runtime` if (collection_name == "core") { @@ -5968,7 +5967,7 @@ gb_internal void parse_setup_file_decls(Parser *p, AstFile *f, String const &bas Token fp_token = fp->BasicLit.token; String file_str = string_trim_whitespace(string_value_from_token(f, fp_token)); String fullpath = file_str; - if (allow_check_foreign_filepath()) { + if (!is_arch_wasm() || string_ends_with(fullpath, str_lit(".o"))) { String foreign_path = {}; bool ok = determine_path_from_string(&p->file_decl_mutex, node, base_dir, file_str, &foreign_path); if (!ok) { -- cgit v1.2.3 From 1eb0bc1408735401a2ba6fdfbc2dd27726be5137 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 4 Jul 2024 14:43:57 +0100 Subject: Remove `*_test.odin`; always compile it for all targets --- src/parser.cpp | 7 ------- src/parser.hpp | 1 - 2 files changed, 8 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 37f9c35de..548e46cbe 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -6342,8 +6342,6 @@ gb_internal bool parse_file(Parser *p, AstFile *f) { } else if (lc == "+lazy") { if (build_context.ignore_lazy) { // Ignore - } else if (f->flags & AstFile_IsTest) { - // Ignore } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) { // Ignore } else { @@ -6461,11 +6459,6 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe if (build_context.command_kind == Command_test) { String name = file->fullpath; name = remove_extension_from_path(name); - - String test_suffix = str_lit("_test"); - if (string_ends_with(name, test_suffix) && name != test_suffix) { - file->flags |= AstFile_IsTest; - } } diff --git a/src/parser.hpp b/src/parser.hpp index 02f2af28d..521fd7a37 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -74,7 +74,6 @@ enum AstFileFlag : u32 { AstFile_IsPrivatePkg = 1<<0, AstFile_IsPrivateFile = 1<<1, - AstFile_IsTest = 1<<3, AstFile_IsLazy = 1<<4, AstFile_NoInstrumentation = 1<<5, -- cgit v1.2.3 From 886ee66e7fcabbd09c20fd55d98051e3854dfd76 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 9 Jul 2024 14:16:56 +0100 Subject: Cache files, env, and args --- src/build_settings.cpp | 7 +- src/cached.cpp | 170 +++++++++++++++++++++++++++++++++++++++++-------- src/main.cpp | 13 +++- src/parser.cpp | 10 +++ src/parser.hpp | 4 ++ 5 files changed, 175 insertions(+), 29 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index be896f6fa..28ca0f088 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -329,7 +329,12 @@ enum SanitizerFlags : u32 { struct BuildCacheData { u64 crc; String cache_dir; - String manifest_path; + + // manifests + String files_path; + String args_path; + String env_path; + bool copy_already_done; }; diff --git a/src/cached.cpp b/src/cached.cpp index ab6d99848..da72e9989 100644 --- a/src/cached.cpp +++ b/src/cached.cpp @@ -1,4 +1,4 @@ -gb_internal GB_COMPARE_PROC(cached_file_cmp) { +gb_internal GB_COMPARE_PROC(string_cmp) { String const &x = *(String *)a; String const &y = *(String *)b; return string_compare(x, y); @@ -182,7 +182,9 @@ bool try_copy_executable_from_cache(void) { // returns false if different, true if it is the same -bool try_cached_build(Checker *c) { +bool try_cached_build(Checker *c, Array const &args) { + TEMPORARY_ALLOCATOR_GUARD(); + Parser *p = c->parser; auto files = array_make(heap_allocator()); @@ -200,7 +202,7 @@ bool try_cached_build(Checker *c) { array_add(&files, cache->path); } - array_sort(files, cached_file_cmp); + array_sort(files, string_cmp); u64 crc = 0; for (String const &path : files) { @@ -213,28 +215,58 @@ bool try_cached_build(Checker *c) { gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16); crc_str = gb_string_append_fmt(crc_str, "%016llx", crc); - String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); - String manifest_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("odin.manifest")); + String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); + String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest")); + String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest")); + String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest")); build_context.build_cache_data.cache_dir = cache_dir; - build_context.build_cache_data.manifest_path = manifest_path; + build_context.build_cache_data.files_path = files_path; + build_context.build_cache_data.args_path = args_path; + build_context.build_cache_data.env_path = env_path; + + auto envs = array_make(heap_allocator()); + defer (array_free(&envs)); + { + #if defined(GB_SYSTEM_WINDOWS) + wchar_t *strings = GetEnvironmentStringsW(); + defer (FreeEnvironmentStringsW(strings)); + + wchar_t *curr_string = strings; + while (curr_string && *curr_string) { + String16 wstr = make_string16_c(curr_string); + curr_string += wstr.len+1; + String str = string16_to_string(temporary_allocator(), wstr); + array_add(&envs, str); + } + #endif + } + array_sort(envs, string_cmp); if (check_if_exists_directory_otherwise_create(cache_dir)) { - goto do_write_file; + goto write_cache; } - if (check_if_exists_file_otherwise_create(manifest_path)) { - goto do_write_file; - } else { + if (check_if_exists_file_otherwise_create(files_path)) { + goto write_cache; + } + if (check_if_exists_file_otherwise_create(args_path)) { + goto write_cache; + } + if (check_if_exists_file_otherwise_create(env_path)) { + goto write_cache; + } + + { // exists already LoadedFile loaded_file = {}; LoadedFileError file_err = load_file_32( - alloc_cstring(temporary_allocator(), manifest_path), + alloc_cstring(temporary_allocator(), files_path), &loaded_file, false ); - if (file_err) { + if (file_err > LoadedFile_Empty) { return false; } @@ -250,7 +282,7 @@ bool try_cached_build(Checker *c) { } isize sep = string_index_byte(line, ' '); if (sep < 0) { - goto do_write_file; + goto write_cache; } String timestamp_str = substring(line, 0, sep); @@ -260,43 +292,131 @@ bool try_cached_build(Checker *c) { path_str = string_trim_whitespace(path_str); if (file_count >= files.count) { - goto do_write_file; + goto write_cache; } if (files[file_count] != path_str) { - goto do_write_file; + goto write_cache; } u64 timestamp = exact_value_to_u64(exact_value_integer_from_string(timestamp_str)); gbFileTime last_write_time = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path_str)); if (last_write_time != timestamp) { - goto do_write_file; + goto write_cache; } } if (file_count != files.count) { - goto do_write_file; + goto write_cache; } + } + { + LoadedFile loaded_file = {}; - goto try_copy_executable; + LoadedFileError file_err = load_file_32( + alloc_cstring(temporary_allocator(), args_path), + &loaded_file, + false + ); + if (file_err > LoadedFile_Empty) { + return false; + } + + String data = {cast(u8 *)loaded_file.data, loaded_file.size}; + String_Iterator it = {data, 0}; + + isize args_count = 0; + + for (; it.pos < data.len; args_count++) { + String line = string_split_iterator(&it, '\n'); + line = string_trim_whitespace(line); + if (line.len == 0) { + break; + } + if (args_count >= args.count) { + goto write_cache; + } + + if (line != args[args_count]) { + goto write_cache; + } + } } + { + LoadedFile loaded_file = {}; -do_write_file:; + LoadedFileError file_err = load_file_32( + alloc_cstring(temporary_allocator(), env_path), + &loaded_file, + false + ); + if (file_err > LoadedFile_Empty) { + return false; + } + + String data = {cast(u8 *)loaded_file.data, loaded_file.size}; + String_Iterator it = {data, 0}; + + isize env_count = 0; + + for (; it.pos < data.len; env_count++) { + String line = string_split_iterator(&it, '\n'); + line = string_trim_whitespace(line); + if (line.len == 0) { + break; + } + if (env_count >= envs.count) { + goto write_cache; + } + + if (line != envs[env_count]) { + goto write_cache; + } + } + } + + return try_copy_executable_from_cache(); + +write_cache:; { - char const *manifest_path_c = alloc_cstring(temporary_allocator(), manifest_path); - gb_file_remove(manifest_path_c); + char const *path_c = alloc_cstring(temporary_allocator(), files_path); + gb_file_remove(path_c); gbFile f = {}; defer (gb_file_close(&f)); - gb_file_open_mode(&f, gbFileMode_Write, manifest_path_c); + gb_file_open_mode(&f, gbFileMode_Write, path_c); for (String const &path : files) { gbFileTime ft = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path)); gb_fprintf(&f, "%llu %.*s\n", cast(unsigned long long)ft, LIT(path)); } - return false; } + { + char const *path_c = alloc_cstring(temporary_allocator(), args_path); + gb_file_remove(path_c); -try_copy_executable:; - return try_copy_executable_from_cache(); + gbFile f = {}; + defer (gb_file_close(&f)); + gb_file_open_mode(&f, gbFileMode_Write, path_c); + + for (String const &arg : args) { + String targ = string_trim_whitespace(arg); + gb_fprintf(&f, "%.*s\n", LIT(targ)); + } + } + { + char const *path_c = alloc_cstring(temporary_allocator(), env_path); + gb_file_remove(path_c); + + gbFile f = {}; + defer (gb_file_close(&f)); + gb_file_open_mode(&f, gbFileMode_Write, path_c); + + for (String const &env : envs) { + gb_fprintf(&f, "%.*s\n", LIT(env)); + } + } + + + return false; } diff --git a/src/main.cpp b/src/main.cpp index 49c34014b..f2312f248 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3271,12 +3271,19 @@ int main(int arg_count, char const **arg_ptr) { print_all_errors(); } - MAIN_TIME_SECTION("type check"); checker->parser = parser; init_checker(checker); - defer (destroy_checker(checker)); + defer (destroy_checker(checker)); // this is here because of a `goto` + + if (build_context.cached && parser->total_seen_load_directive_count.load() == 0) { + MAIN_TIME_SECTION("check cached build (pre-semantic check)"); + if (try_cached_build(checker, args)) { + goto end_of_code_gen; + } + } + MAIN_TIME_SECTION("type check"); check_parsed_files(checker); check_defines(&build_context, checker); if (any_errors()) { @@ -3331,7 +3338,7 @@ int main(int arg_count, char const **arg_ptr) { if (build_context.cached) { MAIN_TIME_SECTION("check cached build"); - if (try_cached_build(checker)) { + if (try_cached_build(checker, args)) { goto end_of_code_gen; } } diff --git a/src/parser.cpp b/src/parser.cpp index 548e46cbe..eb012e980 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -787,6 +787,9 @@ gb_internal Ast *ast_basic_directive(AstFile *f, Token token, Token name) { Ast *result = alloc_ast_node(f, Ast_BasicDirective); result->BasicDirective.token = token; result->BasicDirective.name = name; + if (string_starts_with(name.string, str_lit("load"))) { + f->seen_load_directive_count++; + } return result; } @@ -6576,6 +6579,13 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) { } } } + + for (AstPackage *pkg : p->packages) { + for (AstFile *file : pkg->files) { + p->total_seen_load_directive_count += file->seen_load_directive_count; + } + } + return ParseFile_None; } diff --git a/src/parser.hpp b/src/parser.hpp index 521fd7a37..86b3393af 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -140,6 +140,8 @@ struct AstFile { // This is effectively a queue but does not require any multi-threading capabilities Array delayed_decls_queues[AstDelayQueue_COUNT]; + std::atomic seen_load_directive_count; + #define PARSER_MAX_FIX_COUNT 6 isize fix_count; TokenPos fix_prev_pos; @@ -210,6 +212,8 @@ struct Parser { std::atomic total_token_count; std::atomic total_line_count; + std::atomic total_seen_load_directive_count; + // TODO(bill): What should this mutex be per? // * Parser // * Package -- cgit v1.2.3 From 36301d03595a7829e2e2e0b6483650887d6016ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 10 Jul 2024 13:03:38 +0100 Subject: Give better syntax error messages for things like `#define Example 123` --- src/parser.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index eb012e980..9ce3d563d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3138,7 +3138,7 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) { Ast *call = ast_call_expr(f, operand, args, open_paren, close_paren, ellipsis); Ast *o = unparen_expr(operand); - if (o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) { + if (o && o->kind == Ast_SelectorExpr && o->SelectorExpr.token.kind == Token_ArrowRight) { return ast_selector_call_expr(f, o->SelectorExpr.token, o, call); } @@ -5257,6 +5257,38 @@ gb_internal Ast *parse_stmt(AstFile *f) { } else if (tag == "include") { syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?"); s = ast_bad_stmt(f, token, f->curr_token); + } else if (tag == "define") { + s = ast_bad_stmt(f, token, f->curr_token); + + if (name.pos.line == f->curr_token.pos.line) { + bool call_like = false; + Ast *macro_expr = nullptr; + Token ident = f->curr_token; + if (allow_token(f, Token_Ident) && + name.pos.line == f->curr_token.pos.line) { + if (f->curr_token.kind == Token_OpenParen && f->curr_token.pos.column == ident.pos.column+ident.string.len) { + call_like = true; + (void)parse_call_expr(f, nullptr); + } + + if (name.pos.line == f->curr_token.pos.line && f->curr_token.kind != Token_Semicolon) { + macro_expr = parse_expr(f, false); + } + } + + ERROR_BLOCK(); + syntax_error(ident, "#define is not a valid declaration, Odin does not have a C-like preprocessor."); + if (macro_expr == nullptr || call_like) { + error_line("\tNote: Odin does not support macros\n"); + } else { + gbString s = expr_to_string(macro_expr); + error_line("\tSuggestion: Did you mean '%.*s :: %s'?\n", LIT(ident.string), s); + gb_string_free(s); + } + } else { + syntax_error(token, "#define is not a valid declaration, Odin does not have a C-like preprocessor."); + } + } else { syntax_error(token, "Unknown tag directive used: '%.*s'", LIT(tag)); s = ast_bad_stmt(f, token, f->curr_token); -- cgit v1.2.3 From edc793d7c123a38826860ef72684308902a7012c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 14 Jul 2024 11:39:05 +0100 Subject: Add `#no_capture args: ..T` to reuse the backing array stack memory --- core/fmt/fmt.odin | 58 +++++++++++++++++++++++------------------------ core/fmt/fmt_js.odin | 24 ++++++++++---------- core/fmt/fmt_os.odin | 24 ++++++++++---------- src/check_expr.cpp | 17 ++++++++++++++ src/check_type.cpp | 17 ++++++++++++++ src/checker.cpp | 1 + src/checker.hpp | 7 ++++++ src/entity.cpp | 2 +- src/llvm_backend.hpp | 7 ++++++ src/llvm_backend_proc.cpp | 27 +++++++++++++++++++++- src/parser.cpp | 1 + src/parser.hpp | 8 +++++-- 12 files changed, 136 insertions(+), 57 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 234f4afbd..e56211346 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -125,7 +125,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist // Returns: A formatted string. // @(require_results) -aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string { +aprint :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprint(&str, ..args, sep=sep) @@ -141,7 +141,7 @@ aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> strin // Returns: A formatted string with a newline character at the end. // @(require_results) -aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> string { +aprintln :: proc(#no_capture args: ..any, sep := " ", allocator := context.allocator) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprintln(&str, ..args, sep=sep) @@ -158,7 +158,7 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str // Returns: A formatted string. The returned string must be freed accordingly. // @(require_results) -aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string { +aprintf :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator, newline := false) -> string { str: strings.Builder strings.builder_init(&str, allocator) return sbprintf(&str, fmt, ..args, newline=newline) @@ -174,7 +174,7 @@ aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newlin // Returns: A formatted string. The returned string must be freed accordingly. // @(require_results) -aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string { +aprintfln :: proc(fmt: string, #no_capture args: ..any, allocator := context.allocator) -> string { return aprintf(fmt, ..args, allocator=allocator, newline=true) } // Creates a formatted string @@ -188,7 +188,7 @@ aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> s // Returns: A formatted string. // @(require_results) -tprint :: proc(args: ..any, sep := " ") -> string { +tprint :: proc(#no_capture args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprint(&str, ..args, sep=sep) @@ -204,7 +204,7 @@ tprint :: proc(args: ..any, sep := " ") -> string { // Returns: A formatted string with a newline character at the end. // @(require_results) -tprintln :: proc(args: ..any, sep := " ") -> string { +tprintln :: proc(#no_capture args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprintln(&str, ..args, sep=sep) @@ -221,7 +221,7 @@ tprintln :: proc(args: ..any, sep := " ") -> string { // Returns: A formatted string. // @(require_results) -tprintf :: proc(fmt: string, args: ..any, newline := false) -> string { +tprintf :: proc(fmt: string, #no_capture args: ..any, newline := false) -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) return sbprintf(&str, fmt, ..args, newline=newline) @@ -237,7 +237,7 @@ tprintf :: proc(fmt: string, args: ..any, newline := false) -> string { // Returns: A formatted string. // @(require_results) -tprintfln :: proc(fmt: string, args: ..any) -> string { +tprintfln :: proc(fmt: string, #no_capture args: ..any) -> string { return tprintf(fmt, ..args, newline=true) } // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. @@ -249,7 +249,7 @@ tprintfln :: proc(fmt: string, args: ..any) -> string { // // Returns: A formatted string // -bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { +bprint :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf) return sbprint(&sb, ..args, sep=sep) } @@ -262,7 +262,7 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { // // Returns: A formatted string with a newline character at the end // -bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { +bprintln :: proc(buf: []byte, #no_capture args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf) return sbprintln(&sb, ..args, sep=sep) } @@ -276,7 +276,7 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { // // Returns: A formatted string // -bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string { +bprintf :: proc(buf: []byte, fmt: string, #no_capture args: ..any, newline := false) -> string { sb := strings.builder_from_bytes(buf) return sbprintf(&sb, fmt, ..args, newline=newline) } @@ -289,7 +289,7 @@ bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> stri // // Returns: A formatted string // -bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string { +bprintfln :: proc(buf: []byte, fmt: string, #no_capture args: ..any) -> string { return bprintf(buf, fmt, ..args, newline=true) } // Runtime assertion with a formatted message @@ -301,14 +301,14 @@ bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string { // - loc: The location of the caller // @(disabled=ODIN_DISABLE_ASSERT) -assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) { +assertf :: proc(condition: bool, fmt: string, #no_capture args: ..any, loc := #caller_location) { if !condition { // NOTE(dragos): We are using the same trick as in builtin.assert // to improve performance to make the CPU not // execute speculatively, making it about an order of // magnitude faster @(cold) - internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) { + internal :: proc(loc: runtime.Source_Code_Location, fmt: string, #no_capture args: ..any) { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -326,7 +326,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati // - args: A variadic list of arguments to be formatted // - loc: The location of the caller // -panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { +panicf :: proc(fmt: string, #no_capture args: ..any, loc := #caller_location) -> ! { p := context.assertion_failure_proc if p == nil { p = runtime.default_assertion_failure_proc @@ -346,7 +346,7 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { // Returns: A formatted C string // @(require_results) -caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { +caprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str) sbprintf(&str, format, ..args, newline=newline) @@ -365,7 +365,7 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { // Returns: A formatted C string // @(require_results) -caprintfln :: proc(format: string, args: ..any) -> cstring { +caprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { return caprintf(format, ..args, newline=true) } // Creates a formatted C string @@ -379,7 +379,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring { // Returns: A formatted C string. // @(require_results) -ctprint :: proc(args: ..any, sep := " ") -> cstring { +ctprint :: proc(#no_capture args: ..any, sep := " ") -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprint(&str, ..args, sep=sep) @@ -399,7 +399,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring { // Returns: A formatted C string // @(require_results) -ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { +ctprintf :: proc(format: string, #no_capture args: ..any, newline := false) -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintf(&str, format, ..args, newline=newline) @@ -418,7 +418,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { // Returns: A formatted C string // @(require_results) -ctprintfln :: proc(format: string, args: ..any) -> cstring { +ctprintfln :: proc(format: string, #no_capture args: ..any) -> cstring { return ctprintf(format, ..args, newline=true) } // Formats using the default print settings and writes to the given strings.Builder @@ -430,7 +430,7 @@ ctprintfln :: proc(format: string, args: ..any) -> cstring { // // Returns: A formatted string // -sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { +sbprint :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string { wprint(strings.to_writer(buf), ..args, sep=sep, flush=true) return strings.to_string(buf^) } @@ -443,7 +443,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // // Returns: The resulting formatted string // -sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { +sbprintln :: proc(buf: ^strings.Builder, #no_capture args: ..any, sep := " ") -> string { wprintln(strings.to_writer(buf), ..args, sep=sep, flush=true) return strings.to_string(buf^) } @@ -457,7 +457,7 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { // // Returns: The resulting formatted string // -sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string { +sbprintf :: proc(buf: ^strings.Builder, fmt: string, #no_capture args: ..any, newline := false) -> string { wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline) return strings.to_string(buf^) } @@ -469,7 +469,7 @@ sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := fal // // Returns: A formatted string // -sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string { +sbprintfln :: proc(buf: ^strings.Builder, format: string, #no_capture args: ..any) -> string { return sbprintf(buf, format, ..args, newline=true) } // Formats and writes to an io.Writer using the default print settings @@ -481,7 +481,7 @@ sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string // // Returns: The number of bytes written // -wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { +wprint :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int { fi: Info fi.writer = w @@ -522,7 +522,7 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { // // Returns: The number of bytes written // -wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { +wprintln :: proc(w: io.Writer, #no_capture args: ..any, sep := " ", flush := true) -> int { fi: Info fi.writer = w @@ -549,11 +549,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int { // // Returns: The number of bytes written // -wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int { +wprintf :: proc(w: io.Writer, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int { MAX_CHECKED_ARGS :: 64 assert(len(args) <= MAX_CHECKED_ARGS, "number of args > 64 is unsupported") - parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], args: ..any) -> int { + parse_options :: proc(fi: ^Info, fmt: string, index, end: int, unused_args: ^bit_set[0 ..< MAX_CHECKED_ARGS], #no_capture args: ..any) -> int { i := index // Prefix @@ -809,7 +809,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline : // // Returns: The number of bytes written. // -wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int { +wprintfln :: proc(w: io.Writer, format: string, #no_capture args: ..any, flush := true) -> int { return wprintf(w, format, ..args, flush=flush, newline=true) } // Writes a ^runtime.Type_Info value to an io.Writer diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin index acf218eb5..4389b8d87 100644 --- a/core/fmt/fmt_js.odin +++ b/core/fmt/fmt_js.odin @@ -43,7 +43,7 @@ fd_to_writer :: proc(fd: os.Handle, loc := #caller_location) -> io.Writer { } // fprint formats using the default print settings and writes to fd -fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { +fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -54,7 +54,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #ca } // fprintln formats using the default print settings and writes to fd -fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { +fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -66,7 +66,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true, loc := # } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false, loc := #caller_location) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false, loc := #caller_location) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -78,24 +78,24 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline } // fprintfln formats according to the specified format string and writes to fd, followed by a newline. -fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, loc := #caller_location) -> int { +fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, loc := #caller_location) -> int { return fprintf(fd, fmt, ..args, flush=flush, newline=true, loc=loc) } // print formats using the default print settings and writes to stdout -print :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) } +print :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stdout, args=args, sep=sep, flush=flush) } // println formats using the default print settings and writes to stdout -println :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) } +println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stdout, args=args, sep=sep, flush=flush) } // printf formats according to the specififed format string and writes to stdout -printf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush) } // printfln formats according to the specified format string and writes to stdout, followed by a newline. -printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } +printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to stderr -eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) } +eprint :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprint(w=stderr, args=args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to stderr -eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) } +eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return wprintln(w=stderr, args=args, sep=sep, flush=flush) } // eprintf formats according to the specififed format string and writes to stderr -eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stderr, fmt, ..args, flush=flush) } // eprintfln formats according to the specified format string and writes to stderr, followed by a newline. -eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } +eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return wprintf(stdout, fmt, ..args, flush=flush, newline=true) } diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 9de0d43be..538f7a08b 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -9,7 +9,7 @@ import "core:io" import "core:bufio" // fprint formats using the default print settings and writes to fd -fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { +fprint :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -20,7 +20,7 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { } // fprintln formats using the default print settings and writes to fd -fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { +fprintln :: proc(fd: os.Handle, #no_capture args: ..any, sep := " ", flush := true) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -31,7 +31,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int { return wprintln(w, ..args, sep=sep, flush=flush) } // fprintf formats according to the specified format string and writes to fd -fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int { +fprintf :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true, newline := false) -> int { buf: [1024]byte b: bufio.Writer defer bufio.writer_flush(&b) @@ -42,7 +42,7 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline return wprintf(w, fmt, ..args, flush=flush, newline=newline) } // fprintfln formats according to the specified format string and writes to fd, followed by a newline. -fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int { +fprintfln :: proc(fd: os.Handle, fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(fd, fmt, ..args, flush=flush, newline=true) } fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) { @@ -67,19 +67,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err: } // print formats using the default print settings and writes to os.stdout -print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } +print :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) } // println formats using the default print settings and writes to os.stdout -println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } +println :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) } // printf formats according to the specified format string and writes to os.stdout -printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } +printf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) } // printfln formats according to the specified format string and writes to os.stdout, followed by a newline. -printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } +printfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) } // eprint formats using the default print settings and writes to os.stderr -eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } +eprint :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) } // eprintln formats using the default print settings and writes to os.stderr -eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } +eprintln :: proc(#no_capture args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) } // eprintf formats according to the specified format string and writes to os.stderr -eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } +eprintf :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) } // eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline. -eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } +eprintfln :: proc(fmt: string, #no_capture args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 12acca0cb..645d8ac5a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6033,6 +6033,23 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A Entity *vt = pt->params->Tuple.variables[pt->variadic_index]; o.type = vt->type; + + // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameter that use `#no_capture` + // on the variadic parameter + if (c->decl && (vt->flags & EntityFlag_NoCapture)) { + bool found = false; + for (NoCaptureData &nc : c->decl->no_captures) { + if (are_types_identical(vt->type, nc.slice_type)) { + nc.max_count = gb_max(nc.max_count, variadic_operands.count); + found = true; + break; + } + } + if (!found) { + array_add(&c->decl->no_captures, NoCaptureData{vt->type, variadic_operands.count}); + } + } + } else { dummy_argument_count += 1; o.type = t_untyped_nil; diff --git a/src/check_type.cpp b/src/check_type.cpp index dd8559114..d1c9bb381 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1953,6 +1953,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para error(name, "'#by_ptr' can only be applied to variable fields"); p->flags &= ~FieldFlag_by_ptr; } + if (p->flags&FieldFlag_no_capture) { + error(name, "'#no_capture' can only be applied to variable variadic fields"); + p->flags &= ~FieldFlag_no_capture; + } param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved); param->TypeName.is_type_alias = true; @@ -2054,6 +2058,15 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para p->flags &= ~FieldFlag_by_ptr; // Remove the flag } } + if (p->flags&FieldFlag_no_capture) { + if (!(is_variadic && variadic_index == variables.count)) { + error(name, "'#no_capture' can only be applied to a variadic parameter"); + p->flags &= ~FieldFlag_no_capture; + } else if (p->flags & FieldFlag_c_vararg) { + error(name, "'#no_capture' cannot be applied to a #c_vararg parameter"); + p->flags &= ~FieldFlag_no_capture; + } + } if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { @@ -2115,6 +2128,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (p->flags&FieldFlag_by_ptr) { param->flags |= EntityFlag_ByPtr; } + if (p->flags&FieldFlag_no_capture) { + param->flags |= EntityFlag_NoCapture; + } + param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it add_entity(ctx, scope, name, param); diff --git a/src/checker.cpp b/src/checker.cpp index 8756cce1a..abacc13cb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -184,6 +184,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) { ptr_set_init(&d->deps, 0); ptr_set_init(&d->type_info_deps, 0); d->labels.allocator = heap_allocator(); + d->no_captures.allocator = heap_allocator(); } gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) { diff --git a/src/checker.hpp b/src/checker.hpp index 781737140..17722f6b6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -181,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] { "Checked", }; +struct NoCaptureData { + Type *slice_type; // ..elem_type + isize max_count; +}; + // DeclInfo is used to store information of certain declarations to allow for "any order" usage struct DeclInfo { DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment @@ -219,6 +224,8 @@ struct DeclInfo { Array labels; + Array no_captures; + // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time struct lbModule *code_gen_module; }; diff --git a/src/entity.cpp b/src/entity.cpp index 41d84e0f7..db6ffdd52 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -45,7 +45,7 @@ enum EntityFlag : u64 { EntityFlag_Value = 1ull<<11, EntityFlag_BitFieldField = 1ull<<12, - + EntityFlag_NoCapture = 1ull<<13, // #no_capture EntityFlag_PolyConst = 1ull<<15, EntityFlag_NotExported = 1ull<<16, diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 005358734..dd1041702 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -296,6 +296,11 @@ enum lbProcedureFlag : u32 { lbProcedureFlag_DebugAllocaCopy = 1<<1, }; +struct lbNoCaptureData { + Type *slice_type; + lbAddr base_array; +}; + struct lbProcedure { u32 flags; u16 state_flags; @@ -336,6 +341,8 @@ struct lbProcedure { bool in_multi_assignment; Array raw_input_parameters; + Array no_captures; + LLVMValueRef temp_callee_return_struct_memory; Ast *curr_stmt; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 610c34de2..ec244e185 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -517,6 +517,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { lb_start_block(p, p->entry_block); map_init(&p->direct_parameters); + p->no_captures.allocator = heap_allocator(); GB_ASSERT(p->type != nullptr); @@ -3450,8 +3451,32 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } isize slice_len = var_args.count; if (slice_len > 0) { + lbAddr base_array = {}; + if (e->flags & EntityFlag_NoCapture) { + for (lbNoCaptureData const &nc : p->no_captures) { + if (are_types_identical(nc.slice_type, slice_type)) { + base_array = nc.base_array; + break; + } + } + DeclInfo *d = decl_info_of_entity(p->entity); + if (d != nullptr && base_array.addr.value == nullptr) { + for (NoCaptureData const &nc : d->no_captures) { + if (are_types_identical(nc.slice_type, slice_type)) { + base_array = lb_add_local_generated(p, alloc_type_array(elem_type, nc.max_count), true); + array_add(&p->no_captures, lbNoCaptureData{slice_type, base_array}); + break; + } + } + } + } + + if (base_array.addr.value == nullptr) { + base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); + } + GB_ASSERT(base_array.addr.value != nullptr); + lbAddr slice = lb_add_local_generated(p, slice_type, true); - lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true); for (isize i = 0; i < var_args.count; i++) { lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i); diff --git a/src/parser.cpp b/src/parser.cpp index 9ce3d563d..a6a146cfd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4014,6 +4014,7 @@ struct ParseFieldPrefixMapping { gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = { {str_lit("using"), Token_using, FieldFlag_using}, {str_lit("no_alias"), Token_Hash, FieldFlag_no_alias}, + {str_lit("no_capture"), Token_Hash, FieldFlag_no_capture}, {str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg}, {str_lit("const"), Token_Hash, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldFlag_any_int}, diff --git a/src/parser.hpp b/src/parser.hpp index 86b3393af..15176f789 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -330,9 +330,10 @@ enum FieldFlag : u32 { FieldFlag_subtype = 1<<7, FieldFlag_by_ptr = 1<<8, FieldFlag_no_broadcast = 1<<9, // disallow array programming + FieldFlag_no_capture = 1<<10, // Internal use by the parser only - FieldFlag_Tags = 1<<10, + FieldFlag_Tags = 1<<11, FieldFlag_Results = 1<<16, @@ -340,7 +341,10 @@ enum FieldFlag : u32 { FieldFlag_Invalid = 1u<<31, // Parameter List Restrictions - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg| + FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast| + FieldFlag_no_capture, + FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; -- cgit v1.2.3 From 139c1bcdda68c30c56ae26a9715a38074b9a1129 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 15 Jul 2024 00:25:41 +0100 Subject: Comment out debug code --- src/parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index a6a146cfd..4924dd37d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -112,7 +112,7 @@ gb_internal isize ast_node_size(AstKind kind) { } -gb_global std::atomic global_total_node_memory_allocated; +// gb_global std::atomic global_total_node_memory_allocated; // NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind) { @@ -122,7 +122,7 @@ gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind) { node->kind = kind; node->file_id = f ? f->id : 0; - global_total_node_memory_allocated.fetch_add(size); + // global_total_node_memory_allocated.fetch_add(size); return node; } -- cgit v1.2.3 From 018026d844c8ad3b625f019acee470dbb865d085 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 15 Jul 2024 00:36:00 +0100 Subject: Use `gb_zero_*` calls --- src/checker.cpp | 6 +++--- src/common_memory.cpp | 7 ------- src/gb/gb.h | 2 +- src/parser.cpp | 2 +- src/types.cpp | 2 +- 5 files changed, 6 insertions(+), 13 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 336440d32..0a671cc2d 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -658,7 +658,7 @@ gb_internal bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) { } } - zero_item(ve); + gb_zero_item(ve); ve->kind = VettedEntity_Shadowed; ve->entity = e; ve->other = shadowed; @@ -677,7 +677,7 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) { } case Entity_ImportName: case Entity_LibraryName: - zero_item(ve); + gb_zero_item(ve); ve->kind = VettedEntity_Unused; ve->entity = e; return true; @@ -1389,7 +1389,7 @@ gb_internal void reset_checker_context(CheckerContext *ctx, AstFile *file, Untyp auto type_path = ctx->type_path; array_clear(type_path); - zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg)); + gb_zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg)); ctx->file = nullptr; ctx->scope = builtin_pkg->scope; diff --git a/src/common_memory.cpp b/src/common_memory.cpp index 60e570eee..42a2125dc 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -2,13 +2,6 @@ #include #endif -gb_internal gb_inline void zero_size(void *ptr, isize len) { - memset(ptr, 0, len); -} - -#define zero_item(ptr) zero_size((ptr), gb_size_of(ptr)) - - template gb_internal gb_inline U bit_cast(V &v) { return reinterpret_cast(v); } diff --git a/src/gb/gb.h b/src/gb/gb.h index 22a30a04b..38dabc9bd 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2534,7 +2534,7 @@ gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) { gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); } gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); } -gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); } +gb_inline void gb_zero_size(void *ptr, isize size) { memset(ptr, 0, size); } #if defined(_MSC_VER) && !defined(__clang__) diff --git a/src/parser.cpp b/src/parser.cpp index 4924dd37d..02c37815b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5413,7 +5413,7 @@ gb_internal ParseFileError init_ast_file(AstFile *f, String const &fullpath, Tok if (!string_ends_with(f->fullpath, str_lit(".odin"))) { return ParseFile_WrongExtension; } - zero_item(&f->tokenizer); + gb_zero_item(&f->tokenizer); f->tokenizer.curr_file_id = f->id; TokenizerInitError err = init_tokenizer_from_fullpath(&f->tokenizer, f->fullpath, build_context.copy_file_contents); diff --git a/src/types.cpp b/src/types.cpp index 92b187cdb..fdc174d81 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -964,7 +964,7 @@ gb_internal Type *alloc_type(TypeKind kind) { // gbAllocator a = heap_allocator(); gbAllocator a = permanent_allocator(); Type *t = gb_alloc_item(a, Type); - zero_item(t); + gb_zero_item(t); t->kind = kind; t->cached_size = -1; t->cached_align = -1; -- cgit v1.2.3 From a45e05bb180ad5ac3f9bc4199ebbf0b3bcadbf25 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 15 Jul 2024 01:36:54 +0100 Subject: Remove need for `BlockingMutex` in `Arena` --- src/common_memory.cpp | 15 +++------------ src/parser.cpp | 2 +- src/parser.hpp | 4 +--- 3 files changed, 5 insertions(+), 16 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/common_memory.cpp b/src/common_memory.cpp index bfe4c2e68..47b2796a9 100644 --- a/src/common_memory.cpp +++ b/src/common_memory.cpp @@ -45,7 +45,7 @@ struct MemoryBlock { struct Arena { MemoryBlock * curr_block; isize minimum_block_size; - BlockingMutex mutex; + // BlockingMutex mutex; isize temp_count; Thread * parent_thread; }; @@ -82,12 +82,7 @@ gb_internal void thread_init_arenas(Thread *t) { gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) { GB_ASSERT(gb_is_power_of_two(alignment)); - - if (arena->parent_thread == nullptr) { - mutex_lock(&arena->mutex); - } else { - GB_ASSERT(arena->parent_thread == get_current_thread()); - } + GB_ASSERT(arena->parent_thread == get_current_thread()); isize size = 0; if (arena->curr_block != nullptr) { @@ -113,11 +108,7 @@ gb_internal void *arena_alloc(Arena *arena, isize min_size, isize alignment) { curr_block->used += size; GB_ASSERT(curr_block->used <= curr_block->size); - - if (arena->parent_thread == nullptr) { - mutex_unlock(&arena->mutex); - } - + // NOTE(bill): memory will be zeroed by default due to virtual memory return ptr; } diff --git a/src/parser.cpp b/src/parser.cpp index 02c37815b..5a3fc1634 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -118,7 +118,7 @@ gb_internal isize ast_node_size(AstKind kind) { gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind) { isize size = ast_node_size(kind); - Ast *node = cast(Ast *)arena_alloc(&global_thread_local_ast_arena, size, 16); + Ast *node = cast(Ast *)arena_alloc(get_arena(ThreadArena_Permanent), size, 16); node->kind = kind; node->file_id = f ? f->id : 0; diff --git a/src/parser.hpp b/src/parser.hpp index 451cdf53d..565a8e621 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -878,10 +878,8 @@ gb_internal gb_inline bool is_ast_when_stmt(Ast *node) { return node->kind == Ast_WhenStmt; } -gb_global gb_thread_local Arena global_thread_local_ast_arena = {}; - gb_internal gb_inline gbAllocator ast_allocator(AstFile *f) { - return arena_allocator(&global_thread_local_ast_arena); + return permanent_allocator(); } gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind); -- cgit v1.2.3