From 8a2a70a3c2a7ac14687d100325e12b8d3d02256e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 17 Feb 2023 13:00:37 +0000 Subject: Fix overriding procedure information for literals --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 58372f7a3..93db559f8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5418,7 +5418,9 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { data->score = score; data->result_type = final_proc_type->Proc.results; data->gen_entity = gen_entity; - add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); + if (!are_types_identical(final_proc_type, ce->proc->tav.type)) { + add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); + } } return err; -- cgit v1.2.3 From 20eacc4a8493f0d0088ceebbcc1490207c48b5ed Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Feb 2023 12:10:28 +0000 Subject: Fix issue that conflicts with constant parapoly procedures and `deferred_*` procedures --- src/check_decl.cpp | 13 ------------- src/check_expr.cpp | 5 ++--- 2 files changed, 2 insertions(+), 16 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1adb1e2ed..63e6514e0 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1532,19 +1532,6 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de break; } } - - // Add constant data to the identifier directly - for (Entity *e : params->variables) { - if (e->kind == Entity_Constant) { - Ast *ident = e->identifier.load(); - if (ident) { - add_entity(ctx, e->scope, ident, e); - ident->tav.mode = Addressing_Constant; - ident->tav.value = e->Constant.value; - ident->tav.type = e->type; - } - } - } } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 93db559f8..356f0aaa3 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5418,9 +5418,8 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { data->score = score; data->result_type = final_proc_type->Proc.results; data->gen_entity = gen_entity; - if (!are_types_identical(final_proc_type, ce->proc->tav.type)) { - add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); - } + + add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); } return err; -- cgit v1.2.3 From c386c72d10540a4a9af89755df7aa3a35a079b64 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Feb 2023 12:11:57 +0000 Subject: Check for procedure literals in `$` parameters --- src/check_expr.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 356f0aaa3..2ed77535c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5419,7 +5419,17 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { data->result_type = final_proc_type->Proc.results; data->gen_entity = gen_entity; - add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); + + Ast *proc_lit = nullptr; + if (ce->proc->tav.value.kind == ExactValue_Procedure) { + Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure); + if (vp && vp->kind == Ast_ProcLit) { + proc_lit = vp; + } + } + if (proc_lit == nullptr) { + add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {}); + } } return err; -- cgit v1.2.3 From a2f02b8b3218dc83a2a2783ceb79cc57abacc7bd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Feb 2023 16:31:22 +0000 Subject: Fix bug with for in statements and pointer intervals --- src/check_expr.cpp | 20 ++++++++++++++------ src/check_stmt.cpp | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2ed77535c..8a3bb2c1b 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7040,7 +7040,7 @@ gb_internal bool ternary_compare_types(Type *x, Type *y) { } -gb_internal bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) { +gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) { if (!is_ast_range(node)) { return false; } @@ -7089,9 +7089,17 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand * } Type *type = x->type; - if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { - error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - return false; + + if (is_for_loop) { + if (!is_type_integer(type) && !is_type_float(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical types are allowed within interval expressions"); + return false; + } + } else { + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + return false; + } } if (x->mode == Addressing_Constant && @@ -8104,7 +8112,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * Operand x = {}; Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr); + bool ok = check_range(c, fv->field, false, &x, &y, nullptr); if (!ok) { continue; } @@ -8320,7 +8328,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * Operand x = {}; Operand y = {}; - bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type); + bool ok = check_range(c, fv->field, false, &x, &y, nullptr, index_type); if (!ok) { continue; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3039995b9..f300f45c7 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -725,7 +725,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod Operand x = {}; Operand y = {}; - bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + bool ok = check_range(ctx, expr, true, &x, &y, &inline_for_depth); if (!ok) { goto skip_expr; } @@ -1439,7 +1439,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Operand x = {}; Operand y = {}; - bool ok = check_range(ctx, expr, &x, &y, nullptr); + bool ok = check_range(ctx, expr, true, &x, &y, nullptr); if (!ok) { goto skip_expr_range_stmt; } -- cgit v1.2.3 From b6a5c5f5d2a466e867f59c463987ba8ee28664a5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Feb 2023 17:24:22 +0000 Subject: Improve some error messages when casting a constant value which needs to be truncated/rounded --- src/check_expr.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8a3bb2c1b..c4a29b04a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2915,17 +2915,32 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { bool can_convert = check_cast_internal(c, x, type); if (!can_convert) { - gbString expr_str = expr_to_string(x->expr); - gbString to_type = type_to_string(type); - gbString from_type = type_to_string(x->type); - error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type); - gb_string_free(from_type); - gb_string_free(to_type); - gb_string_free(expr_str); + TEMPORARY_ALLOCATOR_GUARD(); + gbString expr_str = expr_to_string(x->expr, temporary_allocator()); + gbString to_type = type_to_string(type, temporary_allocator()); + gbString from_type = type_to_string(x->type, temporary_allocator()); + x->mode = Addressing_Invalid; + + begin_error_block(); + error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type); + if (is_const_expr) { + gbString val_str = exact_value_to_string(x->value); + if (is_type_float(x->type) && is_type_integer(type)) { + error_line("\t%s cannot be expressed without truncation/rounding as the type '%s'\n", val_str, to_type); + + // NOTE(bill): keep the mode and modify the type to minimize errors further on + x->mode = Addressing_Constant; + x->type = type; + } else { + error_line("\t%s cannot be expressed as the type '%s'\n", val_str, to_type); + } + gb_string_free(val_str); + } check_cast_error_suggestion(c, x, type); - x->mode = Addressing_Invalid; + end_error_block(); + return; } -- cgit v1.2.3 From 59a601f2cf1f7575662e4e7f1c3cabd079280d0a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Feb 2023 23:08:02 +0000 Subject: Improve error messages when trying to access a non-existent field on a type --- src/check_expr.cpp | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c4a29b04a..30e2c9ad8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4745,20 +4745,29 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod gbString op_str = expr_to_string(op_expr); gbString type_str = type_to_string_shorthand(operand->type); gbString sel_str = expr_to_string(selector); - error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); - - if (operand->type != nullptr && selector->kind == Ast_Ident) { - String const &name = selector->Ident.token.string; - Type *bt = base_type(operand->type); - if (operand->type->kind == Type_Named && - operand->type->Named.type_name && - operand->type->Named.type_name->kind == Entity_TypeName && - operand->type->Named.type_name->TypeName.objc_metadata) { - check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type); - } else if (bt->kind == Type_Struct) { - check_did_you_mean_type(name, bt->Struct.fields); - } else if (bt->kind == Type_Enum) { - check_did_you_mean_type(name, bt->Enum.fields); + + if (operand->mode == Addressing_Type) { + if (is_type_polymorphic(operand->type, true)) { + error(op_expr, "Type '%s' has no field nor polymorphic parameter '%s'", op_str, sel_str); + } else { + error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str); + } + } else { + error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str); + + if (operand->type != nullptr && selector->kind == Ast_Ident) { + String const &name = selector->Ident.token.string; + Type *bt = base_type(operand->type); + if (operand->type->kind == Type_Named && + operand->type->Named.type_name && + operand->type->Named.type_name->kind == Entity_TypeName && + operand->type->Named.type_name->TypeName.objc_metadata) { + check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type); + } else if (bt->kind == Type_Struct) { + check_did_you_mean_type(name, bt->Struct.fields); + } else if (bt->kind == Type_Enum) { + check_did_you_mean_type(name, bt->Enum.fields); + } } } -- cgit v1.2.3 From b9f7b2fdfaa41e9b4e48362d3bc1b839b62dd914 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Feb 2023 23:16:25 +0000 Subject: Improve error message for typed constants that cannot be represented by a type --- src/check_expr.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 30e2c9ad8..c0920bde8 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2116,16 +2116,21 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ o->mode = Addressing_Invalid; ); + ERROR_BLOCK(); + + if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { - ERROR_BLOCK(); - error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s); + if (are_types_identical(o->type, type)) { + error(o->expr, "Numeric value '%s' cannot be represented by '%s', got %s", a, c, s); + } else { + error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s); + } check_assignment_error_suggestion(ctx, o, type); } } else { - ERROR_BLOCK(); error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); check_assignment_error_suggestion(ctx, o, type); } @@ -2597,10 +2602,12 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod x->type = t_untyped_integer; } + x->expr = node; x->value = exact_value_shift(be->op.kind, x_val, y_val); + if (is_type_typed(x->type)) { - check_is_expressible(c, x, base_type(x->type)); + check_is_expressible(c, x, x->type); } return; } -- cgit v1.2.3 From f5d507a9b9d20941bd86ba8559f710f21f0c8ccd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 22 Feb 2023 11:30:08 +0000 Subject: Improve errors about conversions of constant integers --- src/check_decl.cpp | 2 +- src/check_expr.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/error.cpp | 33 ++++++++++++++++++++++------- 3 files changed, 82 insertions(+), 15 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 63e6514e0..7978aa0ef 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -578,7 +578,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr if (operand.mode == Addressing_Invalid || base_type(operand.type) == t_invalid) { gbString str = expr_to_string(init); - error(e->token, "Invalid declaration type '%s'", str); + error(init, "Invalid declaration value '%s'", str); gb_string_free(str); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index c0920bde8..d7e7b29a4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2019,6 +2019,47 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i } +gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) { + if (is_type_integer(type) && o->value.kind == ExactValue_Integer) { + gbString b = type_to_string(type); + + i64 sz = type_size_of(type); + BigInt *bi = &o->value.value_integer; + if (is_type_unsigned(type)) { + if (big_int_is_neg(bi)) { + error_line("\tA negative value cannot be represented by the unsigned integer type '%s'\n", b); + } else { + BigInt one = big_int_make_u64(1); + BigInt max_size = big_int_make_u64(1); + BigInt bits = big_int_make_i64(8*sz); + big_int_shl_eq(&max_size, &bits); + big_int_sub_eq(&max_size, &one); + String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } + } else { + BigInt zero = big_int_make_u64(0); + BigInt one = big_int_make_u64(1); + BigInt max_size = big_int_make_u64(1); + BigInt bits = big_int_make_i64(8*sz - 1); + big_int_shl_eq(&max_size, &bits); + if (big_int_is_neg(bi)) { + big_int_neg(&max_size, &max_size); + String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } else { + big_int_sub_eq(&max_size, &one); + String max_size_str = big_int_to_string(temporary_allocator(), &max_size); + error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str)); + } + } + + gb_string_free(b); + + return true; + } + return false; +} gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); @@ -2050,6 +2091,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\t whereas slices in general are assumed to be mutable.\n"); } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) { error_line("\tSuggestion: the expression may be casted to %s\n", b); + } else if (check_integer_exceed_suggestion(c, o, type)) { + return; } } @@ -2089,8 +2132,8 @@ gb_internal void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type } } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { error_line("\tSuggestion: a string may be transmuted to %s\n", b); - } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) { - error_line("\tSuggestion: the expression may be casted to %s\n", b); + } else if (check_integer_exceed_suggestion(c, o, type)) { + return; } } @@ -2124,10 +2167,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ error(o->expr, "'%s' truncated to '%s', got %s", a, b, s); } else { if (are_types_identical(o->type, type)) { - error(o->expr, "Numeric value '%s' cannot be represented by '%s', got %s", a, c, s); + error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b); } else { - error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s); + error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c); } + check_assignment_error_suggestion(ctx, o, type); } } else { @@ -2934,15 +2978,21 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { if (is_const_expr) { gbString val_str = exact_value_to_string(x->value); if (is_type_float(x->type) && is_type_integer(type)) { - error_line("\t%s cannot be expressed without truncation/rounding as the type '%s'\n", val_str, to_type); + error_line("\t%s cannot be represented without truncation/rounding as the type '%s'\n", val_str, to_type); // NOTE(bill): keep the mode and modify the type to minimize errors further on x->mode = Addressing_Constant; x->type = type; } else { - error_line("\t%s cannot be expressed as the type '%s'\n", val_str, to_type); + error_line("\t'%s' cannot be represented as the type '%s'\n", val_str, to_type); + if (is_type_numeric(type)) { + // NOTE(bill): keep the mode and modify the type to minimize errors further on + x->mode = Addressing_Constant; + x->type = type; + } } gb_string_free(val_str); + } check_cast_error_suggestion(c, x, type); diff --git a/src/error.cpp b/src/error.cpp index a0bb4ad5b..33157948f 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -97,15 +97,37 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { +// NOTE: defined in build_settings.cpp +gb_internal bool global_warnings_as_errors(void); +gb_internal bool global_ignore_warnings(void); +gb_internal bool show_error_line(void); +gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); + +gb_internal void warning(Token const &token, char const *fmt, ...); +gb_internal void error(Token const &token, char const *fmt, ...); +gb_internal void error(TokenPos pos, char const *fmt, ...); +gb_internal void error_line(char const *fmt, ...); +gb_internal void syntax_error(Token const &token, char const *fmt, ...); +gb_internal void syntax_error(TokenPos pos, char const *fmt, ...); +gb_internal void syntax_warning(Token const &token, char const *fmt, ...); +gb_internal void compiler_error(char const *fmt, ...); + gb_internal void begin_error_block(void) { mutex_lock(&global_error_collector.block_mutex); global_error_collector.in_block.store(true); } gb_internal void end_error_block(void) { - if (global_error_collector.error_buffer.count > 0) { - isize n = global_error_collector.error_buffer.count; - u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1); + isize n = global_error_collector.error_buffer.count; + if (n > 0) { + u8 *text = global_error_collector.error_buffer.data; + if (show_error_line() && n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) { + // add an extra new line as padding when the error line is being shown + error_line("\n"); + } + + n = global_error_collector.error_buffer.count; + text = gb_alloc_array(permanent_allocator(), u8, n+1); gb_memmove(text, global_error_collector.error_buffer.data, n); text[n] = 0; String s = {text, n}; @@ -152,11 +174,6 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) { gb_global ErrorOutProc *error_out_va = default_error_out_va; -// NOTE: defined in build_settings.cpp -gb_internal bool global_warnings_as_errors(void); -gb_internal bool global_ignore_warnings(void); -gb_internal bool show_error_line(void); -gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); gb_internal void error_out(char const *fmt, ...) { va_list va; -- cgit v1.2.3 From 090e30f07b10a79edf38736cc466a4f223167971 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 22 Feb 2023 11:48:10 +0000 Subject: Make `-verbose-errors` the default; `-terse-errors` to disable it --- src/build_settings.cpp | 4 ++-- src/check_expr.cpp | 6 +++++- src/error.cpp | 29 ++++++++++++++++++++++++----- src/exact_value.cpp | 4 +--- src/main.cpp | 9 ++++++++- 5 files changed, 40 insertions(+), 12 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/build_settings.cpp b/src/build_settings.cpp index a3873be82..ff372f418 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -288,7 +288,7 @@ struct BuildContext { bool ignore_warnings; bool warnings_as_errors; - bool show_error_line; + bool hide_error_line; bool ignore_lazy; bool ignore_llvm_build; @@ -1033,7 +1033,7 @@ gb_internal String get_fullpath_core(gbAllocator a, String path) { } gb_internal bool show_error_line(void) { - return build_context.show_error_line; + return !build_context.hide_error_line; } gb_internal bool has_asm_extension(String const &path) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d7e7b29a4..f42483c86 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1679,9 +1679,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { case Token_Not: if (!is_type_boolean(type)) { + ERROR_BLOCK(); str = expr_to_string(o->expr); - error(op, "Operator '%.*s' is only allowed on boolean expression", LIT(op.string)); + error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string)); gb_string_free(str); + if (is_type_integer(type)) { + error_line("\tSuggestion: Did you mean to use the bitwise not operator '~'?\n"); + } } break; diff --git a/src/error.cpp b/src/error.cpp index 33157948f..cb2aa4836 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -121,7 +121,26 @@ gb_internal void end_error_block(void) { isize n = global_error_collector.error_buffer.count; if (n > 0) { u8 *text = global_error_collector.error_buffer.data; - if (show_error_line() && n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) { + + bool add_extra_newline = false; + + if (show_error_line()) { + if (n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) { + add_extra_newline = true; + } + } else { + isize newline_count = 0; + for (isize i = 0; i < n; i++) { + if (text[i] == '\n') { + newline_count += 1; + } + } + if (newline_count > 1) { + add_extra_newline = true; + } + } + + if (add_extra_newline) { // add an extra new line as padding when the error line is being shown error_line("\n"); } @@ -198,12 +217,12 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { // TODO(bill): This assumes ASCII enum { - MAX_LINE_LENGTH = 76, + MAX_LINE_LENGTH = 80, MAX_TAB_WIDTH = 8, - ELLIPSIS_PADDING = 8 + ELLIPSIS_PADDING = 8 // `... ...` }; - error_out("\n\t"); + error_out("\t"); if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) { i32 const half_width = MAX_LINE_LENGTH/2; i32 left = cast(i32)(offset); @@ -244,7 +263,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { } } - error_out("\n\n"); + error_out("\n"); return true; } return false; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fc4ae2155..1ab50800d 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -578,9 +578,7 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3 } } -failure: - GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op])); - +failure:; ExactValue error_value = {}; return error_value; } diff --git a/src/main.cpp b/src/main.cpp index 480930d4a..02996065a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -659,6 +659,7 @@ enum BuildFlagKind { BuildFlag_IgnoreWarnings, BuildFlag_WarningsAsErrors, + BuildFlag_TerseErrors, BuildFlag_VerboseErrors, BuildFlag_ErrorPosStyle, @@ -832,6 +833,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_TerseErrors, str_lit("terse-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all); @@ -1462,8 +1464,13 @@ gb_internal bool parse_build_flags(Array args) { } break; } + + case BuildFlag_TerseErrors: + build_context.hide_error_line = true; + break; case BuildFlag_VerboseErrors: - build_context.show_error_line = true; + gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n"); + build_context.hide_error_line = false; break; case BuildFlag_ErrorPosStyle: -- cgit v1.2.3 From 6a6d7701f9892a3468c74f0c7d1e70e04f529824 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 22 Feb 2023 21:50:49 +0000 Subject: Improve error bounds for `check_comparison` --- src/check_expr.cpp | 6 +++--- src/check_stmt.cpp | 8 ++++---- src/error.cpp | 8 +++++++- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f42483c86..00d394966 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2397,7 +2397,7 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t } -gb_internal void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { +gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Operand *y, TokenKind op) { if (x->mode == Addressing_Type && y->mode == Addressing_Type) { bool comp = are_types_identical(x->type, y->type); switch (op) { @@ -2485,7 +2485,7 @@ gb_internal void check_comparison(CheckerContext *c, Operand *x, Operand *y, Tok } if (err_str != nullptr) { - error(x->expr, "Cannot compare expression, %s", err_str); + error(node, "Cannot compare expression, %s", err_str); x->type = t_untyped_bool; } else { if (x->mode == Addressing_Constant && @@ -3498,7 +3498,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ if (token_is_comparison(op.kind)) { - check_comparison(c, x, y, op.kind); + check_comparison(c, node, x, y, op.kind); return; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index f300f45c7..7e3948336 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -978,19 +978,19 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags Operand a = lhs; Operand b = rhs; - check_comparison(ctx, &a, &x, Token_LtEq); + check_comparison(ctx, expr, &a, &x, Token_LtEq); if (a.mode == Addressing_Invalid) { continue; } - check_comparison(ctx, &b, &x, upper_op); + check_comparison(ctx, expr, &b, &x, upper_op); if (b.mode == Addressing_Invalid) { continue; } Operand a1 = lhs; Operand b1 = rhs; - check_comparison(ctx, &a1, &b1, Token_LtEq); + check_comparison(ctx, expr, &a1, &b1, Token_LtEq); add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs); @@ -1029,7 +1029,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags // NOTE(bill): the ordering here matters Operand z = y; - check_comparison(ctx, &z, &x, Token_CmpEq); + check_comparison(ctx, expr, &z, &x, Token_CmpEq); if (z.mode == Addressing_Invalid) { continue; } diff --git a/src/error.cpp b/src/error.cpp index 750cd147f..9279ed4d4 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -264,7 +264,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { ELLIPSIS_PADDING = 8 // `... ...` }; - error_out("\t"); + error_out("\n\t"); terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); @@ -345,6 +345,9 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va } else if (global_error_collector.prev != pos) { global_error_collector.prev = pos; error_out_pos(pos); + if (has_ansi_terminal_colours()) { + error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); + } error_out_va(fmt, va); error_out("\n"); show_error_on_line(pos, end); @@ -395,6 +398,9 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li } else if (global_error_collector.prev != pos) { global_error_collector.prev = pos; error_out_pos(pos); + if (has_ansi_terminal_colours()) { + error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); + } error_out_va(fmt, va); } mutex_unlock(&global_error_collector.mutex); -- cgit v1.2.3 From a9182cfd8cf9dd8cec125221e39c0fcd7a3fd3e3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 26 Feb 2023 13:26:35 +0000 Subject: Allow compound literals to access fields through `using` --- src/check_expr.cpp | 205 +++++++++++++++++++++++++------------------ src/common.cpp | 5 ++ src/error.cpp | 4 +- src/llvm_backend_const.cpp | 77 +++++++++++++++- src/llvm_backend_expr.cpp | 20 ++++- src/llvm_backend_utility.cpp | 2 +- src/ptr_set.cpp | 2 +- src/types.cpp | 1 - 8 files changed, 219 insertions(+), 97 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 00d394966..b9588a798 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7890,6 +7890,124 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no return Expr_Expr; } + +gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice const &elems, Operand *o, Type *type, bool &is_constant) { + Type *bt = base_type(type); + + StringSet fields_visited = {}; + defer (string_set_destroy(&fields_visited)); + + StringMap fields_visited_through_raw_union = {}; + defer (string_map_destroy(&fields_visited_through_raw_union)); + + for (Ast *elem : elems) { + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + if (fv->field->kind != Ast_Ident) { + gbString expr_str = expr_to_string(fv->field); + error(elem, "Invalid field name '%s' in structure literal", expr_str); + gb_string_free(expr_str); + continue; + } + String name = fv->field->Ident.token.string; + + Selection sel = lookup_field(type, name, o->mode == Addressing_Type); + bool is_unknown = sel.entity == nullptr; + if (is_unknown) { + error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name)); + continue; + } + + Entity *field = bt->Struct.fields[sel.index[0]]; + add_entity_use(c, fv->field, field); + if (string_set_update(&fields_visited, name)) { + if (sel.index.count > 1) { + if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { + error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + } else { + error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string)); + } + } else { + error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string)); + } + continue; + } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) { + error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found)); + continue; + } + if (sel.indirect) { + error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name)); + continue; + } + + if (sel.index.count > 1) { + if (is_constant) { + Type *ft = type; + for (i32 index : sel.index) { + Type *bt = base_type(ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + is_constant = false; + break; + } + ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + ft = bt->Array.elem; + break; + default: + GB_PANIC("invalid type: %s", type_to_string(ft)); + break; + } + } + if (is_constant && + (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) { + is_constant = false; + } + } + + Type *nested_ft = bt; + for (i32 index : sel.index) { + Type *bt = base_type(nested_ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + for (Entity *re : bt->Struct.fields) { + string_map_set(&fields_visited_through_raw_union, re->token.string, sel.entity->token.string); + } + } + nested_ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + nested_ft = bt->Array.elem; + break; + default: + GB_PANIC("invalid type %s", type_to_string(nested_ft)); + break; + } + } + field = sel.entity; + } + + + Operand o = {}; + check_expr_or_type(c, &o, fv->value, field->type); + + if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { + is_constant = false; + } + if (is_constant) { + is_constant = check_is_operand_compound_lit_constant(c, &o); + } + + check_assignment(c, &o, field->type, str_lit("structure literal")); + } +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); @@ -7977,45 +8095,13 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count); gb_string_free(type_str); } else { - Ast *elem = cl->elems[0]; - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - break; - } - - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - break; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - break; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - - check_assignment(c, &o, field->type, str_lit("structure literal")); + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); } - } } break; } - isize field_count = t->Struct.fields.count; isize min_field_count = t->Struct.fields.count; for (isize i = min_field_count-1; i >= 0; i--) { @@ -8029,58 +8115,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (cl->elems[0]->kind == Ast_FieldValue) { - TEMPORARY_ALLOCATOR_GUARD(); - - bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count); - - for (Ast *elem : cl->elems) { - if (elem->kind != Ast_FieldValue) { - error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); - continue; - } - ast_node(fv, FieldValue, elem); - if (fv->field->kind != Ast_Ident) { - gbString expr_str = expr_to_string(fv->field); - error(elem, "Invalid field name '%s' in structure literal", expr_str); - gb_string_free(expr_str); - continue; - } - String name = fv->field->Ident.token.string; - - Selection sel = lookup_field(type, name, o->mode == Addressing_Type); - bool is_unknown = sel.entity == nullptr; - if (is_unknown) { - error(elem, "Unknown field '%.*s' in structure literal", LIT(name)); - continue; - } - - if (sel.index.count > 1) { - error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name)); - continue; - } - - Entity *field = t->Struct.fields[sel.index[0]]; - add_entity_use(c, fv->field, field); - - if (fields_visited[sel.index[0]]) { - error(elem, "Duplicate field '%.*s' in structure literal", LIT(name)); - continue; - } - - fields_visited[sel.index[0]] = true; - - Operand o = {}; - check_expr_or_type(c, &o, fv->value, field->type); - - if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) { - is_constant = false; - } - if (is_constant) { - is_constant = check_is_operand_compound_lit_constant(c, &o); - } - - check_assignment(c, &o, field->type, str_lit("structure literal")); - } + check_compound_literal_field_values(c, cl->elems, o, type, is_constant); } else { bool seen_field_value = false; diff --git a/src/common.cpp b/src/common.cpp index 859aa4a56..90632def3 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -53,6 +53,11 @@ struct TypeIsPointer { enum {value = true}; }; +template struct TypeIsPtrSizedInteger { enum {value = false}; }; +template <> struct TypeIsPtrSizedInteger { enum {value = true}; }; +template <> struct TypeIsPtrSizedInteger { enum {value = true}; }; + + #include "unicode.cpp" #include "array.cpp" #include "threading.cpp" diff --git a/src/error.cpp b/src/error.cpp index d20373be9..c684be1e7 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -423,7 +423,7 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); - show_error_on_line(pos, end); + // show_error_on_line(pos, end); } else if (pos.line == 0) { error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); @@ -451,7 +451,7 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); - show_error_on_line(pos, end); + // show_error_on_line(pos, end); } else if (pos.line == 0) { error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 72c2a0495..3da768cd1 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -386,6 +386,31 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi return value; } +gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, Ast *elem) { + GB_ASSERT(!sel.indirect); + for (i32 index : sel.index) { + Type *bt = base_type(ft); + switch (bt->kind) { + case Type_Struct: + if (bt->Struct.is_raw_union) { + return false; + } + ft = bt->Struct.fields[index]->type; + break; + case Type_Array: + ft = bt->Array.elem; + break; + default: + return false; + } + } + + + if (is_type_raw_union(ft) || is_type_typeid(ft)) { + return false; + } + return lb_is_elem_const(elem, ft); +} gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) { LLVMContextRef ctx = m->ctx; @@ -979,12 +1004,58 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo GB_ASSERT(tav.mode != Addressing_Invalid); Selection sel = lookup_field(type, name, false); + GB_ASSERT(!sel.indirect); + Entity *f = type->Struct.fields[sel.index[0]]; - i32 index = field_remapping[f->Variable.field_index]; if (elem_type_can_be_constant(f->type)) { - values[index] = lb_const_value(m, f->type, tav.value, allow_local).value; - visited[index] = true; + if (sel.index.count == 1) { + values[index] = lb_const_value(m, f->type, tav.value, allow_local).value; + visited[index] = true; + } else { + if (!visited[index]) { + values[index] = lb_const_value(m, f->type, {}, false).value; + visited[index] = true; + } + unsigned idx_list_len = cast(unsigned)sel.index.count-1; + unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len); + + if (lb_is_nested_possibly_constant(type, sel, fv->value)) { + bool is_constant = true; + Type *cv_type = f->type; + for (isize j = 1; j < sel.index.count; j++) { + i32 index = sel.index[j]; + Type *cvt = base_type(cv_type); + + if (cvt->kind == Type_Struct) { + if (cvt->Struct.is_raw_union) { + // sanity check which should have been caught by `lb_is_nested_possibly_constant` + is_constant = false; + break; + } + cv_type = cvt->Struct.fields[index]->type; + + if (is_type_struct(cv_type)) { + auto cv_field_remapping = lb_get_struct_remapping(m, cv_type); + idx_list[j-1] = cast(unsigned)cv_field_remapping[index]; + } else { + idx_list[j-1] = cast(unsigned)index; + } + } else if (cvt->kind == Type_Array) { + cv_type = cvt->Array.elem; + + idx_list[j-1] = cast(unsigned)index; + } else { + GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type)); + } + } + if (is_constant) { + LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value; + GB_ASSERT(LLVMIsConstant(elem_value)); + values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len); + } + } + } } } } else { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 5bffc7226..3676847b4 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4044,7 +4044,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { return {}; } - gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { ast_node(cl, CompoundLit, expr); @@ -4093,12 +4092,25 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) { ast_node(fv, FieldValue, elem); String name = fv->field->Ident.token.string; Selection sel = lookup_field(bt, name, false); - index = sel.index[0]; + GB_ASSERT(!sel.indirect); + elem = fv->value; - TypeAndValue tav = type_and_value_of_expr(elem); + if (sel.index.count > 1) { + if (lb_is_nested_possibly_constant(type, sel, elem)) { + continue; + } + lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel); + field_expr = lb_build_expr(p, elem); + field_expr = lb_emit_conv(p, field_expr, sel.entity->type); + lb_emit_store(p, dst, field_expr); + continue; + } + + index = sel.index[0]; } else { - TypeAndValue tav = type_and_value_of_expr(elem); Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index); + GB_ASSERT(sel.index.count == 1); + GB_ASSERT(!sel.indirect); index = sel.index[0]; } diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index a4ef61531..19df9ab06 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -915,7 +915,7 @@ gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t) if (field_remapping == nullptr) { field_remapping = map_get(&m->struct_field_remapping, cast(void *)t); } - GB_ASSERT(field_remapping != nullptr); + GB_ASSERT_MSG(field_remapping != nullptr, "%s", type_to_string(t)); return *field_remapping; } diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 2b8f38fef..ff4befc37 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -1,6 +1,6 @@ template struct PtrSet { - static_assert(TypeIsPointer::value, "PtrSet::T must be a pointer"); + static_assert(TypeIsPointer::value || TypeIsPtrSizedInteger::value, "PtrSet::T must be a pointer"); static constexpr uintptr TOMBSTONE = ~(uintptr)(0ull); T * keys; diff --git a/src/types.cpp b/src/types.cpp index 85364667d..ee610a2ce 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -430,7 +430,6 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) { return res; } - gb_global Type basic_types[] = { {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, -- cgit v1.2.3 From 1f17a391c62826f460f485d644f275f8e0d00630 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 27 Feb 2023 15:58:32 +0000 Subject: Improve error line squiggle logic --- src/check_expr.cpp | 4 ++-- src/error.cpp | 36 ++++++++++++++++++------------------ src/parser_pos.cpp | 4 ++-- 3 files changed, 22 insertions(+), 22 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b9588a798..29a673980 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -4615,7 +4615,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod entity = scope_lookup_current(import_scope, entity_name); bool allow_builtin = false; if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) { - error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); + error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name)); operand->mode = Addressing_Invalid; operand->expr = node; @@ -4635,7 +4635,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod if (!is_entity_exported(entity, allow_builtin)) { gbString sel_str = expr_to_string(selector); - error(op_expr, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name)); + error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name)); gb_string_free(sel_str); // NOTE(bill): make the state valid still, even if it's "invalid" // operand->mode = Addressing_Invalid; diff --git a/src/error.cpp b/src/error.cpp index c684be1e7..2974dc039 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -261,30 +261,31 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { enum { MAX_LINE_LENGTH = 80, MAX_TAB_WIDTH = 8, - ELLIPSIS_PADDING = 8 // `... ...` + ELLIPSIS_PADDING = 8, // `... ...` + MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING, }; error_out("\t"); terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); - isize squiggle_extra = 0; - - if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) { - i32 const half_width = MAX_LINE_LENGTH/2; - i32 left = cast(i32)(offset); - i32 right = cast(i32)(line.len - offset); - left = gb_min(left, half_width); - right = gb_min(right, half_width); - - line.text += offset-left; - line.len -= offset+right-left; - line = string_trim_whitespace(line); + i32 error_length = gb_max(end.offset - pos.offset, 1); - squiggle_extra = ELLIPSIS_PADDING/2 + 1; - offset = left + ELLIPSIS_PADDING/2; + isize squiggle_extra = 0; + if (line.len > MAX_LINE_LENGTH_PADDED) { + i32 left = MAX_TAB_WIDTH; + line.text += offset-left; + line.len -= offset-left; + offset = left+MAX_TAB_WIDTH/2; + if (line.len > MAX_LINE_LENGTH_PADDED) { + line.len = MAX_LINE_LENGTH_PADDED; + if (error_length > line.len-left) { + error_length = cast(i32)line.len - left; + squiggle_extra = 1; + } + } error_out("... %.*s ...", LIT(line)); } else { error_out("%.*s", LIT(line)); @@ -304,11 +305,10 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { error_out("~"); } } else if (end.line == pos.line && end.column > pos.column) { - i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset)); - for (i32 i = 1; i < length-1+squiggle_extra; i++) { + for (i32 i = 1; i < error_length-1+squiggle_extra; i++) { error_out("~"); } - if (length > 1 && squiggle_extra == 0) { + if (error_length > 1 && squiggle_extra == 0) { error_out("^"); } } diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp index fb7f0c9c2..8a0c3eca6 100644 --- a/src/parser_pos.cpp +++ b/src/parser_pos.cpp @@ -20,8 +20,8 @@ gb_internal Token ast_token(Ast *node) { case Ast_ParenExpr: return node->ParenExpr.open; case Ast_CallExpr: return ast_token(node->CallExpr.proc); case Ast_SelectorExpr: - if (node->SelectorExpr.selector != nullptr) { - return ast_token(node->SelectorExpr.selector); + if (node->SelectorExpr.expr != nullptr) { + return ast_token(node->SelectorExpr.expr); } return node->SelectorExpr.token; case Ast_SelectorCallExpr: -- cgit v1.2.3