diff --git a/core/crypto/_blake2/_blake2.odin b/core/crypto/_blake2/blake2.odin similarity index 100% rename from core/crypto/_blake2/_blake2.odin rename to core/crypto/_blake2/blake2.odin diff --git a/core/crypto/_sha3/_sha3.odin b/core/crypto/_sha3/sha3.odin similarity index 100% rename from core/crypto/_sha3/_sha3.odin rename to core/crypto/_sha3/sha3.odin diff --git a/core/crypto/_tiger/_tiger.odin b/core/crypto/_tiger/tiger.odin similarity index 100% rename from core/crypto/_tiger/_tiger.odin rename to core/crypto/_tiger/tiger.odin diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin index d007e16d7..bc381efee 100644 --- a/core/encoding/json/parser.odin +++ b/core/encoding/json/parser.odin @@ -343,7 +343,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a i += 1 continue } - r, w := utf8.decode_rune_in_string(s) + r, w := utf8.decode_rune_in_string(s[i:]) if r == utf8.RUNE_ERROR && w == 1 { break } diff --git a/core/io/util.odin b/core/io/util.odin index c77d0be9d..c24eb99c5 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -39,12 +39,12 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> ( } write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { - buf: [32]byte + buf: [39]byte s := strconv.append_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) return write_string(w, s, n_written) } write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { - buf: [32]byte + buf: [40]byte s := strconv.append_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) return write_string(w, s, n_written) } diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 7e13bb929..d0e550743 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -413,6 +413,48 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string { } +cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool { + x := ([^]byte)(lhs) + y := ([^]byte)(rhs) + if x == y { + return true + } + if (x == nil) ~ (y == nil) { + return false + } + xn := cstring_len(lhs) + yn := cstring_len(rhs) + if xn != yn { + return false + } + return #force_inline memory_equal(x, y, xn) +} + +cstring_cmp :: proc "contextless" (lhs, rhs: cstring) -> int { + x := ([^]byte)(lhs) + y := ([^]byte)(rhs) + if x == y { + return 0 + } + if (x == nil) ~ (y == nil) { + return -1 if x == nil else +1 + } + xn := cstring_len(lhs) + yn := cstring_len(rhs) + ret := memory_compare(x, y, min(xn, yn)) + if ret == 0 && xn != yn { + return -1 if xn < yn else +1 + } + return ret +} + +cstring_ne :: #force_inline proc "contextless" (a, b: cstring) -> bool { return !cstring_eq(a, b) } +cstring_lt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) < 0 } +cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) > 0 } +cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 } +cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 } + + complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b) } complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b) } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 2e65c5750..851665cb2 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2120,7 +2120,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As return false; } Type *t = o.type; - if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) { + if (t == nullptr || t == t_invalid || is_type_asm_proc(t) || is_type_polymorphic(t)) { error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name)); return false; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 700412a86..968b6ec1e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2462,8 +2462,9 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t add_package_dependency(c, "runtime", "quaternion256_ne"); break; case Basic_cstring: - add_package_dependency(c, "runtime", "cstring_to_string"); - /*fallthrough*/ + add_package_dependency(c, "runtime", "cstring_eq"); + add_package_dependency(c, "runtime", "cstring_ne"); + break; case Basic_string: add_package_dependency(c, "runtime", "string_eq"); add_package_dependency(c, "runtime", "string_ne"); @@ -2621,7 +2622,16 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper if (!is_type_untyped(x->type)) size = gb_max(size, type_size_of(x->type)); if (!is_type_untyped(y->type)) size = gb_max(size, type_size_of(y->type)); - if (is_type_string(x->type) || is_type_string(y->type)) { + if (is_type_cstring(x->type) && is_type_cstring(y->type)) { + switch (op) { + case Token_CmpEq: add_package_dependency(c, "runtime", "cstring_eq"); break; + case Token_NotEq: add_package_dependency(c, "runtime", "cstring_ne"); break; + case Token_Lt: add_package_dependency(c, "runtime", "cstring_lt"); break; + case Token_Gt: add_package_dependency(c, "runtime", "cstring_gt"); break; + case Token_LtEq: add_package_dependency(c, "runtime", "cstring_le"); break; + case Token_GtEq: add_package_dependency(c, "runtime", "cstring_gt"); break; + } + } else if (is_type_string(x->type) || is_type_string(y->type)) { switch (op) { case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break; case Token_NotEq: add_package_dependency(c, "runtime", "string_ne"); break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index cb1aa86ab..3f1b9611c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1893,7 +1893,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { - error(e->token, "@(thread_local) is not supported for this target platform"); + // error(e->token, "@(thread_local) is not supported for this target platform"); } diff --git a/src/check_type.cpp b/src/check_type.cpp index 5e7e76d66..a43c296a6 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -613,6 +613,9 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * scope_reserve(ctx->scope, min_field_count); + rw_mutex_lock(&struct_type->Struct.fields_mutex); + defer (rw_mutex_unlock(&struct_type->Struct.fields_mutex)); + if (st->is_raw_union && min_field_count > 1) { struct_type->Struct.is_raw_union = true; context = str_lit("struct #raw_union"); diff --git a/src/checker.cpp b/src/checker.cpp index 984a00a90..7182b6559 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2029,6 +2029,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { break; + case Type_Generic: + break; + default: GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind); break; diff --git a/src/error.cpp b/src/error.cpp index 6a039006b..e63682829 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -356,7 +356,9 @@ gb_internal void error_out_coloured(char const *str, TerminalStyle style, Termin gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); - + if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + gb_exit(1); + } mutex_lock(&global_error_collector.mutex); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { @@ -372,11 +374,10 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va error_out_va(fmt, va); error_out("\n"); show_error_on_line(pos, end); + } else { + global_error_collector.count.fetch_sub(1); } mutex_unlock(&global_error_collector.mutex); - if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { - gb_exit(1); - } } gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { @@ -410,8 +411,11 @@ gb_internal void error_line_va(char const *fmt, va_list va) { } gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { - mutex_lock(&global_error_collector.mutex); global_error_collector.count.fetch_add(1); + if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) { + gb_exit(1); + } + mutex_lock(&global_error_collector.mutex); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -425,15 +429,15 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li error_out_va(fmt, va); } mutex_unlock(&global_error_collector.mutex); - if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) { - gb_exit(1); - } } gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { + global_error_collector.count.fetch_add(1); + if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + gb_exit(1); + } mutex_lock(&global_error_collector.mutex); - global_error_collector.count++; // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { global_error_collector.prev = pos; @@ -447,16 +451,14 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * error_out_va(fmt, va); error_out("\n"); } - mutex_unlock(&global_error_collector.mutex); - if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { - gb_exit(1); - } } gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); - + if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + gb_exit(1); + } mutex_lock(&global_error_collector.mutex); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { @@ -474,9 +476,6 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, show_error_on_line(pos, end); } mutex_unlock(&global_error_collector.mutex); - if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { - gb_exit(1); - } } @@ -578,7 +577,3 @@ gb_internal void compiler_error(char const *fmt, ...) { GB_DEBUG_TRAP(); gb_exit(1); } - - - - diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index e8569eba9..d6e8843fa 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -2414,7 +2414,28 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left } if (is_type_string(a)) { - if (is_type_cstring(a)) { + if (is_type_cstring(a) && is_type_cstring(b)) { + left = lb_emit_conv(p, left, t_cstring); + right = lb_emit_conv(p, right, t_cstring); + char const *runtime_procedure = nullptr; + switch (op_kind) { + case Token_CmpEq: runtime_procedure = "cstring_eq"; break; + case Token_NotEq: runtime_procedure = "cstring_ne"; break; + case Token_Lt: runtime_procedure = "cstring_lt"; break; + case Token_Gt: runtime_procedure = "cstring_gt"; break; + case Token_LtEq: runtime_procedure = "cstring_le"; break; + case Token_GtEq: runtime_procedure = "cstring_gt"; break; + } + GB_ASSERT(runtime_procedure != nullptr); + + auto args = array_make(permanent_allocator(), 2); + args[0] = left; + args[1] = right; + return lb_emit_runtime_call(p, runtime_procedure, args); + } + + + if (is_type_cstring(a) ^ is_type_cstring(b)) { left = lb_emit_conv(p, left, t_string); right = lb_emit_conv(p, right, t_string); } diff --git a/src/parser.cpp b/src/parser.cpp index cdf3c239c..a4bf949b9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1361,6 +1361,22 @@ gb_internal Token peek_token(AstFile *f) { return {}; } +gb_internal Token peek_token_n(AstFile *f, isize n) { + Token found = {}; + for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) { + Token tok = f->tokens[i]; + if (tok.kind == Token_Comment) { + continue; + } + found = tok; + if (n-- == 0) { + return found; + } + } + return {}; +} + + gb_internal bool skip_possible_newline(AstFile *f) { if (token_is_newline(f->curr_token)) { advance_token(f); @@ -2206,6 +2222,10 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { Ast *tag = ast_basic_directive(f, token, name); Ast *original_expr = parse_expr(f, lhs); Ast *expr = unparen_expr(original_expr); + if (expr == nullptr) { + syntax_error(name, "Expected a compound literal after #%.*s", LIT(name.string)); + return ast_bad_expr(f, token, name); + } switch (expr->kind) { case Ast_ArrayType: syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types"); @@ -3419,6 +3439,18 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) { case Token_Colon: expect_token_after(f, Token_Colon, "identifier list"); if ((flags&StmtAllowFlag_Label) && lhs.count == 1) { + bool is_partial = false; + Token partial_token = {}; + if (f->curr_token.kind == Token_Hash) { + // NOTE(bill): This is purely for error messages + Token name = peek_token_n(f, 0); + if (name.kind == Token_Ident && name.string == "partial" && + peek_token_n(f, 1).kind == Token_switch) { + partial_token = expect_token(f, Token_Hash); + expect_token(f, Token_Ident); + is_partial = true; + } + } switch (f->curr_token.kind) { case Token_OpenBrace: // block statement case Token_if: @@ -3440,6 +3472,19 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) { break; } #undef _SET_LABEL + + if (is_partial) { + switch (stmt->kind) { + case Ast_SwitchStmt: + stmt->SwitchStmt.partial = true; + break; + case Ast_TypeSwitchStmt: + stmt->TypeSwitchStmt.partial = true; + break; + } + syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string)); + } + return stmt; } break; } @@ -3984,7 +4029,9 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl if (f->curr_token.kind != Token_Eq) { type = parse_var_type(f, allow_ellipsis, allow_typeid_token); Ast *tt = unparen_expr(type); - if (is_signature && !any_polymorphic_names && tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) { + if (is_signature && !any_polymorphic_names && + tt != nullptr && + tt->kind == Ast_TypeidType && tt->TypeidType.specialization != nullptr) { syntax_error(type, "Specialization of typeid is not allowed without polymorphic names"); } } @@ -5941,6 +5988,16 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe } } + { + String name = file->fullpath; + name = remove_directory_from_path(name); + name = remove_extension_from_path(name); + + if (string_starts_with(name, str_lit("_"))) { + syntax_error(pos, "Files cannot start with '_', got '%.*s'", LIT(file->fullpath)); + } + } + if (build_context.command_kind == Command_test) { String name = file->fullpath; name = remove_extension_from_path(name); @@ -5951,6 +6008,7 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe } } + if (parse_file(p, file)) { MUTEX_GUARD_BLOCK(&pkg->files_mutex) { array_add(&pkg->files, file); diff --git a/src/types.cpp b/src/types.cpp index 22deca1dc..f3062365f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -143,7 +143,8 @@ struct TypeStruct { Type * soa_elem; i32 soa_count; StructSoaKind soa_kind; - BlockingMutex mutex; // for settings offsets + RwMutex fields_mutex; + BlockingMutex offset_mutex; // for settings offsets bool is_polymorphic; bool are_offsets_set : 1; @@ -2645,10 +2646,14 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple return are_types_identical(x->Slice.elem, y->Slice.elem); case Type_BitSet: - return are_types_identical(x->BitSet.elem, y->BitSet.elem) && - are_types_identical(x->BitSet.underlying, y->BitSet.underlying) && - x->BitSet.lower == y->BitSet.lower && - x->BitSet.upper == y->BitSet.upper; + if (are_types_identical(x->BitSet.elem, y->BitSet.elem) && + are_types_identical(x->BitSet.underlying, y->BitSet.underlying)) { + if (is_type_enum(x->BitSet.elem)) { + return true; + } + return x->BitSet.lower == y->BitSet.lower && x->BitSet.upper == y->BitSet.upper; + } + return false; case Type_Enum: @@ -2951,7 +2956,11 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { gbAllocator a = permanent_allocator(); isize max_count = 0; switch (type->kind) { - case Type_Struct: max_count = type->Struct.fields.count; break; + case Type_Struct: + rw_mutex_shared_lock(&type->Struct.fields_mutex); + max_count = type->Struct.fields.count; + rw_mutex_shared_unlock(&type->Struct.fields_mutex); + break; case Type_Tuple: max_count = type->Tuple.variables.count; break; } @@ -2960,7 +2969,9 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { } switch (type->kind) { - case Type_Struct: + case Type_Struct: { + rw_mutex_shared_lock(&type->Struct.fields_mutex); + defer (rw_mutex_shared_unlock(&type->Struct.fields_mutex)); for (isize i = 0; i < max_count; i++) { Entity *f = type->Struct.fields[i]; if (f->kind == Entity_Variable) { @@ -2971,7 +2982,8 @@ gb_internal Selection lookup_field_from_index(Type *type, i64 index) { } } } - break; + } break; + case Type_Tuple: for (isize i = 0; i < max_count; i++) { Entity *f = type->Tuple.variables[i]; @@ -3024,7 +3036,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } if (type->kind == Type_Struct) { - for_array(i, type->Struct.fields) { + rw_mutex_shared_lock(&type->Struct.fields_mutex); + isize field_count = type->Struct.fields.count; + rw_mutex_shared_unlock(&type->Struct.fields_mutex); + if (field_count != 0) for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->flags&EntityFlag_Using) { sel = lookup_field_with_selection(f->type, field_name, is_type, sel, allow_blank_ident); @@ -3052,7 +3067,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } if (type->kind == Type_Struct) { + rw_mutex_shared_lock(&type->Struct.fields_mutex); Scope *s = type->Struct.scope; + rw_mutex_shared_unlock(&type->Struct.fields_mutex); if (s != nullptr) { Entity *found = scope_lookup_current(s, field_name); if (found != nullptr && found->kind != Entity_Variable) { @@ -3100,7 +3117,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name } } - for_array(i, type->Struct.fields) { + rw_mutex_shared_lock(&type->Struct.fields_mutex); + isize field_count = type->Struct.fields.count; + rw_mutex_shared_unlock(&type->Struct.fields_mutex); + if (field_count != 0) for_array(i, type->Struct.fields) { Entity *f = type->Struct.fields[i]; if (f->kind != Entity_Variable || (f->flags & EntityFlag_Field) == 0) { continue; @@ -3680,7 +3700,7 @@ gb_internal i64 *type_set_offsets_of(Slice const &fields, bool is_pack gb_internal bool type_set_offsets(Type *t) { t = base_type(t); if (t->kind == Type_Struct) { - MUTEX_GUARD(&t->Struct.mutex); + MUTEX_GUARD(&t->Struct.offset_mutex); if (!t->Struct.are_offsets_set) { t->Struct.are_offsets_being_processed = true; t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union); diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin index 937d1c738..23361d694 100644 --- a/tests/core/encoding/json/test_core_json.odin +++ b/tests/core/encoding/json/test_core_json.odin @@ -33,6 +33,7 @@ main :: proc() { marshal_json(&t) unmarshal_json(&t) surrogate(&t) + utf8_string_of_multibyte_characters(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) if TEST_fail > 0 { @@ -359,3 +360,10 @@ surrogate :: proc(t: ^testing.T) { expect(t, uerr == nil, fmt.tprintf("Expected `json.unmarshal(%q)` to return a nil error, got %v", string(out), uerr)) expect(t, back == input, fmt.tprintf("Expected `json.unmarshal(%q)` to return %q, got %v", string(out), input, uerr)) } + +@test +utf8_string_of_multibyte_characters :: proc(t: ^testing.T) { + _, err := json.parse_string(`"🐛✅"`) + msg := fmt.tprintf("Expected `json.parse` to return nil, got %v", err) + expect(t, err == nil, msg) +}