From fe0b5bf4e27912c49f6c5eab817cbf514b0b22e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 Jan 2022 23:28:59 +0000 Subject: Parse comments on enums fields --- src/parser.cpp | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 076c698ff..ebe65cee1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -693,6 +693,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) { return result; } + +Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) { + Ast *result = alloc_ast_node(f, Ast_EnumFieldValue); + result->EnumFieldValue.name = name; + result->EnumFieldValue.value = value; + result->EnumFieldValue.docs = docs; + result->EnumFieldValue.comment = comment; + return result; +} + Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token open, Token close) { Ast *result = alloc_ast_node(f, Ast_CompoundLit); result->CompoundLit.type = type; @@ -1689,6 +1699,36 @@ Array parse_element_list(AstFile *f) { return elems; } +Array parse_enum_field_list(AstFile *f) { + auto elems = array_make(heap_allocator()); + + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + CommentGroup *docs = f->lead_comment; + CommentGroup *comment = nullptr; + Ast *name = parse_value(f); + Ast *value = nullptr; + if (f->curr_token.kind == Token_Eq) { + Token eq = expect_token(f, Token_Eq); + value = parse_value(f); + } + + comment = f->line_comment; + + Ast *elem = ast_enum_field_value(f, name, value, docs, comment); + array_add(&elems, elem); + + if (!allow_token(f, Token_Comma)) { + break; + } + + if (!elem->EnumFieldValue.comment) { + elem->EnumFieldValue.comment = f->line_comment; + } + } + + return elems; +} Ast *parse_literal_value(AstFile *f, Ast *type) { Array elems = {}; @@ -2449,7 +2489,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { skip_possible_newline_for_literal(f); Token open = expect_token(f, Token_OpenBrace); - Array values = parse_element_list(f); + Array values = parse_enum_field_list(f); Token close = expect_closing_brace_of_field_list(f); return ast_enum_type(f, token, base_type, values); -- cgit v1.2.3 From c0479f1564119603f022f5f3d22dd8dc3a1e5983 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 Jan 2022 23:42:04 +0000 Subject: Handle line comment better --- src/parser.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index ebe65cee1..108411cd0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1244,7 +1244,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) { return comments; } -void comsume_comment_groups(AstFile *f, Token prev) { +void consume_comment_groups(AstFile *f, Token prev) { if (f->curr_token.kind == Token_Comment) { CommentGroup *comment = nullptr; isize end_line = 0; @@ -1288,7 +1288,7 @@ Token advance_token(AstFile *f) { if (ok) { switch (f->curr_token.kind) { case Token_Comment: - comsume_comment_groups(f, prev); + consume_comment_groups(f, prev); break; case Token_Semicolon: if (ignore_newlines(f) && f->curr_token.string == "\n") { @@ -1699,6 +1699,16 @@ Array parse_element_list(AstFile *f) { return elems; } +CommentGroup *consume_line_comment(AstFile *f) { + CommentGroup *comment = f->line_comment; + if (f->line_comment == f->lead_comment) { + f->lead_comment = nullptr; + } + f->line_comment = nullptr; + return comment; + +} + Array parse_enum_field_list(AstFile *f) { auto elems = array_make(heap_allocator()); @@ -1713,7 +1723,7 @@ Array parse_enum_field_list(AstFile *f) { value = parse_value(f); } - comment = f->line_comment; + comment = consume_line_comment(f); Ast *elem = ast_enum_field_value(f, name, value, docs, comment); array_add(&elems, elem); @@ -1723,7 +1733,7 @@ Array parse_enum_field_list(AstFile *f) { } if (!elem->EnumFieldValue.comment) { - elem->EnumFieldValue.comment = f->line_comment; + elem->EnumFieldValue.comment = consume_line_comment(f); } } @@ -5438,7 +5448,7 @@ bool parse_file(Parser *p, AstFile *f) { String filepath = f->tokenizer.fullpath; String base_dir = dir_from_path(filepath); if (f->curr_token.kind == Token_Comment) { - comsume_comment_groups(f, f->prev_token); + consume_comment_groups(f, f->prev_token); } CommentGroup *docs = f->lead_comment; -- cgit v1.2.3 From 24e7356825a473cba0a1e9962470be73d60ad248 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 27 Jan 2022 16:08:47 +0000 Subject: Add `#no_type_assert` and `#type_assert` to disable implicit type assertions with `x.(T)` --- src/check_expr.cpp | 8 +++++ src/check_stmt.cpp | 8 +++++ src/checker.cpp | 12 ++++++++ src/llvm_backend_expr.cpp | 72 +++++++++++++++++++++++++------------------- src/llvm_backend_stmt.cpp | 7 +++++ src/llvm_backend_utility.cpp | 41 ++++++++++++++++--------- src/parser.cpp | 36 ++++++++++++++++++++++ src/parser.hpp | 4 +++ 8 files changed, 142 insertions(+), 46 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 276e9d0bb..fb5a90f5a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6883,6 +6883,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type out &= ~StateFlag_no_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } + c->state_flags = out; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 94b7561c7..f9e55ab37 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) { out &= ~StateFlag_no_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } + ctx->state_flags = out; } diff --git a/src/checker.cpp b/src/checker.cpp index e0c756bb8..038709056 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4875,6 +4875,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0; + bool type_assert = (pi->tags & ProcTag_type_assert) != 0; + bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0; + if (bounds_check) { ctx.state_flags |= StateFlag_bounds_check; ctx.state_flags &= ~StateFlag_no_bounds_check; @@ -4882,6 +4885,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc ctx.state_flags |= StateFlag_no_bounds_check; ctx.state_flags &= ~StateFlag_bounds_check; } + + if (type_assert) { + ctx.state_flags |= StateFlag_type_assert; + ctx.state_flags &= ~StateFlag_no_type_assert; + } else if (no_type_assert) { + ctx.state_flags |= StateFlag_no_type_assert; + ctx.state_flags &= ~StateFlag_type_assert; + } + if (pi->body != nullptr && e != nullptr) { GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0); } diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 9b2e26434..ea031ee56 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2768,27 +2768,29 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { Type *src_type = type_deref(v.type); Type *dst_type = type; - lbValue src_tag = {}; - lbValue dst_tag = {}; - if (is_type_union_maybe_pointer(src_type)) { - src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v); - dst_tag = lb_const_bool(p->module, t_bool, true); - } else { - src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); - dst_tag = lb_const_union_tag(p->module, src_type, dst_type); - } - lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); - auto args = array_make(permanent_allocator(), 6); - args[0] = ok; + if ((p->state_flags & StateFlag_no_type_assert) == 0) { + lbValue src_tag = {}; + lbValue dst_tag = {}; + if (is_type_union_maybe_pointer(src_type)) { + src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v); + dst_tag = lb_const_bool(p->module, t_bool, true); + } else { + src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v)); + dst_tag = lb_const_union_tag(p->module, src_type, dst_type); + } + lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag); + auto args = array_make(permanent_allocator(), 6); + args[0] = ok; - args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(p->module, t_i32, pos.line); - args[3] = lb_const_int(p->module, t_i32, pos.column); + args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(p->module, t_i32, pos.line); + args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = lb_typeid(p->module, src_type); - args[5] = lb_typeid(p->module, dst_type); - lb_emit_runtime_call(p, "type_assertion_check", args); + args[4] = lb_typeid(p->module, src_type); + args[5] = lb_typeid(p->module, dst_type); + lb_emit_runtime_call(p, "type_assertion_check", args); + } lbValue data_ptr = v; return lb_emit_conv(p, data_ptr, tv.type); @@ -2797,23 +2799,23 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { if (is_type_pointer(v.type)) { v = lb_emit_load(p, v); } - lbValue data_ptr = lb_emit_struct_ev(p, v, 0); - lbValue any_id = lb_emit_struct_ev(p, v, 1); - lbValue id = lb_typeid(p->module, type); + if ((p->state_flags & StateFlag_no_type_assert) == 0) { + lbValue any_id = lb_emit_struct_ev(p, v, 1); + lbValue id = lb_typeid(p->module, type); + lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); + auto args = array_make(permanent_allocator(), 6); + args[0] = ok; - lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id); - auto args = array_make(permanent_allocator(), 6); - args[0] = ok; - - args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(p->module, t_i32, pos.line); - args[3] = lb_const_int(p->module, t_i32, pos.column); + args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(p->module, t_i32, pos.line); + args[3] = lb_const_int(p->module, t_i32, pos.column); - args[4] = any_id; - args[5] = id; - lb_emit_runtime_call(p, "type_assertion_check", args); + args[4] = any_id; + args[5] = id; + lb_emit_runtime_call(p, "type_assertion_check", args); + } return lb_emit_conv(p, data_ptr, tv.type); } else { @@ -2843,6 +2845,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { out &= ~StateFlag_bounds_check; } + if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } else if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } + p->state_flags = out; } diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 3375ceda9..916c0433e 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1991,6 +1991,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) { out |= StateFlag_no_bounds_check; out &= ~StateFlag_bounds_check; } + if (in & StateFlag_no_type_assert) { + out |= StateFlag_no_type_assert; + out &= ~StateFlag_type_assert; + } else if (in & StateFlag_type_assert) { + out |= StateFlag_type_assert; + out &= ~StateFlag_no_type_assert; + } p->state_flags = out; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 5b1b11b44..7e2bd7daa 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -626,6 +626,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lbValue value_ = lb_address_from_load_or_generate_local(p, value); + if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) { + // just do a bit cast of the data at the front + lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type)); + return lb_emit_load(p, ptr); + } + lbValue tag = {}; lbValue dst_tag = {}; lbValue cond = {}; @@ -666,23 +672,22 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p lb_start_block(p, end_block); if (!is_tuple) { - { - // NOTE(bill): Panic on invalid conversion - Type *dst_type = tuple->Tuple.variables[0]->type; + GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0); + // NOTE(bill): Panic on invalid conversion + Type *dst_type = tuple->Tuple.variables[0]->type; - lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); - auto args = array_make(permanent_allocator(), 7); - args[0] = ok; + lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); + auto args = array_make(permanent_allocator(), 7); + args[0] = ok; - args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); - args[2] = lb_const_int(m, t_i32, pos.line); - args[3] = lb_const_int(m, t_i32, pos.column); + args[1] = lb_const_string(m, get_file_path_string(pos.file_id)); + args[2] = lb_const_int(m, t_i32, pos.line); + args[3] = lb_const_int(m, t_i32, pos.column); - args[4] = lb_typeid(m, src_type); - args[5] = lb_typeid(m, dst_type); - args[6] = lb_emit_conv(p, value_, t_rawptr); - lb_emit_runtime_call(p, "type_assertion_check2", args); - } + args[4] = lb_typeid(m, src_type); + args[5] = lb_typeid(m, dst_type); + args[6] = lb_emit_conv(p, value_, t_rawptr); + lb_emit_runtime_call(p, "type_assertion_check2", args); return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0)); } @@ -706,6 +711,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos } Type *dst_type = tuple->Tuple.variables[0]->type; + if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) { + // just do a bit cast of the data at the front + lbValue ptr = lb_emit_struct_ev(p, value, 0); + ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type)); + return lb_addr(ptr); + } + lbAddr v = lb_add_local_generated(p, tuple, true); lbValue dst_typeid = lb_typeid(m, dst_type); @@ -731,7 +743,6 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos if (!is_tuple) { // NOTE(bill): Panic on invalid conversion - lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1)); auto args = array_make(permanent_allocator(), 7); args[0] = ok; diff --git a/src/parser.cpp b/src/parser.cpp index 108411cd0..9cc9adfc9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1843,6 +1843,8 @@ void parse_proc_tags(AstFile *f, u64 *tags) { ELSE_IF_ADD_TAG(require_results) ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(no_bounds_check) + ELSE_IF_ADD_TAG(type_assert) + ELSE_IF_ADD_TAG(no_type_assert) else { syntax_error(tag_expr, "Unknown procedure type tag #%.*s", LIT(tag_name)); } @@ -1853,6 +1855,10 @@ void parse_proc_tags(AstFile *f, u64 *tags) { if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) { syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure"); } + + if ((*tags & ProcTag_type_assert) && (*tags & ProcTag_no_type_assert)) { + syntax_error(f->curr_token, "You cannot apply both #type_assert and #no_type_assert to a procedure"); + } } @@ -2000,11 +2006,23 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta syntax_error(tag_token, "#bounds_check and #no_bounds_check cannot be applied together"); } break; + case StateFlag_type_assert: + if ((s->state_flags & StateFlag_no_type_assert) != 0) { + syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together"); + } + break; + case StateFlag_no_type_assert: + if ((s->state_flags & StateFlag_type_assert) != 0) { + syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together"); + } + break; } switch (state_flag) { case StateFlag_bounds_check: case StateFlag_no_bounds_check: + case StateFlag_type_assert: + case StateFlag_no_type_assert: switch (s->kind) { case Ast_BlockStmt: case Ast_IfStmt: @@ -2128,6 +2146,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { } else if (name.string == "no_bounds_check") { Ast *operand = parse_expr(f, lhs); return parse_check_directive_for_statement(operand, name, StateFlag_no_bounds_check); + } else if (name.string == "type_assert") { + Ast *operand = parse_expr(f, lhs); + return parse_check_directive_for_statement(operand, name, StateFlag_type_assert); + } else if (name.string == "no_type_assert") { + Ast *operand = parse_expr(f, lhs); + return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert); } else if (name.string == "relative") { Ast *tag = ast_basic_directive(f, token, name); tag = parse_call_expr(f, tag); @@ -2224,6 +2248,12 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (tags & ProcTag_bounds_check) { body->state_flags |= StateFlag_bounds_check; } + if (tags & ProcTag_no_type_assert) { + body->state_flags |= StateFlag_no_type_assert; + } + if (tags & ProcTag_type_assert) { + body->state_flags |= StateFlag_type_assert; + } return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { @@ -4611,6 +4641,12 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "no_bounds_check") { s = parse_stmt(f); return parse_check_directive_for_statement(s, name, StateFlag_no_bounds_check); + } else if (tag == "type_assert") { + s = parse_stmt(f); + return parse_check_directive_for_statement(s, name, StateFlag_type_assert); + } else if (tag == "no_type_assert") { + s = parse_stmt(f); + return parse_check_directive_for_statement(s, name, StateFlag_no_type_assert); } else if (tag == "partial") { s = parse_stmt(f); switch (s->kind) { diff --git a/src/parser.hpp b/src/parser.hpp index b005a4465..656f709e8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -226,6 +226,8 @@ enum ProcInlining { enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, + ProcTag_type_assert = 1<<2, + ProcTag_no_type_assert = 1<<3, ProcTag_require_results = 1<<4, ProcTag_optional_ok = 1<<5, @@ -258,6 +260,8 @@ ProcCallingConvention default_calling_convention(void) { enum StateFlag : u8 { StateFlag_bounds_check = 1<<0, StateFlag_no_bounds_check = 1<<1, + StateFlag_type_assert = 1<<2, + StateFlag_no_type_assert = 1<<3, StateFlag_BeenHandled = 1<<7, }; -- cgit v1.2.3 From 67ba05cb7cfee60daaf257230ddbc4f1e0a9f8ac Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 31 Jan 2022 19:33:02 +0000 Subject: Correct false positive check in `check_unique_package_names` --- src/checker.cpp | 10 ++++++++-- src/parser.cpp | 5 +++++ src/parser_pos.cpp | 6 ++++++ 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index 038709056..d9a1af0d1 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5307,12 +5307,18 @@ void check_unique_package_names(Checker *c) { string_map_set(&pkgs, key, pkg); continue; } + auto *this = pkg->files[0]->pkg_decl; + auto *other = (*found)->files[0]->pkg_decl; + if (this == other) { + // NOTE(bill): A false positive was found, ignore it + continue; + } - error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name)); + error(this, "Duplicate declaration of 'package %.*s'", LIT(name)); error_line("\tA package name must be unique\n" "\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n" "\tA package name is required for link name prefixing to have a consistent ABI\n"); - error((*found)->files[0]->pkg_decl, "found at previous location"); + error(other, "found at previous location"); } } diff --git a/src/parser.cpp b/src/parser.cpp index 9cc9adfc9..7a858e520 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -183,6 +183,11 @@ Ast *clone_ast(Ast *node) { n->FieldValue.value = clone_ast(n->FieldValue.value); break; + case Ast_EnumFieldValue: + n->EnumFieldValue.name = clone_ast(n->EnumFieldValue.name); + n->EnumFieldValue.value = clone_ast(n->EnumFieldValue.value); + break; + case Ast_TernaryIfExpr: n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x); n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond); diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index 6ef0db215..54c3ec1f1 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -39,6 +39,7 @@ Token ast_token(Ast *node) { case Ast_SliceExpr: return node->SliceExpr.open; case Ast_Ellipsis: return node->Ellipsis.token; case Ast_FieldValue: return node->FieldValue.eq; + case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name); case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); @@ -178,6 +179,11 @@ Token ast_end_token(Ast *node) { } return node->Ellipsis.token; case Ast_FieldValue: return ast_end_token(node->FieldValue.value); + case Ast_EnumFieldValue: + if (node->EnumFieldValue.value) { + return ast_end_token(node->EnumFieldValue.value); + } + return ast_end_token(node->EnumFieldValue.name); case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y); case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y); -- cgit v1.2.3 From 78815778ee399b4df1c5f7c44522c792b9dc3e23 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Feb 2022 15:28:49 +0000 Subject: Add `//+private file` to complement `//+private` (`//+private package`) --- src/checker.cpp | 9 ++++++--- src/entity.cpp | 2 +- src/parser.cpp | 12 ++++++++++-- src/parser.hpp | 8 +++++--- src/string.cpp | 12 ++++++++++-- 5 files changed, 32 insertions(+), 11 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index d50d4d176..4dcb5120f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3467,9 +3467,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { if (entity_visibility_kind == EntityVisiblity_Public && (c->scope->flags&ScopeFlag_File) && - c->scope->file && - (c->scope->file->flags & AstFile_IsPrivate)) { - entity_visibility_kind = EntityVisiblity_PrivateToPackage; + c->scope->file) { + if (c->scope->file->flags & AstFile_IsPrivatePkg) { + entity_visibility_kind = EntityVisiblity_PrivateToPackage; + } else if (c->scope->file->flags & AstFile_IsPrivateFile) { + entity_visibility_kind = EntityVisiblity_PrivateToFile; + } } if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) { diff --git a/src/entity.cpp b/src/entity.cpp index a0438a9f4..8327a517e 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -245,7 +245,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) { if (e->flags & EntityFlag_NotExported) { return false; } - if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) { + if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) { return false; } diff --git a/src/parser.cpp b/src/parser.cpp index 7a858e520..bf8c909c2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5535,8 +5535,16 @@ bool parse_file(Parser *p, AstFile *f) { if (!parse_build_tag(tok, lc)) { return false; } - } else if (lc == "+private") { - f->flags |= AstFile_IsPrivate; + } else if (string_starts_with(lc, str_lit("+private"))) { + f->flags |= AstFile_IsPrivatePkg; + String command = string_trim_starts_with(lc, str_lit("+private ")); + if (lc == "+private") { + f->flags |= AstFile_IsPrivatePkg; + } else if (command == "package") { + f->flags |= AstFile_IsPrivatePkg; + } else if (command == "file") { + f->flags |= AstFile_IsPrivateFile; + } } else if (lc == "+lazy") { if (build_context.ignore_lazy) { // Ignore diff --git a/src/parser.hpp b/src/parser.hpp index 656f709e8..0712e83cb 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -78,9 +78,11 @@ struct ImportedFile { }; enum AstFileFlag : u32 { - AstFile_IsPrivate = 1<<0, - AstFile_IsTest = 1<<1, - AstFile_IsLazy = 1<<2, + AstFile_IsPrivatePkg = 1<<0, + AstFile_IsPrivateFile = 1<<1, + + AstFile_IsTest = 1<<3, + AstFile_IsLazy = 1<<4, }; enum AstDelayQueueKind { diff --git a/src/string.cpp b/src/string.cpp index 800378689..eb6058f78 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -195,8 +195,6 @@ template bool operator > (String const &a, char const (&b)[N]) { retu template bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); } template bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); } - - gb_inline bool string_starts_with(String const &s, String const &prefix) { if (prefix.len > s.len) { return false; @@ -230,6 +228,16 @@ gb_inline bool string_ends_with(String const &s, u8 suffix) { return s[s.len-1] == suffix; } + + +gb_inline String string_trim_starts_with(String const &s, String const &prefix) { + if (string_starts_with(s, prefix)) { + return substring(s, prefix.len, s.len); + } + return s; +} + + gb_inline isize string_extension_position(String const &str) { isize dot_pos = -1; isize i = str.len; -- cgit v1.2.3 From 5db603ded27b8cb6dac444fa660009fd7ecc5184 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 2 Feb 2022 15:39:41 +0000 Subject: Minor sanity clean up --- src/parser.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index bf8c909c2..6db71bc4a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5538,6 +5538,7 @@ bool parse_file(Parser *p, AstFile *f) { } else if (string_starts_with(lc, str_lit("+private"))) { f->flags |= AstFile_IsPrivatePkg; String command = string_trim_starts_with(lc, str_lit("+private ")); + command = string_trim_whitespace(command); if (lc == "+private") { f->flags |= AstFile_IsPrivatePkg; } else if (command == "package") { -- cgit v1.2.3 From 97be86710306702a672309b23fbe8d38f1e6eeec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 13:01:15 +0000 Subject: Rename `#partial[Enum]Type` to `#sparse[Enum]Type` for non-contiguous enum fields --- core/reflect/types.odin | 3 +++ core/runtime/core.odin | 1 + core/runtime/print.odin | 3 +++ src/check_type.cpp | 13 +++++++------ src/llvm_backend_type.cpp | 4 +++- src/parser.cpp | 2 +- src/types.cpp | 4 ++++ 7 files changed, 22 insertions(+), 8 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 74778013a..a9a4a8d48 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -472,6 +472,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - write_type(w, info.elem, &n) or_return case Type_Info_Enumerated_Array: + if info.is_sparse { + io.write_string(w, "#sparse", &n) or_return + } io.write_string(w, "[", &n) or_return write_type(w, info.index, &n) or_return io.write_string(w, "]", &n) or_return diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 424650828..35144473b 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -95,6 +95,7 @@ Type_Info_Enumerated_Array :: struct { count: int, min_value: Type_Info_Enum_Value, max_value: Type_Info_Enum_Value, + is_sparse: bool, } Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int} Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int} diff --git a/core/runtime/print.odin b/core/runtime/print.odin index 8c0b65864..06740bc75 100644 --- a/core/runtime/print.odin +++ b/core/runtime/print.odin @@ -260,6 +260,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) { print_type(info.elem) case Type_Info_Enumerated_Array: + if info.is_sparse { + print_string("#sparse") + } print_byte('[') print_type(info.index) print_byte(']') diff --git a/src/check_type.cpp b/src/check_type.cpp index a6d82c86e..6d3e32466 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2713,29 +2713,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid); - bool is_partial = false; + bool is_sparse = false; if (at->tag != nullptr) { GB_ASSERT(at->tag->kind == Ast_BasicDirective); String name = at->tag->BasicDirective.name.string; - if (name == "partial") { - is_partial = true; + if (name == "sparse") { + is_sparse = true; } else { error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); } } - if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) { + if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { error(e, "Non-contiguous enumeration used as an index in an enumerated array"); long long ea_count = cast(long long)t->EnumeratedArray.count; long long enum_count = cast(long long)bt->Enum.fields.count; error_line("\tenumerated array length: %lld\n", ea_count); error_line("\tenum field count: %lld\n", enum_count); - error_line("\tSuggestion: prepend #partial to the enumerated array to allow for non-named elements\n"); + error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); if (2*enum_count < ea_count) { error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); - error_line("\t this warning will be removed if #partial is applied\n"); + error_line("\t this warning will be removed if #sparse is applied\n"); } } + t->EnumeratedArray.is_sparse = is_sparse; *type = t; diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e1332c6f3..1d6297164 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -454,7 +454,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da case Type_EnumeratedArray: { tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr); - LLVMValueRef vals[6] = { + LLVMValueRef vals[7] = { lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value, lb_get_type_info_ptr(m, t->EnumeratedArray.index).value, lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value, @@ -463,6 +463,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da // Unions LLVMConstNull(lb_type(m, t_type_info_enum_value)), LLVMConstNull(lb_type(m, t_type_info_enum_value)), + + lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value, }; lbValue res = {}; diff --git a/src/parser.cpp b/src/parser.cpp index 6db71bc4a..7302b18a9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2134,7 +2134,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { break; } return original_type; - } else if (name.string == "partial") { + } else if (name.string == "sparse") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); diff --git a/src/types.cpp b/src/types.cpp index 07951196a..e0d35a12c 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -221,6 +221,7 @@ struct TypeProc { ExactValue *max_value; \ i64 count; \ TokenKind op; \ + bool is_sparse; \ }) \ TYPE_KIND(Slice, struct { Type *elem; }) \ TYPE_KIND(DynamicArray, struct { Type *elem; }) \ @@ -3830,6 +3831,9 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_EnumeratedArray: + if (type->EnumeratedArray.is_sparse) { + str = gb_string_appendc(str, "#sparse"); + } str = gb_string_append_rune(str, '['); str = write_type_to_string(str, type->EnumeratedArray.index); str = gb_string_append_rune(str, ']'); -- cgit v1.2.3 From 6418ec3b21de26bac4b291a2ad8e58c011c21c38 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Feb 2022 13:09:16 +0000 Subject: Correct `#sparse` usage and error messages --- core/math/big/common.odin | 2 +- examples/demo/demo.odin | 16 ++++++++-------- src/parser.cpp | 16 ++++++++++++++++ src/parser.hpp | 1 + 4 files changed, 26 insertions(+), 9 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/math/big/common.odin b/core/math/big/common.odin index 2b34a9163..e1198c352 100644 --- a/core/math/big/common.odin +++ b/core/math/big/common.odin @@ -172,7 +172,7 @@ Error :: enum int { Unimplemented = 127, } -Error_String :: #partial [Error]string{ +Error_String :: #sparse[Error]string{ .Okay = "Okay", .Out_Of_Memory = "Out of memory", .Invalid_Pointer = "Invalid pointer", diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 3e34e3d49..8c6ea0fa4 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1921,14 +1921,14 @@ constant_literal_expressions :: proc() { fmt.println("-------") - Partial_Baz :: enum{A=5, B, C, D=16} - #assert(len(Partial_Baz) < len(#partial [Partial_Baz]int)) - PARTIAL_ENUM_ARRAY_CONST :: #partial [Partial_Baz]int{.A ..= .C = 1, .D = 16} - - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.A]) - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.B]) - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.C]) - fmt.println(PARTIAL_ENUM_ARRAY_CONST[.D]) + Sparse_Baz :: enum{A=5, B, C, D=16} + #assert(len(Sparse_Baz) < len(#sparse[Sparse_Baz]int)) + SPARSE_ENUM_ARRAY_CONST :: #sparse[Sparse_Baz]int{.A ..= .C = 1, .D = 16} + + fmt.println(SPARSE_ENUM_ARRAY_CONST[.A]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.B]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.C]) + fmt.println(SPARSE_ENUM_ARRAY_CONST[.D]) fmt.println("-------") diff --git a/src/parser.cpp b/src/parser.cpp index 7302b18a9..0914c77ca 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2134,6 +2134,22 @@ Ast *parse_operand(AstFile *f, bool lhs) { break; } return original_type; + } else if (name.string == "partial") { + Ast *tag = ast_basic_directive(f, token, name); + Ast *original_expr = parse_expr(f, lhs); + Ast *expr = unparen_expr(original_expr); + switch (expr->kind) { + case Ast_ArrayType: + syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types"); + break; + case Ast_CompoundLit: + expr->CompoundLit.tag = tag; + break; + default: + syntax_error(expr, "Expected a compound literal after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[expr->kind])); + break; + } + return original_expr; } else if (name.string == "sparse") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); diff --git a/src/parser.hpp b/src/parser.hpp index 0712e83cb..ff0df0382 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -350,6 +350,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { Slice elems; \ Token open, close; \ i64 max_count; \ + Ast *tag; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ -- cgit v1.2.3 From 65dedbb1caaa785a444d32a7a15adaf6c396b07f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 11:54:15 +0000 Subject: Add `#subtype` struct field prefix, required to have a COM interface hierarchy --- src/check_expr.cpp | 3 +++ src/check_type.cpp | 18 ++++++++++++++++++ src/entity.cpp | 5 +++++ src/parser.cpp | 4 +++- src/parser.hpp | 3 ++- src/types.cpp | 12 ++++++------ 6 files changed, 37 insertions(+), 8 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7fb0e44f2..884f1bb9f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9774,6 +9774,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { if (f->flags&FieldFlag_const) { str = gb_string_appendc(str, "#const "); } + if (f->flags&FieldFlag_subtype) { + str = gb_string_appendc(str, "#subtype "); + } for_array(i, f->names) { Ast *name = f->names[i]; diff --git a/src/check_type.cpp b/src/check_type.cpp index 32340070e..7e0ad2bd9 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields } bool is_using = (p->flags&FieldFlag_using) != 0; + bool is_subtype = (p->flags&FieldFlag_subtype) != 0; for_array(j, p->names) { Ast *name = p->names[j]; @@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx, ctx->scope, name, field); field->Variable.field_group_index = field_group_index; + if (is_subtype) { + field->flags |= EntityFlag_Subtype; + } if (j == 0) { field->Variable.docs = docs; @@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields populate_using_entity_scope(ctx, node, p, type); } + + if (is_subtype && p->names.count > 0) { + Type *first_type = fields_array[fields_array.count-1]->type; + Type *t = base_type(type_deref(first_type)); + + if (!does_field_type_allow_using(t) && + p->names.count >= 1 && + p->names[0]->kind == Ast_Ident) { + Token name_token = p->names[0]->Ident.token; + gbString type_str = type_to_string(first_type); + error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str); + gb_string_free(type_str); + } + } } *fields = slice_from_array(fields_array); diff --git a/src/entity.cpp b/src/entity.cpp index df8ee3faa..f5720293f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -74,6 +74,7 @@ enum EntityFlag : u64 { EntityFlag_Test = 1ull<<30, EntityFlag_Init = 1ull<<31, + EntityFlag_Subtype = 1ull<<32, EntityFlag_CustomLinkName = 1ull<<40, EntityFlag_CustomLinkage_Internal = 1ull<<41, @@ -86,6 +87,10 @@ enum EntityFlag : u64 { EntityFlag_Overridden = 1ull<<63, }; +enum : u64 { + EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype, +}; + enum EntityState : u32 { EntityState_Unresolved = 0, EntityState_InProgress = 1, diff --git a/src/parser.cpp b/src/parser.cpp index 0914c77ca..b55d745f1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3504,12 +3504,13 @@ enum FieldPrefixKind : i32 { FieldPrefix_Unknown = -1, FieldPrefix_Invalid = 0, - FieldPrefix_using, + FieldPrefix_using, // implies #subtype FieldPrefix_const, FieldPrefix_no_alias, FieldPrefix_c_vararg, FieldPrefix_auto_cast, FieldPrefix_any_int, + FieldPrefix_subtype, // does not imply `using` semantics }; struct ParseFieldPrefixMapping { @@ -3526,6 +3527,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { {str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg}, {str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int}, + {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype}, }; diff --git a/src/parser.hpp b/src/parser.hpp index ff0df0382..fb84210b3 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,6 +282,7 @@ enum FieldFlag : u32 { FieldFlag_auto_cast = 1<<4, FieldFlag_const = 1<<5, FieldFlag_any_int = 1<<6, + FieldFlag_subtype = 1<<7, // Internal use by the parser only FieldFlag_Tags = 1<<10, @@ -289,7 +290,7 @@ enum FieldFlag : u32 { // Parameter List Restrictions FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int, - FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags, + FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; enum StmtAllowFlag { diff --git a/src/types.cpp b/src/types.cpp index 78958146b..2c1e6162f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2334,7 +2334,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { GB_ASSERT(is_type_struct(src) || is_type_union(src)); for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) { + if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) { if (are_types_identical(dst, f->type)) { return f->token.string; } @@ -2343,7 +2343,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) { return f->token.string; } } - if (is_type_struct(f->type)) { + if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) { String name = lookup_subtype_polymorphic_field(dst, f->type); if (name.len > 0) { return name; @@ -2489,9 +2489,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (xf->token.string != yf->token.string) { return false; } - bool xf_is_using = (xf->flags&EntityFlag_Using) != 0; - bool yf_is_using = (yf->flags&EntityFlag_Using) != 0; - if (xf_is_using ^ yf_is_using) { + u64 xf_flags = (xf->flags&EntityFlags_IsSubtype); + u64 yf_flags = (yf->flags&EntityFlags_IsSubtype); + if (xf_flags != yf_flags) { return false; } } @@ -3813,7 +3813,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0 for_array(i, src->Struct.fields) { Entity *f = src->Struct.fields[i]; - if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { + if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) { continue; } -- cgit v1.2.3 From db6bd9b358f17c0259ff5fe6411ce93407613338 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 16 Feb 2022 16:03:49 +0000 Subject: Allow sysv and win64 calling conventions to be used on any platform on amd64 --- core/runtime/core.odin | 5 +++++ src/check_type.cpp | 19 +++++++++++++++++++ src/docs_writer.cpp | 35 +---------------------------------- src/llvm_abi.cpp | 6 ++++++ src/llvm_backend.hpp | 4 ++++ src/llvm_backend_proc.cpp | 2 +- src/parser.cpp | 6 ++++++ src/parser.hpp | 18 ++++++++++++++++++ 8 files changed, 60 insertions(+), 35 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index fec51f236..229d70417 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -33,6 +33,11 @@ Calling_Convention :: enum u8 { None = 6, Naked = 7, + + _ = 8, // reserved + + Win64 = 9, + SysV = 10, } Type_Info_Enum_Value :: distinct i64 diff --git a/src/check_type.cpp b/src/check_type.cpp index 32340070e..a11f5234b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1910,6 +1910,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, c->scope->flags &= ~ScopeFlag_ContextDefined; } + TargetArchKind arch = build_context.metrics.arch; + switch (cc) { + case ProcCC_StdCall: + case ProcCC_FastCall: + if (arch != TargetArch_i386 || arch != TargetArch_amd64) { + error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s", + proc_calling_convention_strings[cc], LIT(target_arch_names[arch])); + } + break; + case ProcCC_Win64: + case ProcCC_SysV: + if (arch != TargetArch_amd64) { + error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s", + proc_calling_convention_strings[cc], LIT(target_arch_names[arch])); + } + break; + } + + bool variadic = false; isize variadic_index = -1; bool success = true; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 0474ce8ff..2c5186c39 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -683,40 +683,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { types[1] = odin_doc_type(w, type->Proc.results); doc_type.types = odin_write_slice(w, types, gb_count_of(types)); - String calling_convention = {}; - switch (type->Proc.calling_convention) { - case ProcCC_Invalid: - // no need - break; - case ProcCC_Odin: - if (default_calling_convention() != ProcCC_Odin) { - calling_convention = str_lit("odin"); - } - break; - case ProcCC_Contextless: - if (default_calling_convention() != ProcCC_Contextless) { - calling_convention = str_lit("contextless"); - } - break; - case ProcCC_CDecl: - calling_convention = str_lit("cdecl"); - break; - case ProcCC_StdCall: - calling_convention = str_lit("stdcall"); - break; - case ProcCC_FastCall: - calling_convention = str_lit("fastcall"); - break; - case ProcCC_None: - calling_convention = str_lit("none"); - break; - case ProcCC_Naked: - calling_convention = str_lit("naked"); - break; - case ProcCC_InlineAsm: - calling_convention = str_lit("inline-assembly"); - break; - } + String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]); doc_type.calling_convention = odin_doc_write_string(w, calling_convention); } break; diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 310df6639..0244b73d6 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -1184,6 +1184,12 @@ LB_ABI_INFO(lb_get_abi_info) { ft->calling_convention = calling_convention; return ft; } + case ProcCC_Win64: + GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); + return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); + case ProcCC_SysV: + GB_ASSERT(build_context.metrics.arch == TargetArch_amd64); + return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention); } switch (build_context.metrics.arch) { diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 087cda22a..f2bcfaff6 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -548,6 +548,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = { lbCallingConvention_C, // ProcCC_None, lbCallingConvention_C, // ProcCC_Naked, lbCallingConvention_C, // ProcCC_InlineAsm, + + lbCallingConvention_Win64, // ProcCC_Win64, + lbCallingConvention_X86_64_SysV, // ProcCC_SysV, + }; enum : LLVMDWARFTypeEncoding { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index ccb16ebe0..261e2819c 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -497,7 +497,7 @@ void lb_begin_procedure_body(lbProcedure *p) { } LLVMValueRef debug_storage_value = value; if (original_value != value && LLVMIsALoadInst(value)) { - debug_storage_value = ptr.value; + debug_storage_value = LLVMGetOperand(value, 0); } lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block); } diff --git a/src/parser.cpp b/src/parser.cpp index 0914c77ca..bd0e55b7f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3412,12 +3412,18 @@ ProcCallingConvention string_to_calling_convention(String s) { if (s == "fast") return ProcCC_FastCall; if (s == "none") return ProcCC_None; if (s == "naked") return ProcCC_Naked; + + if (s == "win64") return ProcCC_Win64; + if (s == "sysv") return ProcCC_SysV; + if (s == "system") { if (build_context.metrics.os == TargetOs_windows) { return ProcCC_StdCall; } return ProcCC_CDecl; } + + return ProcCC_Invalid; } diff --git a/src/parser.hpp b/src/parser.hpp index ff0df0382..9e93f4b26 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -249,12 +249,30 @@ enum ProcCallingConvention : i32 { ProcCC_InlineAsm = 8, + ProcCC_Win64 = 9, + ProcCC_SysV = 10, + + ProcCC_MAX, ProcCC_ForeignBlockDefault = -1, }; +char const *proc_calling_convention_strings[ProcCC_MAX] = { + "", + "odin", + "contextless", + "cdecl", + "stdcall", + "fastcall", + "none", + "naked", + "inlineasm", + "win64", + "sysv", +}; + ProcCallingConvention default_calling_convention(void) { return ProcCC_Odin; } -- cgit v1.2.3 From 71df46456a4db2592e8cebbdd4c46dc8b58b5a24 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 18 Feb 2022 21:30:25 +0000 Subject: Minimize memory usage by having an arena per thread rather than an arena per file --- src/array.cpp | 4 +++- src/checker.cpp | 4 ++-- src/common.cpp | 2 +- src/main.cpp | 2 +- src/parser.cpp | 12 ++++++------ src/parser.hpp | 6 ++---- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/array.cpp b/src/array.cpp index ac3727978..d08bd647f 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -89,7 +89,9 @@ template void slice_init(Slice *s, gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); s->data = gb_alloc_array(allocator, T, count); - GB_ASSERT(s->data != nullptr); + if (count > 0) { + GB_ASSERT(s->data != nullptr); + } s->count = count; } diff --git a/src/checker.cpp b/src/checker.cpp index f8aa8d45b..f440b7c9a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -225,8 +225,8 @@ bool decl_info_has_init(DeclInfo *d) { Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) { Scope *s = gb_alloc_item(permanent_allocator(), Scope); s->parent = parent; - string_map_init(&s->elements, permanent_allocator(), init_elements_capacity); - ptr_set_init(&s->imported, permanent_allocator(), 0); + string_map_init(&s->elements, heap_allocator(), init_elements_capacity); + ptr_set_init(&s->imported, heap_allocator(), 0); mutex_init(&s->mutex); if (parent != nullptr && parent != builtin_pkg->scope) { diff --git a/src/common.cpp b/src/common.cpp index ab2a46118..d3ee95b76 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1021,7 +1021,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil #endif } - gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath); + gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath); if (fc.size > I32_MAX) { err = LoadedFile_FileTooLarge; diff --git a/src/main.cpp b/src/main.cpp index 014fbf822..291b56996 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2577,7 +2577,7 @@ int main(int arg_count, char const **arg_ptr) { // NOTE(bill): add 'shared' directory if it is not already set if (!find_library_collection_path(str_lit("shared"), nullptr)) { add_library_collection(str_lit("shared"), - get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); + get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); } diff --git a/src/parser.cpp b/src/parser.cpp index 7309d9769..8a7ab2d20 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -57,6 +57,9 @@ isize ast_node_size(AstKind kind) { return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); } + +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++ Ast *alloc_ast_node(AstFile *f, AstKind kind) { gbAllocator a = ast_allocator(f); @@ -66,6 +69,9 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) { Ast *node = cast(Ast *)gb_alloc(a, size); node->kind = kind; node->file_id = f ? f->id : 0; + + global_total_node_memory_allocated += size; + return node; } @@ -4851,12 +4857,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) { f->prev_token = f->tokens[f->prev_token_index]; f->curr_token = f->tokens[f->curr_token_index]; - isize const page_size = 4*1024; - isize block_size = 2*f->tokens.count*gb_size_of(Ast); - block_size = ((block_size + page_size-1)/page_size) * page_size; - block_size = gb_clamp(block_size, page_size, DEFAULT_MINIMUM_BLOCK_SIZE); - f->arena.minimum_block_size = block_size; - array_init(&f->comments, heap_allocator(), 0, 0); array_init(&f->imports, heap_allocator(), 0, 0); diff --git a/src/parser.hpp b/src/parser.hpp index 83c755553..f6791a291 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -97,8 +97,6 @@ struct AstFile { AstPackage * pkg; Scope * scope; - Arena arena; - Ast * pkg_decl; String fullpath; Tokenizer tokenizer; @@ -801,10 +799,10 @@ gb_inline bool is_ast_when_stmt(Ast *node) { return node->kind == Ast_WhenStmt; } -gb_global gb_thread_local Arena global_ast_arena = {}; +gb_global gb_thread_local Arena global_thread_local_ast_arena = {}; gbAllocator ast_allocator(AstFile *f) { - Arena *arena = f ? &f->arena : &global_ast_arena; + Arena *arena = &global_thread_local_ast_arena; return arena_allocator(arena); } -- cgit v1.2.3 From cad753e3986183fa180abae480a85432b4b36af1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 22:53:13 +0000 Subject: Simplify `parse_binary_expr` --- src/parser.cpp | 157 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 109 insertions(+), 48 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 8a7ab2d20..c214df782 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3013,66 +3013,127 @@ i32 token_precedence(AstFile *f, TokenKind t) { return 0; } +// Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { +// Ast *expr = parse_unary_expr(f, lhs); +// for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { +// for (;;) { +// Token op = f->curr_token; +// i32 op_prec = token_precedence(f, op.kind); +// if (op_prec != prec) { +// // NOTE(bill): This will also catch operators that are not valid "binary" operators +// break; +// } +// Token prev = f->prev_token; +// switch (op.kind) { +// case Token_if: +// case Token_when: +// if (prev.pos.line < op.pos.line) { +// // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition +// goto loop_end; +// } +// break; +// } +// expect_operator(f); // NOTE(bill): error checks too + +// if (op.kind == Token_Question) { +// Ast *cond = expr; +// // Token_Question +// Ast *x = parse_expr(f, lhs); +// Token token_c = expect_token(f, Token_Colon); +// Ast *y = parse_expr(f, lhs); +// expr = ast_ternary_if_expr(f, x, cond, y); +// } else if (op.kind == Token_if || op.kind == Token_when) { +// Ast *x = expr; +// Ast *cond = parse_expr(f, lhs); +// Token tok_else = expect_token(f, Token_else); +// Ast *y = parse_expr(f, lhs); + +// switch (op.kind) { +// case Token_if: +// expr = ast_ternary_if_expr(f, x, cond, y); +// break; +// case Token_when: +// expr = ast_ternary_when_expr(f, x, cond, y); +// break; +// } +// } else { +// Ast *right = parse_binary_expr(f, false, prec+1); +// if (right == nullptr) { +// syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); +// } +// if (op.kind == Token_or_else) { +// // NOTE(bill): easier to handle its logic different with its own AST kind +// expr = ast_or_else_expr(f, expr, op, right); +// } else { +// expr = ast_binary_expr(f, op, expr, right); +// } +// } + +// lhs = false; +// } +// loop_end:; +// } +// return expr; +// } + Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Ast *expr = parse_unary_expr(f, lhs); - for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { - for (;;) { - Token op = f->curr_token; - i32 op_prec = token_precedence(f, op.kind); - if (op_prec != prec) { - // NOTE(bill): This will also catch operators that are not valid "binary" operators - break; + for (;;) { + Token op = f->curr_token; + i32 op_prec = token_precedence(f, op.kind); + if (op_prec < prec_in) { + // NOTE(bill): This will also catch operators that are not valid "binary" operators + break; + } + Token prev = f->prev_token; + switch (op.kind) { + case Token_if: + case Token_when: + if (prev.pos.line < op.pos.line) { + // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition + goto loop_end; } - Token prev = f->prev_token; + break; + } + expect_operator(f); // NOTE(bill): error checks too + + if (op.kind == Token_Question) { + Ast *cond = expr; + // Token_Question + Ast *x = parse_expr(f, lhs); + Token token_c = expect_token(f, Token_Colon); + Ast *y = parse_expr(f, lhs); + expr = ast_ternary_if_expr(f, x, cond, y); + } else if (op.kind == Token_if || op.kind == Token_when) { + Ast *x = expr; + Ast *cond = parse_expr(f, lhs); + Token tok_else = expect_token(f, Token_else); + Ast *y = parse_expr(f, lhs); + switch (op.kind) { case Token_if: + expr = ast_ternary_if_expr(f, x, cond, y); + break; case Token_when: - if (prev.pos.line < op.pos.line) { - // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition - goto loop_end; - } + expr = ast_ternary_when_expr(f, x, cond, y); break; } - expect_operator(f); // NOTE(bill): error checks too - - if (op.kind == Token_Question) { - Ast *cond = expr; - // Token_Question - Ast *x = parse_expr(f, lhs); - Token token_c = expect_token(f, Token_Colon); - Ast *y = parse_expr(f, lhs); - expr = ast_ternary_if_expr(f, x, cond, y); - } else if (op.kind == Token_if || op.kind == Token_when) { - Ast *x = expr; - Ast *cond = parse_expr(f, lhs); - Token tok_else = expect_token(f, Token_else); - Ast *y = parse_expr(f, lhs); - - switch (op.kind) { - case Token_if: - expr = ast_ternary_if_expr(f, x, cond, y); - break; - case Token_when: - expr = ast_ternary_when_expr(f, x, cond, y); - break; - } + } else { + Ast *right = parse_binary_expr(f, false, op_prec+1); + if (right == nullptr) { + syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); + } + if (op.kind == Token_or_else) { + // NOTE(bill): easier to handle its logic different with its own AST kind + expr = ast_or_else_expr(f, expr, op, right); } else { - Ast *right = parse_binary_expr(f, false, prec+1); - if (right == nullptr) { - syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); - } - if (op.kind == Token_or_else) { - // NOTE(bill): easier to handle its logic different with its own AST kind - expr = ast_or_else_expr(f, expr, op, right); - } else { - expr = ast_binary_expr(f, op, expr, right); - } + expr = ast_binary_expr(f, op, expr, right); } - - lhs = false; } - loop_end:; + + lhs = false; } + loop_end:; return expr; } -- cgit v1.2.3 From 62d232d798fff25597b6e3591c6a178c32fa440e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 22 Feb 2022 22:59:00 +0000 Subject: Correct `ExactValue_Pointer` --- src/exact_value.cpp | 17 ++++++++++++--- src/parser.cpp | 63 ----------------------------------------------------- 2 files changed, 14 insertions(+), 66 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fd90278e5..3dae96853 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -630,6 +630,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: case ExactValue_Quaternion: + case ExactValue_Pointer: + case ExactValue_Procedure: + case ExactValue_Typeid: return; case ExactValue_Integer: @@ -671,9 +674,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) { return; } break; - - case ExactValue_Procedure: - return; } compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind); @@ -932,6 +932,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { break; } + case ExactValue_Pointer: { + switch (op) { + case Token_CmpEq: return x.value_pointer == y.value_pointer; + case Token_NotEq: return x.value_pointer != y.value_pointer; + case Token_Lt: return x.value_pointer < y.value_pointer; + case Token_LtEq: return x.value_pointer <= y.value_pointer; + case Token_Gt: return x.value_pointer > y.value_pointer; + case Token_GtEq: return x.value_pointer >= y.value_pointer; + } + } + case ExactValue_Typeid: switch (op) { case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); diff --git a/src/parser.cpp b/src/parser.cpp index c214df782..9659e8c18 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3013,69 +3013,6 @@ i32 token_precedence(AstFile *f, TokenKind t) { return 0; } -// Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { -// Ast *expr = parse_unary_expr(f, lhs); -// for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) { -// for (;;) { -// Token op = f->curr_token; -// i32 op_prec = token_precedence(f, op.kind); -// if (op_prec != prec) { -// // NOTE(bill): This will also catch operators that are not valid "binary" operators -// break; -// } -// Token prev = f->prev_token; -// switch (op.kind) { -// case Token_if: -// case Token_when: -// if (prev.pos.line < op.pos.line) { -// // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition -// goto loop_end; -// } -// break; -// } -// expect_operator(f); // NOTE(bill): error checks too - -// if (op.kind == Token_Question) { -// Ast *cond = expr; -// // Token_Question -// Ast *x = parse_expr(f, lhs); -// Token token_c = expect_token(f, Token_Colon); -// Ast *y = parse_expr(f, lhs); -// expr = ast_ternary_if_expr(f, x, cond, y); -// } else if (op.kind == Token_if || op.kind == Token_when) { -// Ast *x = expr; -// Ast *cond = parse_expr(f, lhs); -// Token tok_else = expect_token(f, Token_else); -// Ast *y = parse_expr(f, lhs); - -// switch (op.kind) { -// case Token_if: -// expr = ast_ternary_if_expr(f, x, cond, y); -// break; -// case Token_when: -// expr = ast_ternary_when_expr(f, x, cond, y); -// break; -// } -// } else { -// Ast *right = parse_binary_expr(f, false, prec+1); -// if (right == nullptr) { -// syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string)); -// } -// if (op.kind == Token_or_else) { -// // NOTE(bill): easier to handle its logic different with its own AST kind -// expr = ast_or_else_expr(f, expr, op, right); -// } else { -// expr = ast_binary_expr(f, op, expr, right); -// } -// } - -// lhs = false; -// } -// loop_end:; -// } -// return expr; -// } - Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Ast *expr = parse_unary_expr(f, lhs); for (;;) { -- cgit v1.2.3 From d4ccb69ccc56fb57cad48493a9e5e78ca3529a84 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 28 Feb 2022 21:49:19 +0000 Subject: Check if directory exists with the same target executable name when building a directory --- src/parser.cpp | 16 ++++++++++++++++ src/parser.hpp | 1 + 2 files changed, 17 insertions(+) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 9659e8c18..f70afe346 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5719,6 +5719,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) { error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } + } else if (init_fullpath.len != 0) { + String path = init_fullpath; + if (path[path.len-1] == '/') { + path.len -= 1; + } + if ((build_context.command_kind & Command__does_build) && + build_context.build_mode == BuildMode_Executable) { + String short_path = filename_from_path(path); + char *cpath = alloc_cstring(heap_allocator(), short_path); + defer (gb_free(heap_allocator(), cpath)); + + if (gb_file_exists(cpath)) { + error_line("Please specify the executable name with -out: as a directory exists with the same name in the current working directory"); + return ParseFile_DirectoryAlreadyExists; + } + } } diff --git a/src/parser.hpp b/src/parser.hpp index f6791a291..c33d1520b 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -46,6 +46,7 @@ enum ParseFileError { ParseFile_InvalidToken, ParseFile_GeneralError, ParseFile_FileTooLarge, + ParseFile_DirectoryAlreadyExists, ParseFile_Count, }; -- cgit v1.2.3 From fad851d80c4d2ded5eba6b766ca00d90893c73aa Mon Sep 17 00:00:00 2001 From: Sébastien Marie Date: Thu, 3 Mar 2022 15:57:51 +0000 Subject: check for semi-colon before EOF too --- src/parser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index f70afe346..94a585f35 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1538,7 +1538,7 @@ void fix_advance_to_next_stmt(AstFile *f) { Token expect_closing(AstFile *f, TokenKind kind, String context) { if (f->curr_token.kind != kind && f->curr_token.kind == Token_Semicolon && - f->curr_token.string == "\n") { + (f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) { Token tok = f->prev_token; tok.pos.column += cast(i32)tok.string.len; syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context)); @@ -1560,6 +1560,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) { switch (curr_token->kind) { case Token_CloseBrace: case Token_CloseParen: + case Token_EOF: ok = true; break; } -- cgit v1.2.3 From 6ea9ba16e72df9ae45d6ff77664d4af9a7197852 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 14 Mar 2022 12:38:56 +0000 Subject: Fix #1610 --- src/parser.cpp | 50 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 94a585f35..a435d1317 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1577,7 +1577,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) { } } -void expect_semicolon(AstFile *f, Ast *s) { +void expect_semicolon(AstFile *f) { Token prev_token = {}; if (allow_token(f, Token_Semicolon)) { @@ -1602,17 +1602,17 @@ void expect_semicolon(AstFile *f, Ast *s) { if (f->curr_token.kind == Token_EOF) { return; } - - if (s != nullptr) { - return; - } switch (f->curr_token.kind) { case Token_EOF: return; } - String p = token_to_string(f->curr_token); - syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); - fix_advance_to_next_stmt(f); + + if (f->curr_token.pos.line == f->prev_token.pos.line) { + String p = token_to_string(f->curr_token); + prev_token.pos = token_pos_end(prev_token); + syntax_error(prev_token, "Expected ';', got %.*s", LIT(p)); + fix_advance_to_next_stmt(f); + } } @@ -3181,7 +3181,7 @@ Ast *parse_foreign_block(AstFile *f, Token token) { Ast *body = ast_block_stmt(f, decls, open, close); Ast *decl = ast_foreign_block_decl(f, token, foreign_library, body, docs); - expect_semicolon(f, decl); + expect_semicolon(f); return decl; } @@ -3227,15 +3227,11 @@ Ast *parse_value_decl(AstFile *f, Array names, CommentGroup *docs) { } if (f->expr_level >= 0) { - Ast *end = nullptr; - if (!is_mutable && values.count > 0) { - end = values[values.count-1]; - } if (f->curr_token.kind == Token_CloseBrace && f->curr_token.pos.line == f->prev_token.pos.line) { } else { - expect_semicolon(f, end); + expect_semicolon(f); } } @@ -4143,11 +4139,7 @@ Ast *parse_return_stmt(AstFile *f) { advance_token(f); } - Ast *end = nullptr; - if (results.count > 0) { - end = results[results.count-1]; - } - expect_semicolon(f, end); + expect_semicolon(f); return ast_return_stmt(f, token, results); } @@ -4398,7 +4390,7 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); } - expect_semicolon(f, s); + expect_semicolon(f); return s; } @@ -4456,7 +4448,7 @@ Ast *parse_foreign_decl(AstFile *f) { } else { s = ast_foreign_import_decl(f, token, filepaths, lib_name, docs, f->line_comment); } - expect_semicolon(f, s); + expect_semicolon(f); return s; } } @@ -4595,7 +4587,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Not: case Token_And: s = parse_simple_stmt(f, StmtAllowFlag_Label); - expect_semicolon(f, s); + expect_semicolon(f); return s; @@ -4623,7 +4615,7 @@ Ast *parse_stmt(AstFile *f) { label = parse_ident(f); } s = ast_branch_stmt(f, token, label); - expect_semicolon(f, s); + expect_semicolon(f); return s; } @@ -4638,12 +4630,12 @@ Ast *parse_stmt(AstFile *f) { Array list = parse_lhs_expr_list(f); if (list.count == 0) { syntax_error(token, "Illegal use of 'using' statement"); - expect_semicolon(f, nullptr); + expect_semicolon(f); return ast_bad_stmt(f, token, f->curr_token); } if (f->curr_token.kind != Token_Colon) { - expect_semicolon(f, list[list.count-1]); + expect_semicolon(f); return ast_using_stmt(f, token, list); } expect_token_after(f, Token_Colon, "identifier list"); @@ -4700,13 +4692,13 @@ Ast *parse_stmt(AstFile *f) { } 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, stmt); + expect_semicolon(f); return stmt; } else if (name.string == "force_inline" || name.string == "force_no_inline") { Ast *expr = parse_force_inlining_operand(f, name); Ast *stmt = ast_expr_stmt(f, expr); - expect_semicolon(f, stmt); + expect_semicolon(f); return stmt; } else if (tag == "unroll") { return parse_unrolled_for_loop(f, name); @@ -4728,7 +4720,7 @@ Ast *parse_stmt(AstFile *f) { case Token_Semicolon: s = ast_empty_stmt(f, token); - expect_semicolon(f, nullptr); + expect_semicolon(f); return s; } @@ -5586,7 +5578,7 @@ bool parse_file(Parser *p, AstFile *f) { } Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment); - expect_semicolon(f, pd); + expect_semicolon(f); f->pkg_decl = pd; if (f->error_count == 0) { -- cgit v1.2.3 From 3f935bea2505b3ee7e169a29b7aed50c0e5614b7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 24 Mar 2022 11:55:03 +0000 Subject: `union #shared_nil` This adds a feature to `union` which requires all the variants to have a `nil` value and on assign to the union, checks whether that value is `nil` or not. If the value is `nil`, the union will be `nil` (thus sharing the `nil` value) --- core/runtime/core.odin | 1 + src/check_expr.cpp | 7 +++++-- src/check_type.cpp | 19 ++++++++++++++----- src/docs_format.cpp | 1 + src/docs_writer.cpp | 8 +++++--- src/llvm_backend_general.cpp | 31 ++++++++++++++++++++++++++++--- src/llvm_backend_type.cpp | 7 ++++--- src/parser.cpp | 30 ++++++++++++++++++++++++++---- src/parser.hpp | 10 ++++++++-- src/types.cpp | 22 ++++++++++++---------- 10 files changed, 104 insertions(+), 32 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 8c95a234f..a5a190a9c 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -136,6 +136,7 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, maybe: bool, + shared_nil: bool, } Type_Info_Enum :: struct { base: ^Type_Info, diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 66bf8bbd7..dcf17af39 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10047,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, st->polymorphic_params, shorthand); str = gb_string_appendc(str, ") "); } - if (st->no_nil) str = gb_string_appendc(str, "#no_nil "); - if (st->maybe) str = gb_string_appendc(str, "#maybe "); + switch (st->kind) { + case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break; + case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; + case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; + } if (st->align) { str = gb_string_appendc(str, "#align "); str = write_expr_to_string(str, st->align, shorthand); diff --git a/src/check_type.cpp b/src/check_type.cpp index 3c7f33a46..51f472961 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -675,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) { + if (!type_has_nil(t)) { + gbString s = type_to_string(t); + error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s); + gb_string_free(s); + } + } } } } union_type->Union.variants = slice_from_array(variants); - union_type->Union.no_nil = ut->no_nil; - union_type->Union.maybe = ut->maybe; - if (union_type->Union.no_nil) { + union_type->Union.kind = ut->kind; + switch (ut->kind) { + case UnionType_no_nil: if (variants.count < 2) { error(ut->align, "A union with #no_nil must have at least 2 variants"); } - } - if (union_type->Union.maybe) { + break; + case UnionType_maybe: if (variants.count != 1) { error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count); } + break; } if (ut->align != nullptr) { diff --git a/src/docs_format.cpp b/src/docs_format.cpp index 7ce93d2bf..ee32d0e05 100644 --- a/src/docs_format.cpp +++ b/src/docs_format.cpp @@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 { OdinDocTypeFlag_Union_polymorphic = 1<<0, OdinDocTypeFlag_Union_no_nil = 1<<1, OdinDocTypeFlag_Union_maybe = 1<<2, + OdinDocTypeFlag_Union_shared_nil = 1<<3, }; enum OdinDocTypeFlag_Proc : u32 { diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 2c5186c39..0ad10ac49 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -619,9 +619,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { case Type_Union: doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } - if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; } - if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; } - + switch (type->Union.kind) { + case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break; + case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; + case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; + } { auto variants = array_make(heap_allocator(), type->Union.variants.count); defer (array_free(&variants)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index dd1555193..f6dd97966 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1176,10 +1176,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia } void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) { - lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + Type *pt = base_type(type_deref(parent.type)); + GB_ASSERT(pt->kind == Type_Union); + if (pt->Union.kind == UnionType_shared_nil) { + lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil"); + lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil"); + lbBlock *done = lb_create_block(p, "shared_nil.done"); + + lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant); + lb_emit_if(p, cond_is_nil, if_nil, if_not_nil); + + lb_start_block(p, if_nil); + lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type))); + lb_emit_jump(p, done); + + lb_start_block(p, if_not_nil); + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + lb_emit_jump(p, done); + + lb_start_block(p, done); - lb_emit_store(p, underlying, variant); - lb_emit_store_union_variant_tag(p, parent, variant_type); + + } else { + lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type)); + + lb_emit_store(p, underlying, variant); + lb_emit_store_union_variant_tag(p, parent, variant_type); + } } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index e245a8b40..7d73956e8 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[7] = {}; + LLVMValueRef vals[8] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -675,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da } vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; - vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value; + vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value; + vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index a435d1317..767119aa8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1071,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_c } -Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe, +Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind, Token where_token, Array const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = slice_from_array(variants); result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; - result->UnionType.no_nil = no_nil; - result->UnionType.maybe = maybe; + result->UnionType.kind = kind; result->UnionType.where_token = where_token; result->UnionType.where_clauses = slice_from_array(where_clauses); return result; @@ -2475,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { Ast *align = nullptr; bool no_nil = false; bool maybe = false; + bool shared_nil = false; + + UnionTypeKind union_kind = UnionType_Normal; Token start_token = f->curr_token; @@ -2501,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); } no_nil = true; + } else if (tag.string == "shared_nil") { + if (shared_nil) { + syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); + } + shared_nil = true; } else if (tag.string == "maybe") { if (maybe) { syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string)); @@ -2513,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (no_nil && maybe) { syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together"); } + if (no_nil && shared_nil) { + syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together"); + } + if (shared_nil && maybe) { + syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together"); + } + + + if (maybe) { + union_kind = UnionType_maybe; + } else if (no_nil) { + union_kind = UnionType_no_nil; + } else if (shared_nil) { + union_kind = UnionType_shared_nil; + } skip_possible_newline_for_literal(f); @@ -2544,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_closing_brace_of_field_list(f); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses); + return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses); } break; case Token_enum: { diff --git a/src/parser.hpp b/src/parser.hpp index c33d1520b..c7b4fd0d8 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -330,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { "intel", }; +enum UnionTypeKind : u8 { + UnionType_Normal = 0, + UnionType_maybe = 1, + UnionType_no_nil = 2, + UnionType_shared_nil = 3, +}; + #define AST_KINDS \ AST_KIND(Ident, "identifier", struct { \ Token token; \ @@ -678,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \ Slice variants; \ Ast *polymorphic_params; \ Ast * align; \ - bool maybe; \ - bool no_nil; \ + UnionTypeKind kind; \ Token where_token; \ Slice where_clauses; \ }) \ diff --git a/src/types.cpp b/src/types.cpp index ef770c846..b231218a2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -165,9 +165,8 @@ struct TypeUnion { i16 tag_size; bool is_polymorphic; - bool is_poly_specialized : 1; - bool no_nil : 1; - bool maybe : 1; + bool is_poly_specialized; + UnionTypeKind kind; }; struct TypeProc { @@ -1664,7 +1663,7 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { + if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { if (t->Union.variants.count == 1) { Type *v = t->Union.variants[0]; return is_type_pointer(v) || is_type_multi_pointer(v); @@ -1676,7 +1675,7 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.maybe) { + if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { if (t->Union.variants.count == 1) { Type *v = t->Union.variants[0]; if (is_type_pointer(v) || is_type_multi_pointer(v)) { @@ -2168,7 +2167,7 @@ bool type_has_nil(Type *t) { case Type_Map: return true; case Type_Union: - return !t->Union.no_nil; + return t->Union.kind != UnionType_no_nil; case Type_Struct: if (is_type_soa_struct(t)) { switch (t->Struct.soa_kind) { @@ -2454,7 +2453,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) { if (y->kind == Type_Union) { if (x->Union.variants.count == y->Union.variants.count && x->Union.custom_align == y->Union.custom_align && - x->Union.no_nil == y->Union.no_nil) { + x->Union.kind == y->Union.kind) { // NOTE(bill): zeroth variant is nullptr for_array(i, x->Union.variants) { if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) { @@ -2598,7 +2597,7 @@ i64 union_variant_index(Type *u, Type *v) { for_array(i, u->Union.variants) { Type *vt = u->Union.variants[i]; if (are_types_identical(v, vt)) { - if (u->Union.no_nil) { + if (u->Union.kind == UnionType_no_nil) { return cast(i64)(i+0); } else { return cast(i64)(i+1); @@ -4021,8 +4020,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { case Type_Union: str = gb_string_appendc(str, "union"); - if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil"); - if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe"); + switch (type->Union.kind) { + case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break; + case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; + case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; + } if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align); str = gb_string_appendc(str, " {"); for_array(i, type->Union.variants) { -- cgit v1.2.3 From 3cab2592c3e5a06882ffd711871a08c893b043f1 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 6 Apr 2022 18:26:23 +0200 Subject: Compiler: Add early error for output path being a directory. - Introduce new `Path` type and an array of build paths on the build context. - Resolve input and output paths/files early (before parsing). - Error early if inputs are missing or outputs are directories. - Plumb new file path generation into linker stage instead of its adhoc method. TODO: - Remove more adhoc file path generation in parser and linker stage. - Make intermediate object file generation use new path system. - Round out and robustify Path helper functions. --- .gitignore | 1 + Makefile | 4 +- build_odin.sh | 4 +- src/build_settings.cpp | 220 ++++++++++++++++++++++++---- src/common.cpp | 257 +------------------------------- src/gb/gb.h | 46 ++++-- src/llvm_backend.cpp | 14 +- src/llvm_backend_general.cpp | 1 - src/main.cpp | 152 +++++++++---------- src/parser.cpp | 2 +- src/path.cpp | 333 ++++++++++++++++++++++++++++++++++++++++++ src/string.cpp | 10 +- tests/core/build.bat | 28 ++-- tests/core/math/big/build.bat | 2 +- tests/issues/run.sh | 4 +- 15 files changed, 674 insertions(+), 404 deletions(-) create mode 100644 src/path.cpp (limited to 'src/parser.cpp') diff --git a/.gitignore b/.gitignore index e8b3d3050..d03a86fd7 100644 --- a/.gitignore +++ b/.gitignore @@ -269,6 +269,7 @@ bin/ # - Linux/MacOS odin odin.dSYM +*.bin # shared collection shared/ diff --git a/Makefile b/Makefile index 82150c6a2..1a1c93180 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -all: debug demo +all: debug demo: - ./odin run examples/demo/demo.odin + ./odin run examples/demo report: ./odin report diff --git a/build_odin.sh b/build_odin.sh index aef3f2836..4810cafd2 100755 --- a/build_odin.sh +++ b/build_odin.sh @@ -102,7 +102,7 @@ build_odin() { } run_demo() { - ./odin run examples/demo/demo.odin -file + ./odin run examples/demo } case $OS in @@ -147,4 +147,4 @@ if [[ $# -eq 1 ]]; then exit 0 else panic "Too many arguments!" -fi +fi \ No newline at end of file diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 2f3eb03a5..0b582eac8 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -3,7 +3,6 @@ #include #endif - // #if defined(GB_SYSTEM_WINDOWS) // #define DEFAULT_TO_THREADED_CHECKER // #endif @@ -198,6 +197,22 @@ enum RelocMode : u8 { RelocMode_DynamicNoPIC, }; +enum BuildPath : u8 { + BuildPath_Main_Package, // Input Path to the package directory (or file) we're building. + BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`. + BuildPath_RES, // Output Path for .res file, generated from previous. + BuildPath_Win_SDK_Root, // windows_sdk_root + BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path + BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path + BuildPath_VS_EXE, // vs_exe_path + BuildPath_VS_LIB, // vs_library_path + + BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`. + BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`. + + BuildPathCOUNT, +}; + // This stores the information for the specify architecture of this build struct BuildContext { // Constants @@ -226,9 +241,13 @@ struct BuildContext { bool show_help; + Array build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files. + // BuildPath enum contains the indices of paths we know *before* the work starts. + String out_filepath; String resource_filepath; String pdb_filepath; + bool has_resource; String link_flags; String extra_linker_flags; @@ -300,8 +319,6 @@ struct BuildContext { }; - - gb_global BuildContext build_context = {0}; bool global_warnings_as_errors(void) { @@ -605,28 +622,6 @@ bool allow_check_foreign_filepath(void) { // is_abs_path // has_subdir -enum TargetFileValidity : u8 { - TargetFileValidity_Invalid, - - TargetFileValidity_Writable_File, - TargetFileValidity_No_Write_Permission, - TargetFileValidity_Directory, - - TargetTargetFileValidity_COUNT, -}; - -TargetFileValidity set_output_filename(void) { - // Assembles the output filename from build_context information. - // Returns `true` if it doesn't exist or is a file. - // Returns `false` if a directory or write-protected file. - - - - - return TargetFileValidity_Writable_File; -} - - String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1}; String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1}; @@ -973,7 +968,6 @@ char *token_pos_to_string(TokenPos const &pos) { return s; } - void init_build_context(TargetMetrics *cross_target) { BuildContext *bc = &build_context; @@ -1152,8 +1146,178 @@ void init_build_context(TargetMetrics *cross_target) { bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3); - - #undef LINK_FLAG_X64 #undef LINK_FLAG_386 } + +#if defined(GB_SYSTEM_WINDOWS) +// NOTE(IC): In order to find Visual C++ paths without relying on environment variables. +// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`. +#include "microsoft_craziness.h" +#endif + +// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate. +// We've previously called `parse_build_flags`, so `out_filepath` should be set. +bool init_build_paths(String init_filename) { + gbAllocator ha = heap_allocator(); + BuildContext *bc = &build_context; + + // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index. + array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT); + + // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path. + bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename); + + bool produces_output_file = false; + if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) { + produces_output_file = true; + } else if (bc->command_kind & Command__does_build) { + produces_output_file = true; + } + + if (!produces_output_file) { + // Command doesn't produce output files. We're done. + return true; + } + + #if defined(GB_SYSTEM_WINDOWS) + if (bc->resource_filepath.len > 0) { + bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath); + bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath); + bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc")); + bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res")); + } + + if (bc->pdb_filepath.len > 0) { + bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath); + } + + if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) { + // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest. + Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8(); + + if (find_result.windows_sdk_version == 0) { + gb_printf_err("Windows SDK not found.\n"); + return false; + } + + GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0); + GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); + + if (find_result.windows_sdk_root.len > 0) { + bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root); + } + + if (find_result.windows_sdk_um_library_path.len > 0) { + bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path); + } + + if (find_result.windows_sdk_ucrt_library_path.len > 0) { + bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path); + } + + if (find_result.vs_exe_path.len > 0) { + bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path); + } + + if (find_result.vs_library_path.len > 0) { + bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path); + } + + gb_free(ha, find_result.windows_sdk_root.text); + gb_free(ha, find_result.windows_sdk_um_library_path.text); + gb_free(ha, find_result.windows_sdk_ucrt_library_path.text); + gb_free(ha, find_result.vs_exe_path.text); + gb_free(ha, find_result.vs_library_path.text); + + } + #endif + + // All the build targets and OSes. + String output_extension; + + if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) { + output_extension = STR_LIT("odin-doc"); + } else if (is_arch_wasm()) { + output_extension = STR_LIT("wasm"); + } else if (build_context.build_mode == BuildMode_Executable) { + // By default use a .bin executable extension. + output_extension = STR_LIT("bin"); + + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("exe"); + } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { + output_extension = make_string(nullptr, 0); + } + } else if (build_context.build_mode == BuildMode_DynamicLibrary) { + // By default use a .so shared library extension. + output_extension = STR_LIT("so"); + + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("dll"); + } else if (build_context.metrics.os == TargetOs_darwin) { + output_extension = STR_LIT("dylib"); + } + } else if (build_context.build_mode == BuildMode_Object) { + // By default use a .o object extension. + output_extension = STR_LIT("o"); + + if (build_context.metrics.os == TargetOs_windows) { + output_extension = STR_LIT("obj"); + } + } else if (build_context.build_mode == BuildMode_Assembly) { + // By default use a .S asm extension. + output_extension = STR_LIT("S"); + } else if (build_context.build_mode == BuildMode_LLVM_IR) { + output_extension = STR_LIT("ll"); + } else { + GB_PANIC("Unhandled build mode/target combination.\n"); + } + + if (bc->out_filepath.len > 0) { + bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath); + } else { + String output_name = remove_directory_from_path(init_filename); + output_name = remove_extension_from_path(output_name); + output_name = copy_string(ha, string_trim_whitespace(output_name)); + + /* + NOTE(Jeroen): This fallback substitution can't be made at this stage. + if (gen->output_name.len == 0) { + gen->output_name = c->info.init_scope->pkg->name; + } + */ + Path output_path = path_from_string(ha, output_name); + + #ifndef GB_SYSTEM_WINDOWS + char cwd[4096]; + getcwd(&cwd[0], 4096); + + const u8 * cwd_str = (const u8 *)&cwd[0]; + output_path.basename = copy_string(ha, make_string(cwd_str, strlen(cwd))); + #endif + + // Replace extension. + if (output_path.ext.len > 0) { + gb_free(ha, output_path.ext.text); + } + output_path.ext = copy_string(ha, output_extension); + + bc->build_paths[BuildPath_Output] = output_path; + } + + // Do we have an extension? We might not if the output filename was supplied. + if (bc->build_paths[BuildPath_Output].ext.len == 0) { + bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension); + } + + // Check if output path is a directory. + if (path_is_directory(bc->build_paths[BuildPath_Output])) { + String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]); + defer (gb_free(ha, output_file.text)); + gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/common.cpp b/src/common.cpp index aaacda04b..94248fb62 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -675,262 +675,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) { #endif - -#if defined(GB_SYSTEM_WINDOWS) - bool path_is_directory(String path) { - gbAllocator a = heap_allocator(); - String16 wstr = string_to_string16(a, path); - defer (gb_free(a, wstr.text)); - - i32 attribs = GetFileAttributesW(wstr.text); - if (attribs < 0) return false; - - return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; - } - -#else - bool path_is_directory(String path) { - gbAllocator a = heap_allocator(); - char *copy = cast(char *)copy_string(a, path).text; - defer (gb_free(a, copy)); - - struct stat s; - if (stat(copy, &s) == 0) { - return (s.st_mode & S_IFDIR) != 0; - } - return false; - } -#endif - - -String path_to_full_path(gbAllocator a, String path) { - gbAllocator ha = heap_allocator(); - char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len); - defer (gb_free(ha, path_c)); - - char *fullpath = gb_path_get_full_name(a, path_c); - String res = string_trim_whitespace(make_string_c(fullpath)); -#if defined(GB_SYSTEM_WINDOWS) - for (isize i = 0; i < res.len; i++) { - if (res.text[i] == '\\') { - res.text[i] = '/'; - } - } -#endif - return res; -} - - - -struct FileInfo { - String name; - String fullpath; - i64 size; - bool is_dir; -}; - -enum ReadDirectoryError { - ReadDirectory_None, - - ReadDirectory_InvalidPath, - ReadDirectory_NotExists, - ReadDirectory_Permission, - ReadDirectory_NotDir, - ReadDirectory_Empty, - ReadDirectory_Unknown, - - ReadDirectory_COUNT, -}; - -i64 get_file_size(String path) { - char *c_str = alloc_cstring(heap_allocator(), path); - defer (gb_free(heap_allocator(), c_str)); - - gbFile f = {}; - gbFileError err = gb_file_open(&f, c_str); - defer (gb_file_close(&f)); - if (err != gbFileError_None) { - return -1; - } - return gb_file_size(&f); -} - - -#if defined(GB_SYSTEM_WINDOWS) -ReadDirectoryError read_directory(String path, Array *fi) { - GB_ASSERT(fi != nullptr); - - gbAllocator a = heap_allocator(); - - while (path.len > 0) { - Rune end = path[path.len-1]; - if (end == '/') { - path.len -= 1; - } else if (end == '\\') { - path.len -= 1; - } else { - break; - } - } - - if (path.len == 0) { - return ReadDirectory_InvalidPath; - } - { - char *c_str = alloc_cstring(a, path); - defer (gb_free(a, c_str)); - - gbFile f = {}; - gbFileError file_err = gb_file_open(&f, c_str); - defer (gb_file_close(&f)); - - switch (file_err) { - case gbFileError_Invalid: return ReadDirectory_InvalidPath; - case gbFileError_NotExists: return ReadDirectory_NotExists; - // case gbFileError_Permission: return ReadDirectory_Permission; - } - } - - if (!path_is_directory(path)) { - return ReadDirectory_NotDir; - } - - - char *new_path = gb_alloc_array(a, char, path.len+3); - defer (gb_free(a, new_path)); - - gb_memmove(new_path, path.text, path.len); - gb_memmove(new_path+path.len, "/*", 2); - new_path[path.len+2] = 0; - - String np = make_string(cast(u8 *)new_path, path.len+2); - String16 wstr = string_to_string16(a, np); - defer (gb_free(a, wstr.text)); - - WIN32_FIND_DATAW file_data = {}; - HANDLE find_file = FindFirstFileW(wstr.text, &file_data); - if (find_file == INVALID_HANDLE_VALUE) { - return ReadDirectory_Unknown; - } - defer (FindClose(find_file)); - - array_init(fi, a, 0, 100); - - do { - wchar_t *filename_w = file_data.cFileName; - i64 size = cast(i64)file_data.nFileSizeLow; - size |= (cast(i64)file_data.nFileSizeHigh) << 32; - String name = string16_to_string(a, make_string16_c(filename_w)); - if (name == "." || name == "..") { - gb_free(a, name.text); - continue; - } - - String filepath = {}; - filepath.len = path.len+1+name.len; - filepath.text = gb_alloc_array(a, u8, filepath.len+1); - defer (gb_free(a, filepath.text)); - gb_memmove(filepath.text, path.text, path.len); - gb_memmove(filepath.text+path.len, "/", 1); - gb_memmove(filepath.text+path.len+1, name.text, name.len); - - FileInfo info = {}; - info.name = name; - info.fullpath = path_to_full_path(a, filepath); - info.size = size; - info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - array_add(fi, info); - } while (FindNextFileW(find_file, &file_data)); - - if (fi->count == 0) { - return ReadDirectory_Empty; - } - - return ReadDirectory_None; -} -#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) - -#include - -ReadDirectoryError read_directory(String path, Array *fi) { - GB_ASSERT(fi != nullptr); - - gbAllocator a = heap_allocator(); - - char *c_path = alloc_cstring(a, path); - defer (gb_free(a, c_path)); - - DIR *dir = opendir(c_path); - if (!dir) { - switch (errno) { - case ENOENT: - return ReadDirectory_NotExists; - case EACCES: - return ReadDirectory_Permission; - case ENOTDIR: - return ReadDirectory_NotDir; - default: - // ENOMEM: out of memory - // EMFILE: per-process limit on open fds reached - // ENFILE: system-wide limit on total open files reached - return ReadDirectory_Unknown; - } - GB_PANIC("unreachable"); - } - - array_init(fi, a, 0, 100); - - for (;;) { - struct dirent *entry = readdir(dir); - if (entry == nullptr) { - break; - } - - String name = make_string_c(entry->d_name); - if (name == "." || name == "..") { - continue; - } - - String filepath = {}; - filepath.len = path.len+1+name.len; - filepath.text = gb_alloc_array(a, u8, filepath.len+1); - defer (gb_free(a, filepath.text)); - gb_memmove(filepath.text, path.text, path.len); - gb_memmove(filepath.text+path.len, "/", 1); - gb_memmove(filepath.text+path.len+1, name.text, name.len); - filepath.text[filepath.len] = 0; - - - struct stat dir_stat = {}; - - if (stat((char *)filepath.text, &dir_stat)) { - continue; - } - - if (S_ISDIR(dir_stat.st_mode)) { - continue; - } - - i64 size = dir_stat.st_size; - - FileInfo info = {}; - info.name = name; - info.fullpath = path_to_full_path(a, filepath); - info.size = size; - array_add(fi, info); - } - - if (fi->count == 0) { - return ReadDirectory_Empty; - } - - return ReadDirectory_None; -} -#else -#error Implement read_directory -#endif - - +#include "path.cpp" struct LoadedFile { void *handle; diff --git a/src/gb/gb.h b/src/gb/gb.h index b72a893f7..3b2d6434c 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -6273,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) { #else char *p, *result, *fullpath = NULL; isize len; - p = realpath(path, NULL); - fullpath = p; - if (p == NULL) { - // NOTE(bill): File does not exist - fullpath = cast(char *)path; - } + fullpath = realpath(path, NULL); + + if (fullpath == NULL) { + // NOTE(Jeroen): Path doesn't exist. + if (gb_strlen(path) > 0 && path[0] == '/') { + // But it is an absolute path, so return as-is. + + fullpath = (char *)path; + len = gb_strlen(fullpath) + 1; + result = gb_alloc_array(a, char, len + 1); + + gb_memmove(result, fullpath, len); + result[len] = 0; + + } else { + // Appears to be a relative path, so construct an absolute one relative to . + char cwd[4096]; + getcwd(&cwd[0], 4096); + + isize path_len = gb_strlen(path); + isize cwd_len = gb_strlen(cwd); + len = cwd_len + 1 + path_len + 1; + result = gb_alloc_array(a, char, len); - len = gb_strlen(fullpath); + gb_memmove(result, (void *)cwd, cwd_len); + result[cwd_len] = '/'; - result = gb_alloc_array(a, char, len + 1); - gb_memmove(result, fullpath, len); - result[len] = 0; - free(p); + gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path)); + result[len] = 0; + } + } else { + len = gb_strlen(fullpath) + 1; + result = gb_alloc_array(a, char, len + 1); + gb_memmove(result, fullpath, len); + result[len] = 0; + free(fullpath); + } return result; #endif } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f5cb84785..7781997f7 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -967,7 +967,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) } String lb_filepath_ll_for_module(lbModule *m) { - String path = m->gen->output_base; + String path = concatenate3_strings(permanent_allocator(), + build_context.build_paths[BuildPath_Output].basename, + STR_LIT("/"), + build_context.build_paths[BuildPath_Output].name + ); + if (m->pkg) { path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); } else if (USE_SEPARATE_MODULES) { @@ -978,7 +983,12 @@ String lb_filepath_ll_for_module(lbModule *m) { return path; } String lb_filepath_obj_for_module(lbModule *m) { - String path = m->gen->output_base; + String path = concatenate3_strings(permanent_allocator(), + build_context.build_paths[BuildPath_Output].basename, + STR_LIT("/"), + build_context.build_paths[BuildPath_Output].name + ); + if (m->pkg) { path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name); } diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 330059622..1a431a4ac 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -87,7 +87,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) { return false; } - String init_fullpath = c->parser->init_fullpath; if (build_context.out_filepath.len == 0) { diff --git a/src/main.cpp b/src/main.cpp index fc8792ceb..7b0364149 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,7 +46,6 @@ gb_global Timings global_timings = {0}; #include "checker.cpp" #include "docs.cpp" - #include "llvm_backend.cpp" #if defined(GB_SYSTEM_OSX) @@ -57,16 +56,8 @@ gb_global Timings global_timings = {0}; #endif #include "query_data.cpp" - - -#if defined(GB_SYSTEM_WINDOWS) -// NOTE(IC): In order to find Visual C++ paths without relying on environment variables. -#include "microsoft_craziness.h" -#endif - #include "bug_report.cpp" - // NOTE(bill): 'name' is used in debugging and profiling modes i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { isize const cmd_cap = 64<<20; // 64 MiB should be more than enough @@ -130,34 +121,35 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) { } - - i32 linker_stage(lbGenerator *gen) { i32 result = 0; Timings *timings = &global_timings; - String output_base = gen->output_base; + String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); + debugf("Linking %.*s\n", LIT(output_filename)); + + // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`. if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); #if defined(GB_SYSTEM_WINDOWS) result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", + "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s", LIT(build_context.ODIN_ROOT), - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #else result = system_exec_command_line_app("wasm-ld", - "wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s", - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s", + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #endif return result; } if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) { -#ifdef GB_SYSTEM_UNIX +#if defined(GB_SYSTEM_UNIX) result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s", - LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); #else gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n", LIT(target_os_names[build_context.metrics.os]), @@ -181,28 +173,11 @@ i32 linker_stage(lbGenerator *gen) { gbString lib_str = gb_string_make(heap_allocator(), ""); defer (gb_string_free(lib_str)); - char const *output_ext = "exe"; gbString link_settings = gb_string_make_reserve(heap_allocator(), 256); defer (gb_string_free(link_settings)); - - // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest. - Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8(); - - if (find_result.windows_sdk_version == 0) { - gb_printf_err("Windows SDK not found.\n"); - exit(1); - } - - if (build_context.ignore_microsoft_magic) { - find_result = {}; - } - // Add library search paths. - if (find_result.vs_library_path.len > 0) { - GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0); - GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0); - + if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) { String path = {}; auto add_path = [&](String path) { if (path[path.len-1] == '\\') { @@ -210,9 +185,9 @@ i32 linker_stage(lbGenerator *gen) { } link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path)); }; - add_path(find_result.windows_sdk_um_library_path); - add_path(find_result.windows_sdk_ucrt_library_path); - add_path(find_result.vs_library_path); + add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename); + add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename); + add_path(build_context.build_paths[BuildPath_VS_LIB].basename); } @@ -252,14 +227,14 @@ i32 linker_stage(lbGenerator *gen) { if (build_context.build_mode == BuildMode_DynamicLibrary) { - output_ext = "dll"; link_settings = gb_string_append_fmt(link_settings, " /DLL"); } else { link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup"); } if (build_context.pdb_filepath != "") { - link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath)); + String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]); + link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path)); } if (build_context.no_crt) { @@ -300,13 +275,21 @@ i32 linker_stage(lbGenerator *gen) { object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path)); } + String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]); + defer (gb_free(heap_allocator(), vs_exe_path.text)); + char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE"; if (!build_context.use_lld) { // msvc if (build_context.has_resource) { + String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]); + String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]); + defer (gb_free(heap_allocator(), rc_path.text)); + defer (gb_free(heap_allocator(), res_path.text)); + result = system_exec_command_line_app("msvc-link", - "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"", - LIT(output_base), - LIT(build_context.resource_filepath) + "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"", + LIT(res_path), + LIT(rc_path) ); if (result) { @@ -314,13 +297,13 @@ i32 linker_stage(lbGenerator *gen) { } result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s " + "\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " " %.*s " " %s " "", - LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext, + LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename), link_settings, subsystem_str, LIT(build_context.link_flags), @@ -329,13 +312,13 @@ i32 linker_stage(lbGenerator *gen) { ); } else { result = system_exec_command_line_app("msvc-link", - "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s " + "\"%.*slink.exe\" %s -OUT:\"%.*s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " " %.*s " " %s " "", - LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext, + LIT(vs_exe_path), object_files, LIT(output_filename), link_settings, subsystem_str, LIT(build_context.link_flags), @@ -350,13 +333,13 @@ i32 linker_stage(lbGenerator *gen) { } else { // lld result = system_exec_command_line_app("msvc-lld-link", - "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s " + "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s " "/nologo /incremental:no /opt:ref /subsystem:%s " " %.*s " " %.*s " " %s " "", - LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext, + LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename), link_settings, subsystem_str, LIT(build_context.link_flags), @@ -415,7 +398,7 @@ i32 linker_stage(lbGenerator *gen) { } else if (string_ends_with(lib, str_lit(".so"))) { // dynamic lib, relative path to executable // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible - // at runtimeto the executable + // at runtime to the executable lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib)); } else { // dynamic or static system lib, just link regularly searching system library paths @@ -431,9 +414,6 @@ i32 linker_stage(lbGenerator *gen) { object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path)); } - // Unlike the Win32 linker code, the output_ext includes the dot, because - // typically executable files on *NIX systems don't have extensions. - String output_ext = {}; gbString link_settings = gb_string_make_reserve(heap_allocator(), 32); if (build_context.no_crt) { @@ -461,26 +441,12 @@ i32 linker_stage(lbGenerator *gen) { // correctly this way since all the other dependencies provided implicitly // by the compiler frontend are still needed and most of the command // line arguments prepared previously are incompatible with ld. - // - // Shared libraries are .dylib on MacOS and .so on Linux. - if (build_context.metrics.os == TargetOs_darwin) { - output_ext = STR_LIT(".dylib"); - } else { - output_ext = STR_LIT(".so"); - } link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' "); link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' "); } else if (build_context.metrics.os != TargetOs_openbsd) { // OpenBSD defaults to PIE executable. do not pass -no-pie for it. link_settings = gb_string_appendc(link_settings, "-no-pie "); } - if (build_context.out_filepath.len > 0) { - //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that - isize pos = string_extension_position(build_context.out_filepath); - if (pos > 0) { - output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len); - } - } gbString platform_lib_str = gb_string_make(heap_allocator(), ""); defer (gb_string_free(platform_lib_str)); @@ -507,7 +473,7 @@ i32 linker_stage(lbGenerator *gen) { defer (gb_string_free(link_command_line)); link_command_line = gb_string_appendc(link_command_line, object_files); - link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s%.*s\" ", LIT(output_base), LIT(output_ext)); + link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename)); link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str); link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str); link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags)); @@ -524,9 +490,7 @@ i32 linker_stage(lbGenerator *gen) { if (build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - result = system_exec_command_line_app("dsymutil", - "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext) - ); + result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename)); if (result) { return result; @@ -1526,6 +1490,10 @@ bool parse_build_flags(Array args) { gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path)); bad_flags = true; break; + } else if (!gb_file_exists((const char *)path.text)) { + gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path)); + bad_flags = true; + break; } build_context.resource_filepath = substring(path, 0, string_extension_position(path)); build_context.has_resource = true; @@ -1540,6 +1508,11 @@ bool parse_build_flags(Array args) { String path = value.value_string; path = string_trim_whitespace(path); if (is_build_flag_path_valid(path)) { + if (path_is_directory(path)) { + gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path)); + bad_flags = true; + break; + } // #if defined(GB_SYSTEM_WINDOWS) // String ext = path_extension(path); // if (ext != ".pdb") { @@ -2666,6 +2639,8 @@ int main(int arg_count, char const **arg_ptr) { return 1; } + init_filename = copy_string(permanent_allocator(), init_filename); + if (init_filename == "-help" || init_filename == "--help") { build_context.show_help = true; @@ -2688,6 +2663,12 @@ int main(int arg_count, char const **arg_ptr) { gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename)); gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n"); return 1; + } else { + String const ext = str_lit(".odin"); + if (!string_ends_with(init_filename, ext)) { + gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + return 1; + } } } } @@ -2709,13 +2690,24 @@ int main(int arg_count, char const **arg_ptr) { get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared"))); } - init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr); // if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) { // print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0])); // return 1; // } + // Set and check build paths... + if (!init_build_paths(init_filename)) { + return 1; + } + + if (build_context.show_debug_messages) { + for_array(i, build_context.build_paths) { + String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]); + debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path)); + } + } + init_global_thread_pool(); defer (thread_pool_destroy(&global_thread_pool)); @@ -2732,6 +2724,8 @@ int main(int arg_count, char const **arg_ptr) { } defer (destroy_parser(parser)); + // TODO(jeroen): Remove the `init_filename` param. + // Let's put that on `build_context.build_paths[0]` instead. if (parse_packages(parser, init_filename) != ParseFile_None) { return 1; } @@ -2810,16 +2804,14 @@ int main(int arg_count, char const **arg_ptr) { } if (run_output) { + String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]); + defer (gb_free(heap_allocator(), exe_name.text)); + #if defined(GB_SYSTEM_WINDOWS) - return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string)); + return system_exec_command_line_app("odin run", "%.*s %.*s", LIT(exe_name), LIT(run_args_string)); #else - //NOTE(thebirk): This whole thing is a little leaky - String output_ext = {}; - String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext); - complete_path = path_to_full_path(permanent_allocator(), complete_path); - return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); + return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string)); #endif } - return 0; } diff --git a/src/parser.cpp b/src/parser.cpp index 767119aa8..df7f908a6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5751,7 +5751,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) { } } } - + { // Add these packages serially and then process them parallel mutex_lock(&p->wait_mutex); diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 000000000..8d8e532b8 --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,333 @@ +/* + Path handling utilities. +*/ + +#if defined(GB_SYSTEM_WINDOWS) + bool path_is_directory(String path) { + gbAllocator a = heap_allocator(); + String16 wstr = string_to_string16(a, path); + defer (gb_free(a, wstr.text)); + + i32 attribs = GetFileAttributesW(wstr.text); + if (attribs < 0) return false; + + return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + +#else + bool path_is_directory(String path) { + gbAllocator a = heap_allocator(); + char *copy = cast(char *)copy_string(a, path).text; + defer (gb_free(a, copy)); + + struct stat s; + if (stat(copy, &s) == 0) { + return (s.st_mode & S_IFDIR) != 0; + } + return false; + } +#endif + + +String path_to_full_path(gbAllocator a, String path) { + gbAllocator ha = heap_allocator(); + char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len); + defer (gb_free(ha, path_c)); + + char *fullpath = gb_path_get_full_name(a, path_c); + String res = string_trim_whitespace(make_string_c(fullpath)); +#if defined(GB_SYSTEM_WINDOWS) + for (isize i = 0; i < res.len; i++) { + if (res.text[i] == '\\') { + res.text[i] = '/'; + } + } +#endif + return copy_string(a, res); +} + +struct Path { + String basename; + String name; + String ext; +}; + +// NOTE(Jeroen): Naively turns a Path into a string. +String path_to_string(gbAllocator a, Path path) { + if (path.basename.len + path.name.len + path.ext.len == 0) { + return make_string(nullptr, 0); + } + + isize len = path.basename.len + 1 + path.name.len + 1; + if (path.ext.len > 0) { + len += path.ext.len + 1; + } + + u8 *str = gb_alloc_array(a, u8, len); + + isize i = 0; + gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len; + gb_memmove(str+i, "/", 1); i += 1; + gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len; + if (path.ext.len > 0) { + gb_memmove(str+i, ".", 1); i += 1; + gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len; + } + str[i] = 0; + + String res = make_string(str, i); + res = string_trim_whitespace(res); + return res; +} + +// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`. +String path_to_full_path(gbAllocator a, Path path) { + String temp = path_to_string(heap_allocator(), path); + defer (gb_free(heap_allocator(), temp.text)); + + return path_to_full_path(a, temp); +} + +// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path, +// and then breaks it into its components to make a Path. +Path path_from_string(gbAllocator a, String const &path) { + Path res = {}; + + if (path.len == 0) return res; + + String fullpath = path_to_full_path(a, path); + defer (gb_free(heap_allocator(), fullpath.text)); + + res.basename = directory_from_path(fullpath); + res.basename = copy_string(a, res.basename); + + if (string_ends_with(fullpath, '/')) { + // It's a directory. We don't need to tinker with the name and extension. + return res; + } + + isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len; + res.name = substring(fullpath, name_start, fullpath.len); + res.name = remove_extension_from_path(res.name); + res.name = copy_string(a, res.name); + + res.ext = path_extension(fullpath, false); // false says not to include the dot. + res.ext = copy_string(a, res.ext); + return res; +} + +bool path_is_directory(Path path) { + String path_string = path_to_full_path(heap_allocator(), path); + defer (gb_free(heap_allocator(), path_string.text)); + + return path_is_directory(path_string); +} + +struct FileInfo { + String name; + String fullpath; + i64 size; + bool is_dir; +}; + +enum ReadDirectoryError { + ReadDirectory_None, + + ReadDirectory_InvalidPath, + ReadDirectory_NotExists, + ReadDirectory_Permission, + ReadDirectory_NotDir, + ReadDirectory_Empty, + ReadDirectory_Unknown, + + ReadDirectory_COUNT, +}; + +i64 get_file_size(String path) { + char *c_str = alloc_cstring(heap_allocator(), path); + defer (gb_free(heap_allocator(), c_str)); + + gbFile f = {}; + gbFileError err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + if (err != gbFileError_None) { + return -1; + } + return gb_file_size(&f); +} + + +#if defined(GB_SYSTEM_WINDOWS) +ReadDirectoryError read_directory(String path, Array *fi) { + GB_ASSERT(fi != nullptr); + + gbAllocator a = heap_allocator(); + + while (path.len > 0) { + Rune end = path[path.len-1]; + if (end == '/') { + path.len -= 1; + } else if (end == '\\') { + path.len -= 1; + } else { + break; + } + } + + if (path.len == 0) { + return ReadDirectory_InvalidPath; + } + { + char *c_str = alloc_cstring(a, path); + defer (gb_free(a, c_str)); + + gbFile f = {}; + gbFileError file_err = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + switch (file_err) { + case gbFileError_Invalid: return ReadDirectory_InvalidPath; + case gbFileError_NotExists: return ReadDirectory_NotExists; + // case gbFileError_Permission: return ReadDirectory_Permission; + } + } + + if (!path_is_directory(path)) { + return ReadDirectory_NotDir; + } + + + char *new_path = gb_alloc_array(a, char, path.len+3); + defer (gb_free(a, new_path)); + + gb_memmove(new_path, path.text, path.len); + gb_memmove(new_path+path.len, "/*", 2); + new_path[path.len+2] = 0; + + String np = make_string(cast(u8 *)new_path, path.len+2); + String16 wstr = string_to_string16(a, np); + defer (gb_free(a, wstr.text)); + + WIN32_FIND_DATAW file_data = {}; + HANDLE find_file = FindFirstFileW(wstr.text, &file_data); + if (find_file == INVALID_HANDLE_VALUE) { + return ReadDirectory_Unknown; + } + defer (FindClose(find_file)); + + array_init(fi, a, 0, 100); + + do { + wchar_t *filename_w = file_data.cFileName; + i64 size = cast(i64)file_data.nFileSizeLow; + size |= (cast(i64)file_data.nFileSizeHigh) << 32; + String name = string16_to_string(a, make_string16_c(filename_w)); + if (name == "." || name == "..") { + gb_free(a, name.text); + continue; + } + + String filepath = {}; + filepath.len = path.len+1+name.len; + filepath.text = gb_alloc_array(a, u8, filepath.len+1); + defer (gb_free(a, filepath.text)); + gb_memmove(filepath.text, path.text, path.len); + gb_memmove(filepath.text+path.len, "/", 1); + gb_memmove(filepath.text+path.len+1, name.text, name.len); + + FileInfo info = {}; + info.name = name; + info.fullpath = path_to_full_path(a, filepath); + info.size = size; + info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + array_add(fi, info); + } while (FindNextFileW(find_file, &file_data)); + + if (fi->count == 0) { + return ReadDirectory_Empty; + } + + return ReadDirectory_None; +} +#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) + +#include + +ReadDirectoryError read_directory(String path, Array *fi) { + GB_ASSERT(fi != nullptr); + + gbAllocator a = heap_allocator(); + + char *c_path = alloc_cstring(a, path); + defer (gb_free(a, c_path)); + + DIR *dir = opendir(c_path); + if (!dir) { + switch (errno) { + case ENOENT: + return ReadDirectory_NotExists; + case EACCES: + return ReadDirectory_Permission; + case ENOTDIR: + return ReadDirectory_NotDir; + default: + // ENOMEM: out of memory + // EMFILE: per-process limit on open fds reached + // ENFILE: system-wide limit on total open files reached + return ReadDirectory_Unknown; + } + GB_PANIC("unreachable"); + } + + array_init(fi, a, 0, 100); + + for (;;) { + struct dirent *entry = readdir(dir); + if (entry == nullptr) { + break; + } + + String name = make_string_c(entry->d_name); + if (name == "." || name == "..") { + continue; + } + + String filepath = {}; + filepath.len = path.len+1+name.len; + filepath.text = gb_alloc_array(a, u8, filepath.len+1); + defer (gb_free(a, filepath.text)); + gb_memmove(filepath.text, path.text, path.len); + gb_memmove(filepath.text+path.len, "/", 1); + gb_memmove(filepath.text+path.len+1, name.text, name.len); + filepath.text[filepath.len] = 0; + + + struct stat dir_stat = {}; + + if (stat((char *)filepath.text, &dir_stat)) { + continue; + } + + if (S_ISDIR(dir_stat.st_mode)) { + continue; + } + + i64 size = dir_stat.st_size; + + FileInfo info = {}; + info.name = name; + info.fullpath = path_to_full_path(a, filepath); + info.size = size; + array_add(fi, info); + } + + if (fi->count == 0) { + return ReadDirectory_Empty; + } + + return ReadDirectory_None; +} +#else +#error Implement read_directory +#endif + diff --git a/src/string.cpp b/src/string.cpp index d3dbc6904..3515df48e 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -245,15 +245,14 @@ gb_inline isize string_extension_position(String const &str) { return dot_pos; } -String path_extension(String const &str) { +String path_extension(String const &str, bool include_dot = true) { isize pos = string_extension_position(str); if (pos < 0) { return make_string(nullptr, 0); } - return substring(str, pos, str.len); + return substring(str, include_dot ? pos : pos + 1, str.len); } - String string_trim_whitespace(String str) { while (str.len > 0 && rune_is_whitespace(str[str.len-1])) { str.len--; @@ -328,7 +327,10 @@ String directory_from_path(String const &s) { break; } } - return substring(s, 0, i); + if (i >= 0) { + return substring(s, 0, i); + } + return substring(s, 0, 0); } String concatenate_strings(gbAllocator a, String const &x, String const &y) { diff --git a/tests/core/build.bat b/tests/core/build.bat index 2f9ba672e..1973c22aa 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -5,61 +5,61 @@ python3 download_assets.py echo --- echo Running core:image tests echo --- -%PATH_TO_ODIN% run image %COMMON% +%PATH_TO_ODIN% run image %COMMON% -out:test_image echo --- echo Running core:compress tests echo --- -%PATH_TO_ODIN% run compress %COMMON% +%PATH_TO_ODIN% run compress %COMMON% -out:test_compress echo --- echo Running core:strings tests echo --- -%PATH_TO_ODIN% run strings %COMMON% +%PATH_TO_ODIN% run strings %COMMON% -out:test_strings echo --- echo Running core:hash tests echo --- -%PATH_TO_ODIN% run hash %COMMON% -o:size +%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_hash echo --- echo Running core:odin tests echo --- -%PATH_TO_ODIN% run odin %COMMON% -o:size +%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_odin echo --- echo Running core:crypto hash tests echo --- -%PATH_TO_ODIN% run crypto %COMMON% +%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto echo --- echo Running core:encoding tests echo --- -%PATH_TO_ODIN% run encoding/hxa %COMMON% -%PATH_TO_ODIN% run encoding/json %COMMON% -%PATH_TO_ODIN% run encoding/varint %COMMON% +%PATH_TO_ODIN% run encoding/hxa %COMMON% -out:test_hxa +%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json +%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint echo --- echo Running core:math/noise tests echo --- -%PATH_TO_ODIN% run math/noise %COMMON% +%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise echo --- echo Running core:math tests echo --- -%PATH_TO_ODIN% run math %COMMON% +%PATH_TO_ODIN% run math %COMMON% -out:test_math echo --- echo Running core:math/linalg/glsl tests echo --- -%PATH_TO_ODIN% run math/linalg/glsl %COMMON% +%PATH_TO_ODIN% run math/linalg/glsl %COMMON% -out:test_glsl echo --- echo Running core:path/filepath tests echo --- -%PATH_TO_ODIN% run path/filepath %COMMON% +%PATH_TO_ODIN% run path/filepath %COMMON% -out:test_filepath echo --- echo Running core:reflect tests echo --- -%PATH_TO_ODIN% run reflect %COMMON% +%PATH_TO_ODIN% run reflect %COMMON% -out:test_reflect diff --git a/tests/core/math/big/build.bat b/tests/core/math/big/build.bat index 16bdbc8ca..ad199d775 100644 --- a/tests/core/math/big/build.bat +++ b/tests/core/math/big/build.bat @@ -4,7 +4,7 @@ set PATH_TO_ODIN==..\..\..\..\odin set TEST_ARGS=-fast-tests set TEST_ARGS=-no-random set TEST_ARGS= -set OUT_NAME=math_big_test_library +set OUT_NAME=math_big_test_library.dll set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style echo --- echo Running core:math/big tests diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 117a9a5f1..91ec99e05 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -8,10 +8,10 @@ COMMON="-collection:tests=tests -out:tests/issues/build/test_issue" set -x ./odin build tests/issues/test_issue_829.odin $COMMON -file -tests/issues/build/test_issue +tests/issues/build/test_issue.bin ./odin build tests/issues/test_issue_1592.odin $COMMON -file -tests/issues/build/test_issue +tests/issues/build/test_issue.bin set +x -- cgit v1.2.3 From 5bc866e420d8eb9e909db71e230d1283c6116d7e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 14 May 2022 14:44:24 +0100 Subject: Allow for `import _ "foo"` to allow for `@(init)` procedures; Remove `using import` code --- src/checker.cpp | 48 ++++++++++-------------------------------------- src/parser.cpp | 14 ++++---------- src/parser.hpp | 1 - 3 files changed, 14 insertions(+), 49 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/checker.cpp b/src/checker.cpp index d186163e4..da9a97622 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4356,6 +4356,9 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { } String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false); + if (is_blank_ident(import_name)) { + force_use = true; + } // NOTE(bill, 2019-05-19): If the directory path is not a valid entity name, force the user to assign a custom one // if (import_name.len == 0 || import_name == "_") { @@ -4363,17 +4366,13 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { // } if (import_name.len == 0 || is_blank_ident(import_name)) { - if (id->is_using) { - // TODO(bill): Should this be a warning? - } else { - if (id->import_name.string == "") { - String invalid_name = id->fullpath; - invalid_name = get_invalid_import_name(invalid_name); + if (id->import_name.string == "") { + String invalid_name = id->fullpath; + invalid_name = get_invalid_import_name(invalid_name); - error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); - } else { - error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); - } + error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name)); + } else { + error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string)); } } else { GB_ASSERT(id->import_name.pos.line != 0); @@ -4383,38 +4382,11 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { scope); add_entity(ctx, parent_scope, nullptr, e); - if (force_use || id->is_using) { + if (force_use) { add_entity_use(ctx, nullptr, e); } } - if (id->is_using) { - if (parent_scope->flags & ScopeFlag_Global) { - error(id->import_name, "built-in package imports cannot use using"); - return; - } - - // NOTE(bill): Add imported entities to this file's scope - for_array(elem_index, scope->elements.entries) { - String name = scope->elements.entries[elem_index].key.string; - Entity *e = scope->elements.entries[elem_index].value; - if (e->scope == parent_scope) continue; - - if (is_entity_exported(e, true)) { - Entity *found = scope_lookup_current(parent_scope, name); - if (found != nullptr) { - // NOTE(bill): - // Date: 2019-03-17 - // The order has to be the other way around as `using` adds the entity into the that - // file scope otherwise the error would be the wrong way around - redeclaration_error(name, found, e); - } else { - add_entity_with_name(ctx, parent_scope, e->identifier, e, name); - } - } - } - } - scope->flags |= ScopeFlag_HasBeenImported; } diff --git a/src/parser.cpp b/src/parser.cpp index df7f908a6..1f4093e5f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1160,11 +1160,10 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C return result; } -Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name, +Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token import_name, CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_ImportDecl); result->ImportDecl.token = token; - result->ImportDecl.is_using = is_using; result->ImportDecl.relpath = relpath; result->ImportDecl.import_name = import_name; result->ImportDecl.docs = docs; @@ -4382,7 +4381,6 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_import); Token import_name = {}; - bool is_using = kind != ImportDecl_Standard; switch (f->curr_token.kind) { case Token_Ident: @@ -4393,22 +4391,18 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) { break; } - if (!is_using && is_blank_ident(import_name)) { - syntax_error(import_name, "Illegal import name: '_'"); - } - Token file_path = expect_token_after(f, Token_String, "import"); Ast *s = nullptr; if (f->curr_proc != nullptr) { - syntax_error(import_name, "You cannot use 'import' within a procedure. This must be done at the file scope"); + syntax_error(import_name, "Cannot use 'import' within a procedure. This must be done at the file scope"); s = ast_bad_decl(f, import_name, file_path); } else { - s = ast_import_decl(f, token, is_using, file_path, import_name, docs, f->line_comment); + s = ast_import_decl(f, token, file_path, import_name, docs, f->line_comment); array_add(&f->imports, s); } - if (is_using) { + if (kind != ImportDecl_Standard) { syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); } diff --git a/src/parser.hpp b/src/parser.hpp index c7b4fd0d8..698ed7623 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -585,7 +585,6 @@ AST_KIND(_DeclBegin, "", bool) \ Token import_name; \ CommentGroup *docs; \ CommentGroup *comment; \ - bool is_using; \ }) \ AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \ Token token; \ -- cgit v1.2.3 From 5c647e2f613b5402dc7bc016e4e454e2ac15afb3 Mon Sep 17 00:00:00 2001 From: Cedric Hutchings Date: Sat, 21 May 2022 01:50:59 -0400 Subject: Fix typo. --- src/parser.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 1f4093e5f..ab947774b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4043,7 +4043,7 @@ Ast *parse_if_stmt(AstFile *f) { if (build_context.disallow_do) { syntax_error(body, "'do' has been disallowed"); } else if (!ast_on_same_line(cond, body)) { - syntax_error(body, "The body of a 'do' be on the same line as if condition"); + syntax_error(body, "The body of a 'do' must be on the same line as if condition"); } } else { body = parse_block_stmt(f, false); @@ -4065,7 +4065,7 @@ Ast *parse_if_stmt(AstFile *f) { if (build_context.disallow_do) { syntax_error(else_stmt, "'do' has been disallowed"); } else if (!ast_on_same_line(else_token, else_stmt)) { - syntax_error(else_stmt, "The body of a 'do' be on the same line as 'else'"); + syntax_error(else_stmt, "The body of a 'do' must be on the same line as 'else'"); } } break; default: @@ -4100,7 +4100,7 @@ Ast *parse_when_stmt(AstFile *f) { if (build_context.disallow_do) { syntax_error(body, "'do' has been disallowed"); } else if (!ast_on_same_line(cond, body)) { - syntax_error(body, "The body of a 'do' be on the same line as when statement"); + syntax_error(body, "The body of a 'do' must be on the same line as when statement"); } } else { body = parse_block_stmt(f, true); @@ -4122,7 +4122,7 @@ Ast *parse_when_stmt(AstFile *f) { if (build_context.disallow_do) { syntax_error(else_stmt, "'do' has been disallowed"); } else if (!ast_on_same_line(else_token, else_stmt)) { - syntax_error(else_stmt, "The body of a 'do' be on the same line as 'else'"); + syntax_error(else_stmt, "The body of a 'do' must be on the same line as 'else'"); } } break; default: @@ -4197,7 +4197,7 @@ Ast *parse_for_stmt(AstFile *f) { if (build_context.disallow_do) { syntax_error(body, "'do' has been disallowed"); } else if (!ast_on_same_line(token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); + syntax_error(body, "The body of a 'do' must be on the same line as the 'for' token"); } } else { body = parse_block_stmt(f, false); @@ -4243,7 +4243,7 @@ Ast *parse_for_stmt(AstFile *f) { if (build_context.disallow_do) { syntax_error(body, "'do' has been disallowed"); } else if (!ast_on_same_line(token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); + syntax_error(body, "The body of a 'do' must be on the same line as the 'for' token"); } } else { body = parse_block_stmt(f, false); @@ -4569,7 +4569,7 @@ Ast *parse_unrolled_for_loop(AstFile *f, Token unroll_token) { if (build_context.disallow_do) { syntax_error(body, "'do' has been disallowed"); } else if (!ast_on_same_line(for_token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); + syntax_error(body, "The body of a 'do' must be on the same line as the 'for' token"); } } else { body = parse_block_stmt(f, false); -- cgit v1.2.3 From 3ec70c5517062f3d35822253b2072df696b0c55f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 23 May 2022 12:04:19 +0100 Subject: Merge functionality of `#maybe` with the standard 'union' functionality --- core/encoding/json/unmarshal.odin | 2 +- core/reflect/reflect.odin | 2 +- core/reflect/types.odin | 16 +++++++++++++--- core/runtime/core.odin | 1 - core/runtime/core_builtin.odin | 2 +- src/check_expr.cpp | 1 - src/docs_writer.cpp | 1 - src/llvm_backend_type.cpp | 5 ++--- src/parser.cpp | 1 + src/parser.hpp | 2 +- src/types.cpp | 19 +++++++------------ 11 files changed, 27 insertions(+), 25 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index bd48011f1..2ff268a21 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { variant := u.variants[0] v.id = variant.id ti = reflect.type_info_base(variant) - if !(u.maybe && reflect.is_pointer(variant)) { + if !reflect.is_pointer_internally(variant) { tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id} assign_int(tag, 1) } diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 49d7ef9b5..27a83e680 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -654,7 +654,7 @@ union_variant_type_info :: proc(a: any) -> ^Type_Info { } type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool { - return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0]) + return len(info.variants) == 1 && is_pointer(info.variants[0]) } union_variant_typeid :: proc(a: any) -> typeid { diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 2e2149820..b211abb45 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -256,6 +256,17 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer) return ok } +is_pointer_internally :: proc(info: ^Type_Info) -> bool { + if info == nil { return false } + #partial switch v in info.variant { + case Type_Info_Pointer, Type_Info_Multi_Pointer, + Type_Info_Procedure: + return true + case Type_Info_String: + return v.is_cstring + } + return false +} is_procedure :: proc(info: ^Type_Info) -> bool { if info == nil { return false } _, ok := type_info_base(info).variant.(Type_Info_Procedure) @@ -531,9 +542,8 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) - case Type_Info_Union: io.write_string(w, "union ", &n) or_return - if info.maybe { - io.write_string(w, "#maybe ", &n) or_return - } + if info.no_nil { io.write_string(w, "#no_nil ", &n) or_return } + if info.shared_nil { io.write_string(w, "#shared_nil ", &n) or_return } if info.custom_align { io.write_string(w, "#align ", &n) or_return io.write_i64(w, i64(ti.align), 10, &n) or_return diff --git a/core/runtime/core.odin b/core/runtime/core.odin index e2933d20a..73d1e6371 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -135,7 +135,6 @@ Type_Info_Union :: struct { custom_align: bool, no_nil: bool, - maybe: bool, shared_nil: bool, } Type_Info_Enum :: struct { diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index cb72e397d..4ddc3928a 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -3,7 +3,7 @@ package runtime import "core:intrinsics" @builtin -Maybe :: union($T: typeid) #maybe {T} +Maybe :: union($T: typeid) {T} @builtin diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 013d22913..b911771b1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10063,7 +10063,6 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = gb_string_appendc(str, ") "); } switch (st->kind) { - case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break; case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break; case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break; } diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 0ad10ac49..2f531a45c 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -620,7 +620,6 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) { doc_type.kind = OdinDocType_Union; if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; } switch (type->Union.kind) { - case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break; case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break; case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break; } diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 7d73956e8..2e7b2788a 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr); { - LLVMValueRef vals[8] = {}; + LLVMValueRef vals[7] = {}; isize variant_count = gb_max(0, t->Union.variants.count); lbValue memory_types = lb_type_info_member_types_offset(p, variant_count); @@ -676,8 +676,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value; vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value; - vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value; - vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; + vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value; for (isize i = 0; i < gb_count_of(vals); i++) { if (vals[i] == nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index ab947774b..d19e249e5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2529,6 +2529,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (maybe) { union_kind = UnionType_maybe; + syntax_error(f->curr_token, "#maybe functionality has now been merged with standard 'union' functionality"); } else if (no_nil) { union_kind = UnionType_no_nil; } else if (shared_nil) { diff --git a/src/parser.hpp b/src/parser.hpp index 698ed7623..dc294b6ce 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -332,7 +332,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = { enum UnionTypeKind : u8 { UnionType_Normal = 0, - UnionType_maybe = 1, + UnionType_maybe = 1, // removed UnionType_no_nil = 2, UnionType_shared_nil = 3, }; diff --git a/src/types.cpp b/src/types.cpp index b4dc17256..c79b8e652 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1685,11 +1685,9 @@ bool is_type_map(Type *t) { bool is_type_union_maybe_pointer(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - return is_type_pointer(v) || is_type_multi_pointer(v); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + return is_type_internally_pointer_like(v); } return false; } @@ -1697,12 +1695,10 @@ bool is_type_union_maybe_pointer(Type *t) { bool is_type_union_maybe_pointer_original_alignment(Type *t) { t = base_type(t); - if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) { - if (t->Union.variants.count == 1) { - Type *v = t->Union.variants[0]; - if (is_type_pointer(v) || is_type_multi_pointer(v)) { - return type_align_of(v) == type_align_of(t); - } + if (t->kind == Type_Union && t->Union.variants.count == 1) { + Type *v = t->Union.variants[0]; + if (is_type_internally_pointer_like(v)) { + return type_align_of(v) == type_align_of(t); } } return false; @@ -4054,7 +4050,6 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) { case Type_Union: str = gb_string_appendc(str, "union"); switch (type->Union.kind) { - case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break; case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break; case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break; } -- cgit v1.2.3 From 66b5a35ec352b74e66ad866640669d920e9d0849 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 26 May 2022 13:45:47 +0100 Subject: Add `simd_to_bits`; correct fix typo causing issue with parapoly --- src/check_builtin.cpp | 27 +++++++++++++++++++++++++++ src/check_decl.cpp | 8 ++++---- src/check_type.cpp | 7 ++++--- src/checker_builtin_procs.hpp | 4 ++++ src/llvm_backend_proc.cpp | 5 +++++ src/parser.cpp | 14 +++++++++++++- 6 files changed, 57 insertions(+), 8 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 45c9c93c5..c432d6080 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1007,6 +1007,33 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call return true; } + case BuiltinProc_simd_to_bits: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) { return false; } + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + Type *elem = base_array_type(x.type); + i64 count = get_array_type_count(x.type); + i64 sz = type_size_of(elem); + Type *bit_elem = nullptr; + switch (sz) { + case 1: bit_elem = t_u8; break; + case 2: bit_elem = t_u16; break; + case 4: bit_elem = t_u32; break; + case 8: bit_elem = t_u64; break; + } + GB_ASSERT(bit_elem != nullptr); + + operand->type = alloc_type_simd_vector(count, bit_elem); + operand->mode = Addressing_Value; + return true; + } + + default: GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name)); } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d8cad2ce1..62a1e2555 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1315,20 +1315,20 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) if (!both_have_where_clauses) switch (kind) { case ProcOverload_Identical: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; // case ProcOverload_CallingConvention: - // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + // error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); // is_invalid = true; // break; case ProcOverload_ParamVariadic: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_ResultCount: case ProcOverload_ResultTypes: - error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); + error(p->token, "Overloaded procedure '%.*s' has the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_Polymorphic: diff --git a/src/check_type.cpp b/src/check_type.cpp index de58db054..f84c15a19 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1234,7 +1234,7 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ } -Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) { +Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) { bool modify_type = !ctx->no_polymorphic_errors; bool show_error = modify_type && !ctx->hide_polymorphic_errors; if (!is_operand_value(operand)) { @@ -2803,13 +2803,14 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t goto array_end; } - if (is_type_polymorphic(elem)) { + if (generic_type != nullptr) { // Ignore } else if (count < 1 || !is_power_of_two(count)) { error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); *type = alloc_type_array(elem, count, generic_type); goto array_end; - } else + } + *type = alloc_type_simd_vector(count, elem, generic_type); if (is_arch_wasm()) { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index eea4d0a8b..350213de2 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -170,6 +170,8 @@ BuiltinProc__simd_begin, BuiltinProc_simd_trunc, BuiltinProc_simd_nearest, + BuiltinProc_simd_to_bits, + BuiltinProc_simd_reverse, BuiltinProc_simd_rotate_left, BuiltinProc_simd_rotate_right, @@ -450,6 +452,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 97bb02ba3..a56aa862a 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1434,6 +1434,11 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_to_bits: + { + res.value = LLVMBuildBitCast(p->builder, arg0.value, lb_type(m, tv.type), ""); + return res; + } } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name)); diff --git a/src/parser.cpp b/src/parser.cpp index d19e249e5..5280fd4b0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -360,6 +360,7 @@ Ast *clone_ast(Ast *node) { case Ast_ArrayType: n->ArrayType.count = clone_ast(n->ArrayType.count); n->ArrayType.elem = clone_ast(n->ArrayType.elem); + n->ArrayType.tag = clone_ast(n->ArrayType.tag); break; case Ast_DynamicArrayType: n->DynamicArrayType.elem = clone_ast(n->DynamicArrayType.elem); @@ -2127,7 +2128,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token name = expect_token(f, Token_Ident); if (name.string == "type") { return ast_helper_type(f, token, parse_type(f)); - } else if (name.string == "soa" || name.string == "simd") { + } else if ( name.string == "simd") { + Ast *tag = ast_basic_directive(f, token, name); + Ast *original_type = parse_type(f); + Ast *type = unparen_expr(original_type); + switch (type->kind) { + case Ast_ArrayType: type->ArrayType.tag = tag; break; + default: + syntax_error(type, "Expected a fixed array type after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[type->kind])); + break; + } + return original_type; + } else if (name.string == "soa") { Ast *tag = ast_basic_directive(f, token, name); Ast *original_type = parse_type(f); Ast *type = unparen_expr(original_type); -- cgit v1.2.3 From ba5f7c4e2af5c82c220b7e1796fde2f026ce4208 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 1 Jun 2022 11:08:19 +0100 Subject: Deprecate `a..b` based ranges in favour of `..=` --- core/encoding/entity/entity.odin | 6 +++--- core/encoding/xml/tokenizer.odin | 2 +- src/parser.cpp | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/encoding/entity/entity.odin b/core/encoding/entity/entity.odin index db1a5ad0b..e5831a75f 100644 --- a/core/encoding/entity/entity.odin +++ b/core/encoding/entity/entity.odin @@ -231,16 +231,16 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) { for len(entity) > 0 { r := entity[0] switch r { - case '0'..'9': + case '0'..='9': val *= base val += int(r - '0') - case 'a'..'f': + case 'a'..='f': if base == 10 { return -1, false } val *= base val += int(r - 'a' + 10) - case 'A'..'F': + case 'A'..='F': if base == 10 { return -1, false } val *= base val += int(r - 'A' + 10) diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin index c3fece76e..d225c5d90 100644 --- a/core/encoding/xml/tokenizer.odin +++ b/core/encoding/xml/tokenizer.odin @@ -198,7 +198,7 @@ is_valid_identifier_rune :: proc(r: rune) -> bool { switch r { case '_', '-', ':': return true case 'A'..='Z', 'a'..='z': return true - case '0'..'9': return true + case '0'..='9': return true case -1: return false } } diff --git a/src/parser.cpp b/src/parser.cpp index 5280fd4b0..b58e3c320 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1428,6 +1428,7 @@ Token expect_operator(AstFile *f) { LIT(p)); } if (f->curr_token.kind == Token_Ellipsis) { + syntax_warning(f->curr_token, "'..' for ranges has now be deprecated, prefer '..='"); f->tokens[f->curr_token_index].flags |= TokenFlag_Replace; } -- cgit v1.2.3 From 35fd8e7f68340a25bcb8397ab35ae5f5fc282657 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 12 Jun 2022 12:20:00 +0100 Subject: Move trailing comma requirement in field lists to `-strict-style` --- src/parser.cpp | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index b58e3c320..30b9455c1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1461,7 +1461,11 @@ Token expect_closing_brace_of_field_list(AstFile *f) { if (allow_token(f, Token_CloseBrace)) { return token; } - if (allow_token(f, Token_Semicolon)) { + bool ok = true; + if (!build_context.strict_style) { + ok = !skip_possible_newline(f); + } + if (ok && allow_token(f, Token_Semicolon)) { String p = token_to_string(token); syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); } @@ -2065,6 +2069,20 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta return s; } +Array parse_union_variant_list(AstFile *f) { + auto variants = array_make(heap_allocator()); + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + Ast *type = parse_type(f); + if (type->kind != Ast_BadExpr) { + array_add(&variants, type); + } + if (!allow_token(f, Token_Comma)) { + break; + } + } + return variants; +} Ast *parse_operand(AstFile *f, bool lhs) { Ast *operand = nullptr; // Operand @@ -2418,7 +2436,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { check_polymorphic_params_for_type(f, polymorphic_params, token); } - isize prev_level = f->expr_level; + isize prev_level; + + prev_level = f->expr_level; f->expr_level = -1; while (allow_token(f, Token_Hash)) { @@ -2457,7 +2477,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { if (f->curr_token.kind == Token_where) { where_token = expect_token(f, Token_where); - isize prev_level = f->expr_level; + prev_level = f->expr_level; f->expr_level = -1; where_clauses = parse_rhs_expr_list(f); f->expr_level = prev_level; @@ -2481,7 +2501,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_union: { Token token = expect_token(f, Token_union); - auto variants = array_make(heap_allocator()); Ast *polymorphic_params = nullptr; Ast *align = nullptr; bool no_nil = false; @@ -2565,18 +2584,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { skip_possible_newline_for_literal(f); Token open = expect_token_after(f, Token_OpenBrace, "union"); - - while (f->curr_token.kind != Token_CloseBrace && - f->curr_token.kind != Token_EOF) { - Ast *type = parse_type(f); - if (type->kind != Ast_BadExpr) { - array_add(&variants, type); - } - if (!allow_token(f, Token_Comma)) { - break; - } - } - + auto variants = parse_union_variant_list(f); Token close = expect_closing_brace_of_field_list(f); return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses); @@ -2734,7 +2742,7 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) { isize prev_expr_level = f->expr_level; bool prev_allow_newline = f->allow_newline; f->expr_level = 0; - f->allow_newline = true; + f->allow_newline = build_context.strict_style; open_paren = expect_token(f, Token_OpenParen); @@ -3775,6 +3783,10 @@ bool check_procedure_name_list(Array const &names) { } Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) { + bool prev_allow_newline = f->allow_newline; + defer (f->allow_newline = prev_allow_newline); + f->allow_newline = build_context.strict_style; + Token start_token = f->curr_token; CommentGroup *docs = f->lead_comment; -- cgit v1.2.3 From 8c0c327df95208634e87df10c2f33b3c870637e4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 Jun 2022 11:00:13 +0100 Subject: Improvements to `-strict-style` and trailing commas --- src/main.cpp | 2 +- src/parser.cpp | 158 +++++++++++++++++++++++++++------------------------------ src/parser.hpp | 1 + 3 files changed, 77 insertions(+), 84 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/main.cpp b/src/main.cpp index ee71b91df..7531fb37c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -574,7 +574,7 @@ void usage(String argv0) { print_usage_line(1, "check parse, and type check a directory of .odin files"); print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program"); - print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package"); + print_usage_line(1, "test build and runs procedures with the attribute @(test) in the initial package"); print_usage_line(1, "doc generate documentation on a directory of .odin files"); print_usage_line(1, "version print version"); print_usage_line(1, "report print information useful to reporting a bug"); diff --git a/src/parser.cpp b/src/parser.cpp index 30b9455c1..22c76d746 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,5 +1,8 @@ #include "parser_pos.cpp" +// #undef at the bottom of this file +#define ALLOW_NEWLINE build_context.strict_style + Token token_end_of_line(AstFile *f, Token tok) { u8 const *start = f->tokenizer.start + tok.pos.offset; u8 const *s = start; @@ -1278,15 +1281,10 @@ void consume_comment_groups(AstFile *f, Token prev) { } } -bool ignore_newlines(AstFile *f) { - if (f->allow_newline) { - return f->expr_level > 0; - } - return f->expr_level >= 0; +gb_inline bool ignore_newlines(AstFile *f) { + return f->expr_level > 0; } - - Token advance_token(AstFile *f) { f->lead_comment = nullptr; f->line_comment = nullptr; @@ -1462,7 +1460,7 @@ Token expect_closing_brace_of_field_list(AstFile *f) { return token; } bool ok = true; - if (!build_context.strict_style) { + if (!f->allow_newline) { ok = !skip_possible_newline(f); } if (ok && allow_token(f, Token_Semicolon)) { @@ -1539,13 +1537,15 @@ void fix_advance_to_next_stmt(AstFile *f) { } } -Token expect_closing(AstFile *f, TokenKind kind, String context) { +Token expect_closing(AstFile *f, TokenKind kind, String const &context) { if (f->curr_token.kind != kind && f->curr_token.kind == Token_Semicolon && (f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) { - Token tok = f->prev_token; - tok.pos.column += cast(i32)tok.string.len; - syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context)); + if (f->allow_newline) { + Token tok = f->prev_token; + tok.pos.column += cast(i32)tok.string.len; + syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context)); + } advance_token(f); } return expect_token(f, kind); @@ -1625,6 +1625,7 @@ Ast * parse_proc_type(AstFile *f, Token proc_token); Array parse_stmt_list(AstFile *f); Ast * parse_stmt(AstFile *f); Ast * parse_body(AstFile *f); +Ast * parse_do_body(AstFile *f, Token const &token, char const *msg); Ast * parse_block_stmt(AstFile *f, b32 is_when); @@ -1708,7 +1709,7 @@ Array parse_element_list(AstFile *f) { array_add(&elems, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -1744,7 +1745,7 @@ Array parse_enum_field_list(AstFile *f) { Ast *elem = ast_enum_field_value(f, name, value, docs, comment); array_add(&elems, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } @@ -1785,14 +1786,14 @@ Ast *parse_value(AstFile *f) { Ast *parse_type_or_ident(AstFile *f); -void check_proc_add_tag(AstFile *f, Ast *tag_expr, u64 *tags, ProcTag tag, String tag_name) { +void check_proc_add_tag(AstFile *f, Ast *tag_expr, u64 *tags, ProcTag tag, String const &tag_name) { if (*tags & tag) { syntax_error(tag_expr, "Procedure tag already used: %.*s", LIT(tag_name)); } *tags |= tag; } -bool is_foreign_name_valid(String name) { +bool is_foreign_name_valid(String const &name) { if (name.len == 0) { return false; } @@ -1888,7 +1889,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi Ast *parse_unary_expr(AstFile *f, bool lhs); -Ast *convert_stmt_to_expr(AstFile *f, Ast *statement, String kind) { +Ast *convert_stmt_to_expr(AstFile *f, Ast *statement, String const &kind) { if (statement == nullptr) { return nullptr; } @@ -2077,7 +2078,7 @@ Array parse_union_variant_list(AstFile *f) { if (type->kind != Ast_BadExpr) { array_add(&variants, type); } - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -2235,7 +2236,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Ast *elem = parse_expr(f, false); array_add(&args, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -2742,7 +2743,7 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) { isize prev_expr_level = f->expr_level; bool prev_allow_newline = f->allow_newline; f->expr_level = 0; - f->allow_newline = build_context.strict_style; + f->allow_newline = ALLOW_NEWLINE; open_paren = expect_token(f, Token_OpenParen); @@ -2776,7 +2777,7 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) { } array_add(&args, arg); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -3125,7 +3126,7 @@ Ast *parse_expr(AstFile *f, bool lhs) { Array parse_expr_list(AstFile *f, bool lhs) { bool allow_newline = f->allow_newline; - f->allow_newline = true; + f->allow_newline = ALLOW_NEWLINE; auto list = array_make(heap_allocator()); for (;;) { @@ -3445,7 +3446,7 @@ Ast *parse_results(AstFile *f, bool *diverging) { } -ProcCallingConvention string_to_calling_convention(String s) { +ProcCallingConvention string_to_calling_convention(String const &s) { if (s == "odin") return ProcCC_Odin; if (s == "contextless") return ProcCC_Contextless; if (s == "cdecl") return ProcCC_CDecl; @@ -3726,12 +3727,12 @@ Array convert_to_ident_list(AstFile *f, Array list, bool ign } -bool parse_expect_field_separator(AstFile *f, Ast *param) { +bool allow_field_separator(AstFile *f) { Token token = f->curr_token; if (allow_token(f, Token_Comma)) { return true; } - if (token.kind == Token_Semicolon) { + if (ALLOW_NEWLINE && token.kind == Token_Semicolon) { String p = token_to_string(token); syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); advance_token(f); @@ -3785,7 +3786,7 @@ bool check_procedure_name_list(Array const &names) { Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) { bool prev_allow_newline = f->allow_newline; defer (f->allow_newline = prev_allow_newline); - f->allow_newline = build_context.strict_style; + f->allow_newline = ALLOW_NEWLINE; Token start_token = f->curr_token; @@ -3816,7 +3817,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi } AstAndFlags naf = {param, flags}; array_add(&list, naf); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -3886,7 +3887,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi } } - parse_expect_field_separator(f, type); + allow_field_separator(f); Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); @@ -3948,7 +3949,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi } - bool ok = parse_expect_field_separator(f, param); + bool ok = allow_field_separator(f); Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); @@ -4005,17 +4006,41 @@ Ast *parse_body(AstFile *f) { Array stmts = {}; Token open, close; isize prev_expr_level = f->expr_level; + bool prev_allow_newline = f->allow_newline; // NOTE(bill): The body may be within an expression so reset to zero f->expr_level = 0; + f->allow_newline = false; open = expect_token(f, Token_OpenBrace); stmts = parse_stmt_list(f); close = expect_token(f, Token_CloseBrace); f->expr_level = prev_expr_level; + f->allow_newline = prev_allow_newline; return ast_block_stmt(f, stmts, open, close); } +Ast *parse_do_body(AstFile *f, Token const &token, char const *msg) { + Token open, close; + isize prev_expr_level = f->expr_level; + bool prev_allow_newline = f->allow_newline; + + // NOTE(bill): The body may be within an expression so reset to zero + f->expr_level = 0; + f->allow_newline = false; + + Ast *body = convert_stmt_to_body(f, parse_stmt(f)); + if (build_context.disallow_do) { + syntax_error(body, "'do' has been disallowed"); + } else if (token.pos.file_id != 0 && !ast_on_same_line(token, body)) { + syntax_error(body, "The body of a 'do' must be on the same line as %s", msg); + } + f->expr_level = prev_expr_level; + f->allow_newline = prev_allow_newline; + + return body; +} + bool parse_control_statement_semicolon_separator(AstFile *f) { Token tok = peek_token(f); if (tok.kind != Token_OpenBrace) { @@ -4065,12 +4090,7 @@ Ast *parse_if_stmt(AstFile *f) { } if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(cond, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as if condition"); - } + body = parse_do_body(f, cond ? ast_token(cond) : token, "the if statement"); } else { body = parse_block_stmt(f, false); } @@ -4085,15 +4105,10 @@ Ast *parse_if_stmt(AstFile *f) { case Token_OpenBrace: else_stmt = parse_block_stmt(f, false); break; - case Token_do: { + case Token_do: expect_token(f, Token_do); - else_stmt = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(else_stmt, "'do' has been disallowed"); - } else if (!ast_on_same_line(else_token, else_stmt)) { - syntax_error(else_stmt, "The body of a 'do' must be on the same line as 'else'"); - } - } break; + else_stmt = parse_do_body(f, else_token, "'else'"); + break; default: syntax_error(f->curr_token, "Expected if statement block statement"); else_stmt = ast_bad_stmt(f, f->curr_token, f->tokens[f->curr_token_index+1]); @@ -4122,12 +4137,7 @@ Ast *parse_when_stmt(AstFile *f) { } if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(cond, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as when statement"); - } + body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement"); } else { body = parse_block_stmt(f, true); } @@ -4144,12 +4154,7 @@ Ast *parse_when_stmt(AstFile *f) { break; case Token_do: { expect_token(f, Token_do); - else_stmt = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(else_stmt, "'do' has been disallowed"); - } else if (!ast_on_same_line(else_token, else_stmt)) { - syntax_error(else_stmt, "The body of a 'do' must be on the same line as 'else'"); - } + else_stmt = parse_do_body(f, else_token, "'else'"); } break; default: syntax_error(f->curr_token, "Expected when statement block statement"); @@ -4219,12 +4224,7 @@ Ast *parse_for_stmt(AstFile *f) { f->allow_range = prev_allow_range; if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(token, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as the 'for' token"); - } + body = parse_do_body(f, token, "the for statement"); } else { body = parse_block_stmt(f, false); } @@ -4265,12 +4265,7 @@ Ast *parse_for_stmt(AstFile *f) { if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(token, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as the 'for' token"); - } + body = parse_do_body(f, token, "the for statement"); } else { body = parse_block_stmt(f, false); } @@ -4469,7 +4464,7 @@ Ast *parse_foreign_decl(AstFile *f) { Token path = expect_token(f, Token_String); array_add(&filepaths, path); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -4526,7 +4521,7 @@ Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind clo array_add(&elems, elem); - if (!allow_token(f, Token_Comma)) { + if (!allow_field_separator(f)) { break; } } @@ -4591,12 +4586,7 @@ Ast *parse_unrolled_for_loop(AstFile *f, Token unroll_token) { f->allow_range = prev_allow_range; if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(for_token, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as the 'for' token"); - } + body = parse_do_body(f, for_token, "the for statement"); } else { body = parse_block_stmt(f, false); } @@ -4780,7 +4770,7 @@ Ast *parse_stmt(AstFile *f) { return parse_block_stmt(f, true); case Token_do: { expect_token(f, Token_do); - Ast *stmt = convert_stmt_to_body(f, parse_stmt(f)); + Ast *stmt = parse_do_body(f, {}, "the for statement"); if (build_context.disallow_do) { syntax_error(stmt, "'do' has been disallowed"); } @@ -4819,7 +4809,7 @@ Array parse_stmt_list(AstFile *f) { } -ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) { +ParseFileError init_ast_file(AstFile *f, String const &fullpath, TokenPos *err_pos) { GB_ASSERT(f != nullptr); f->fullpath = string_trim_whitespace(fullpath); // Just in case set_file_path_string(f->id, fullpath); @@ -5114,7 +5104,7 @@ gb_global Rune illegal_import_runes[] = { '|', ',', '<', '>', '?', }; -bool is_import_path_valid(String path) { +bool is_import_path_valid(String const &path) { if (path.len > 0) { u8 *start = path.text; u8 *end = path.text + path.len; @@ -5146,7 +5136,7 @@ bool is_import_path_valid(String path) { return false; } -bool is_build_flag_path_valid(String path) { +bool is_build_flag_path_valid(String const &path) { if (path.len > 0) { u8 *start = path.text; u8 *end = path.text + path.len; @@ -5198,7 +5188,7 @@ bool is_package_name_reserved(String const &name) { } -bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String original_string, String *path) { +bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String base_dir, String const &original_string, String *path) { GB_ASSERT(path != nullptr); // NOTE(bill): if file_mutex == nullptr, this means that the code is used within the semantics stage @@ -5312,9 +5302,9 @@ bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node, String bas -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls); +void parse_setup_file_decls(Parser *p, AstFile *f, String const &base_dir, Slice &decls); -void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenStmt *ws) { +void parse_setup_file_when_stmt(Parser *p, AstFile *f, String const &base_dir, AstWhenStmt *ws) { if (ws->body != nullptr) { auto stmts = ws->body->BlockStmt.stmts; parse_setup_file_decls(p, f, base_dir, stmts); @@ -5333,7 +5323,7 @@ void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstWhenS } } -void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Slice &decls) { +void parse_setup_file_decls(Parser *p, AstFile *f, String const &base_dir, Slice &decls) { for_array(i, decls) { Ast *node = decls[i]; if (!is_ast_decl(node) && @@ -5833,3 +5823,5 @@ ParseFileError parse_packages(Parser *p, String init_filename) { } + +#undef ALLOW_NEWLINE diff --git a/src/parser.hpp b/src/parser.hpp index a648828fb..c167ef6d5 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -814,3 +814,4 @@ gbAllocator ast_allocator(AstFile *f) { Ast *alloc_ast_node(AstFile *f, AstKind kind); gbString expr_to_string(Ast *expression); +bool allow_field_separator(AstFile *f); \ No newline at end of file -- cgit v1.2.3 From b40998de9e74588a498fecaa4a2ac903da411f7e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 Jun 2022 11:11:51 +0100 Subject: Improve `-strict-style` for `foreign import` --- src/parser.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 22c76d746..a6f30cdfd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2112,6 +2112,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_OpenParen: { bool allow_newline; + isize prev_expr_level; Token open, close; // NOTE(bill): Skip the Paren Expression open = expect_token(f, Token_OpenParen); @@ -2121,16 +2122,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { return ast_bad_expr(f, open, close); } + prev_expr_level = f->expr_level; allow_newline = f->allow_newline; if (f->expr_level < 0) { f->allow_newline = false; } - f->expr_level++; + // NOTE(bill): enforce it to >0 + f->expr_level = gb_max(f->expr_level, 0)+1; operand = parse_expr(f, false); - f->expr_level--; f->allow_newline = allow_newline; + f->expr_level = prev_expr_level; close = expect_token(f, Token_CloseParen); return ast_paren_expr(f, operand, open, close); @@ -4010,7 +4013,7 @@ Ast *parse_body(AstFile *f) { // NOTE(bill): The body may be within an expression so reset to zero f->expr_level = 0; - f->allow_newline = false; + // f->allow_newline = false; open = expect_token(f, Token_OpenBrace); stmts = parse_stmt_list(f); close = expect_token(f, Token_CloseBrace); @@ -4468,7 +4471,7 @@ Ast *parse_foreign_decl(AstFile *f) { break; } } - expect_token(f, Token_CloseBrace); + expect_closing_brace_of_field_list(f); } else { filepaths = array_make(heap_allocator(), 0, 1); Token path = expect_token(f, Token_String); -- cgit v1.2.3 From 041625381cfd6236de5bc5fea3a398123b7328ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Jul 2022 17:36:03 +0100 Subject: Fix #1888 --- src/check_expr.cpp | 8 ++++++-- src/llvm_backend.hpp | 2 ++ src/llvm_backend_expr.cpp | 38 ++++++++++++++++++++++++++++---------- src/llvm_backend_proc.cpp | 5 +---- src/parser.cpp | 1 + src/parser.hpp | 4 +++- 6 files changed, 41 insertions(+), 17 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b42301cd6..13d6badbe 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8643,6 +8643,8 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type Ast *first_arg = x.expr->SelectorExpr.expr; GB_ASSERT(first_arg != nullptr); + first_arg->state_flags |= StateFlag_SelectorCallExpr; + Type *pt = base_type(x.type); GB_ASSERT(pt->kind == Type_Proc); Type *first_type = nullptr; @@ -8668,6 +8670,7 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type y.mode = first_arg->tav.mode; y.type = first_arg->tav.type; y.value = first_arg->tav.value; + if (check_is_assignable_to(c, &y, first_type)) { // Do nothing, it's valid } else { @@ -10034,9 +10037,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) { str = write_expr_to_string(str, ce->proc, shorthand); str = gb_string_appendc(str, "("); - for_array(i, ce->args) { + isize idx0 = cast(isize)ce->was_selector; + for (isize i = idx0; i < ce->args.count; i++) { Ast *arg = ce->args[i]; - if (i > 0) { + if (i > idx0) { str = gb_string_appendc(str, ", "); } str = write_expr_to_string(str, arg, shorthand); diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index f65e1079e..87a354a49 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -292,6 +292,8 @@ struct lbProcedure { LLVMMetadataRef debug_info; lbCopyElisionHint copy_elision_hint; + + PtrMap selector_values; }; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 1894e85f6..a95d884b0 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2993,9 +2993,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) { return lb_build_addr_ptr(p, ue->expr); } +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr); lbValue lb_build_expr(lbProcedure *p, Ast *expr) { - lbModule *m = p->module; - u16 prev_state_flags = p->state_flags; defer (p->state_flags = prev_state_flags); @@ -3022,6 +3021,32 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { p->state_flags = out; } + + // IMPORTANT NOTE(bill): + // Selector Call Expressions (foo->bar(...)) + // must only evaluate `foo` once as it gets transformed into + // `foo.bar(foo, ...)` + // And if `foo` is a procedure call or something more complex, storing the value + // once is a very good idea + // If a stored value is found, it must be removed from the cache + if (expr->state_flags & StateFlag_SelectorCallExpr) { + lbValue *pp = map_get(&p->selector_values, expr); + if (pp != nullptr) { + lbValue res = *pp; + map_remove(&p->selector_values, expr); + return res; + } + } + lbValue res = lb_build_expr_internal(p, expr); + if (expr->state_flags & StateFlag_SelectorCallExpr) { + map_set(&p->selector_values, expr, res); + } + return res; +} + +lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) { + lbModule *m = p->module; + expr = unparen_expr(expr); TokenPos expr_pos = ast_token(expr).pos; @@ -3119,14 +3144,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_ast_node(se, SelectorCallExpr, expr); GB_ASSERT(se->modified_call); - TypeAndValue tav = type_and_value_of_expr(expr); - GB_ASSERT(tav.mode != Addressing_Invalid); - lbValue res = lb_build_call_expr(p, se->call); - - ast_node(ce, CallExpr, se->call); - ce->sce_temp_data = gb_alloc_copy(permanent_allocator(), &res, gb_size_of(res)); - - return res; + return lb_build_call_expr(p, se->call); case_end; case_ast_node(te, TernaryIfExpr, expr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 75ca77641..1c0ecabbe 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -113,6 +113,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) p->branch_blocks.allocator = a; p->context_stack.allocator = a; p->scope_stack.allocator = a; + map_init(&p->selector_values, a); if (p->is_foreign) { lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library); @@ -2832,10 +2833,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) { expr = unparen_expr(expr); ast_node(ce, CallExpr, expr); - if (ce->sce_temp_data) { - return *(lbValue *)ce->sce_temp_data; - } - lbValue res = lb_build_call_expr_internal(p, expr); if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures diff --git a/src/parser.cpp b/src/parser.cpp index a6f30cdfd..b63c530d6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -164,6 +164,7 @@ Ast *clone_ast(Ast *node) { case Ast_SelectorCallExpr: n->SelectorCallExpr.expr = clone_ast(n->SelectorCallExpr.expr); n->SelectorCallExpr.call = clone_ast(n->SelectorCallExpr.call); + GB_ASSERT(n->SelectorCallExpr.modified_call); break; case Ast_IndexExpr: n->IndexExpr.expr = clone_ast(n->IndexExpr.expr); diff --git a/src/parser.hpp b/src/parser.hpp index c167ef6d5..8719b5e56 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,6 +282,8 @@ enum StateFlag : u8 { StateFlag_type_assert = 1<<2, StateFlag_no_type_assert = 1<<3, + StateFlag_SelectorCallExpr = 1<<6, + StateFlag_BeenHandled = 1<<7, }; @@ -411,7 +413,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token ellipsis; \ ProcInlining inlining; \ bool optional_ok_one; \ - void *sce_temp_data; \ + bool was_selector; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3 From 80c10644ddb6676c879e045e24f200632cd6e2a7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 16 Jul 2022 17:39:13 +0100 Subject: Remove assert --- src/parser.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index b63c530d6..a6f30cdfd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -164,7 +164,6 @@ Ast *clone_ast(Ast *node) { case Ast_SelectorCallExpr: n->SelectorCallExpr.expr = clone_ast(n->SelectorCallExpr.expr); n->SelectorCallExpr.call = clone_ast(n->SelectorCallExpr.call); - GB_ASSERT(n->SelectorCallExpr.modified_call); break; case Ast_IndexExpr: n->IndexExpr.expr = clone_ast(n->IndexExpr.expr); -- cgit v1.2.3 From 22a0c3fce1b41c62a2038060b592e0ae79912d4d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 18 Jul 2022 15:09:04 +0100 Subject: Disallow `proc() do stmt` and only allow `proc() { stmt }` --- core/sys/windows/winerror.odin | 2 +- src/parser.cpp | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'src/parser.cpp') diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin index b7652d1f4..7bd0bfe9f 100644 --- a/core/sys/windows/winerror.odin +++ b/core/sys/windows/winerror.odin @@ -45,4 +45,4 @@ ERROR_NOT_SAME_OBJECT : DWORD : 1656 E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001 -SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool do return result >= 0 +SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool { return result >= 0 } diff --git a/src/parser.cpp b/src/parser.cpp index a6f30cdfd..dd9d6f036 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2324,11 +2324,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = convert_stmt_to_body(f, parse_stmt(f)); f->curr_proc = curr_proc; - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(type, body)) { - syntax_error(body, "The body of a 'do' must be on the same line as the signature"); - } + syntax_error(body, "'do' for procedure bodies is not allowed, prefer {}"); return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } -- cgit v1.2.3 From c8ab1b7ee1b1ba6444a057c6afa6a9d6eb7a7dae Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 24 Jul 2022 13:11:48 +0100 Subject: Add `#by_ptr` procedure attribute to enforce a parameter to be passed by pointer internally --- src/check_type.cpp | 20 +++++++++++++++++++- src/entity.cpp | 1 + src/llvm_backend_general.cpp | 5 +++-- src/parser.cpp | 2 ++ src/parser.hpp | 3 ++- 5 files changed, 27 insertions(+), 4 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 741385e29..dea523599 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1616,6 +1616,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is error(name, "'#any_int' can only be applied to variable fields"); p->flags &= ~FieldFlag_any_int; } + if (p->flags&FieldFlag_by_ptr) { + error(name, "'#by_ptr' can only be applied to variable fields"); + p->flags &= ~FieldFlag_by_ptr; + } param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved); param->TypeName.is_type_alias = true; @@ -1692,10 +1696,17 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is if (p->flags&FieldFlag_no_alias) { if (!is_type_pointer(type)) { - error(name, "'#no_alias' can only be applied to fields of pointer type"); + error(name, "'#no_alias' can only be applied pointer typed parameters"); p->flags &= ~FieldFlag_no_alias; // Remove the flag } } + if (p->flags&FieldFlag_by_ptr) { + if (is_type_internally_pointer_like(type)) { + error(name, "'#by_ptr' can only be applied to non-pointer-like parameters"); + p->flags &= ~FieldFlag_by_ptr; // Remove the flag + } + } + if (is_poly_name) { if (p->flags&FieldFlag_no_alias) { error(name, "'#no_alias' can only be applied to non constant values"); @@ -1713,6 +1724,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is error(name, "'#const' can only be applied to variable fields"); p->flags &= ~FieldFlag_const; } + if (p->flags&FieldFlag_by_ptr) { + error(name, "'#by_ptr' can only be applied to variable fields"); + p->flags &= ~FieldFlag_by_ptr; + } if (!is_type_constant_type(type) && !is_type_polymorphic(type)) { gbString str = type_to_string(type); @@ -1745,6 +1760,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is if (p->flags&FieldFlag_const) { param->flags |= EntityFlag_ConstInput; } + if (p->flags&FieldFlag_by_ptr) { + param->flags |= EntityFlag_ByPtr; + } param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it add_entity(ctx, scope, name, param); diff --git a/src/entity.cpp b/src/entity.cpp index 76e6912b9..3d3712328 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -83,6 +83,7 @@ enum EntityFlag : u64 { EntityFlag_CustomLinkage_LinkOnce = 1ull<<44, EntityFlag_Require = 1ull<<50, + EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer EntityFlag_Overridden = 1ull<<63, }; diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index b2a609f85..a4c2ce370 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1958,11 +1958,12 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (e->flags & EntityFlag_CVarArg) { continue; } - Type *e_type = reduce_tuple_to_single_type(e->type); LLVMTypeRef param_type = nullptr; - if (is_type_boolean(e_type) && + if (e->flags & EntityFlag_ByPtr) { + param_type = lb_type(m, alloc_type_pointer(e_type)); + } else if (is_type_boolean(e_type) && type_size_of(e_type) <= 1) { param_type = LLVMInt1TypeInContext(m->ctx); } else { diff --git a/src/parser.cpp b/src/parser.cpp index dd9d6f036..247255ce8 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3561,6 +3561,7 @@ enum FieldPrefixKind : i32 { FieldPrefix_auto_cast, FieldPrefix_any_int, FieldPrefix_subtype, // does not imply `using` semantics + FieldPrefix_by_ptr, }; struct ParseFieldPrefixMapping { @@ -3578,6 +3579,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = { {str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const}, {str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int}, {str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype}, + {str_lit("by_ptr"), Token_Hash, FieldPrefix_by_ptr, FieldFlag_by_ptr}, }; diff --git a/src/parser.hpp b/src/parser.hpp index 8719b5e56..3126e0a02 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -302,13 +302,14 @@ enum FieldFlag : u32 { FieldFlag_const = 1<<5, FieldFlag_any_int = 1<<6, FieldFlag_subtype = 1<<7, + FieldFlag_by_ptr = 1<<8, // Internal use by the parser only FieldFlag_Tags = 1<<10, FieldFlag_Results = 1<<16, // Parameter List Restrictions - FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int, + FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr, FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags, }; -- cgit v1.2.3 From 5f2b220a850c6812bb7b5e4d778be37d8dc8962b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 25 Jul 2022 12:12:25 +0100 Subject: Fix minor issue with a lack of a trailing comma --- src/parser.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 247255ce8..b62ec7a74 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3894,7 +3894,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi while (f->curr_token.kind != follow && - f->curr_token.kind != Token_EOF) { + f->curr_token.kind != Token_EOF && + f->curr_token.kind != Token_Semicolon) { CommentGroup *docs = f->lead_comment; u32 set_flags = parse_field_prefixes(f); Token tag = {}; @@ -3922,7 +3923,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi default_value = parse_expr(f, false); if (!allow_default_parameters) { syntax_error(f->curr_token, "Default parameters are only allowed for procedures"); - default_value = nullptr; + default_value = nullptr; } } -- cgit v1.2.3