From f513b6237feabcdf7fa5095f5f58d39b9845f653 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 11 Aug 2022 12:59:55 +0200 Subject: [PATCH] Improved error messages for foo(void), foo(int!) declarations. --- src/compiler/parse_expr.c | 2 +- src/compiler/parse_global.c | 26 ++++++++++++++------ src/compiler/parse_stmt.c | 2 +- src/compiler/parser_internal.h | 2 +- src/compiler/sema_decls.c | 20 ++++++++++++--- src/compiler/sema_expr.c | 21 +++++++++++++--- test/test_suite/abi/darwinx64_2.c3t | 2 +- test/test_suite/clang/2002-04.c3t | 2 +- test/test_suite/functions/failable_param.c3 | 3 +++ test/test_suite/functions/void_params.c3 | 2 ++ test/test_suite2/abi/darwinx64_2.c3t | 2 +- test/test_suite2/clang/2002-04.c3t | 2 +- test/test_suite2/functions/failable_param.c3 | 3 +++ test/test_suite2/functions/void_params.c3 | 2 ++ 14 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 test/test_suite/functions/failable_param.c3 create mode 100644 test/test_suite/functions/void_params.c3 create mode 100644 test/test_suite2/functions/failable_param.c3 create mode 100644 test/test_suite2/functions/void_params.c3 diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 04527f334..b128c62bd 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1594,7 +1594,7 @@ Expr *parse_type_expression_with_path(ParseContext *c, Path *path) } else { - ASSIGN_TYPE_OR_RET(type, parse_failable_type(c), poisoned_expr); + ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), poisoned_expr); } if (tok_is(c, TOKEN_LBRACE)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 205f5f7eb..93535f4e8 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -703,7 +703,7 @@ TypeInfo *parse_type(ParseContext *c) return parse_type_with_base(c, base); } -TypeInfo *parse_failable_type(ParseContext *c) +TypeInfo *parse_optional_type(ParseContext *c) { ASSIGN_TYPE_OR_RET(TypeInfo *info, parse_base_type(c), poisoned_type_info); ASSIGN_TYPE_OR_RET(info, parse_type_with_base(c, info), poisoned_type_info); @@ -771,7 +771,7 @@ Decl *parse_decl(ParseContext *c) bool is_threadlocal = try_consume(c, TOKEN_TLOCAL); bool is_static = !is_threadlocal && try_consume(c, TOKEN_STATIC); - ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_failable_type(c), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_optional_type(c), poisoned_decl); ASSIGN_DECL_OR_RET(Decl * decl, parse_decl_after_type(c, type), poisoned_decl); if (type->failable && decl->var.unwrap) @@ -959,7 +959,7 @@ static inline Decl *parse_global_declaration(ParseContext *c, Visibility visibil { bool threadlocal = try_consume(c, TOKEN_TLOCAL); - ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_failable_type(c), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_optional_type(c), poisoned_decl); Decl *decl = DECL_VAR_NEW(type, VARDECL_GLOBAL, visibility); @@ -999,7 +999,12 @@ static inline Decl *parse_global_declaration(ParseContext *c, Visibility visibil */ static inline bool parse_param_decl(ParseContext *c, Visibility parent_visibility, Decl*** parameters, bool require_name) { - ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_type(c), false); + ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_optional_type(c), false); + if (type->failable) + { + SEMA_ERROR(type, "Parameters may not be failable."); + return false; + } bool vararg = try_consume(c, TOKEN_ELLIPSIS); Decl *param = DECL_VAR_NEW(type, VARDECL_PARAM, parent_visibility); param->var.vararg = vararg; @@ -1069,7 +1074,7 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref if (!ellipsis && parse_next_is_typed_parameter(c)) { - ASSIGN_TYPE_OR_RET(type, parse_type(c), false); + ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), false); ellipsis = try_consume(c, TOKEN_ELLIPSIS); } @@ -1159,7 +1164,7 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref default: if (!type && parse_next_may_be_type(c)) { - ASSIGN_TYPE_OR_RET(type, parse_type(c), false); + ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), false); param_kind = VARDECL_PARAM; no_name = true; span = type->span; @@ -1168,6 +1173,11 @@ bool parse_parameters(ParseContext *c, Visibility visibility, Decl ***params_ref SEMA_ERROR_HERE("Expected a parameter."); return false; } + if (type && type->failable) + { + SEMA_ERROR(type, "Parameters may not be failable."); + return false; + } Decl *param = decl_new_var(name, span, type, param_kind, visibility); param->var.type_info = type; if (!no_name) @@ -1523,7 +1533,7 @@ static inline Decl *parse_define_type(ParseContext *c, Visibility visibility) Decl *decl = decl_new_with_type(alias_name, name_loc, DECL_TYPEDEF, visibility); decl->typedef_decl.is_func = true; decl->typedef_decl.is_distinct = distinct; - ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_failable_type(c), poisoned_decl); + ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_optional_type(c), poisoned_decl); decl->typedef_decl.function_signature.returntype = type_infoid(type_info); if (!parse_parameter_list(c, decl->visibility, &(decl->typedef_decl.function_signature), true)) { @@ -1726,7 +1736,7 @@ static inline bool parse_func_macro_header(ParseContext *c, bool is_macro, } // 2. Now we must have a type - either that is the return type or the method type. - ASSIGN_TYPE_OR_RET(rtype, parse_failable_type(c), false); + ASSIGN_TYPE_OR_RET(rtype, parse_optional_type(c), false); // 4. We might have a type here, if so then we read it. if (!tok_is(c, TOKEN_DOT) && !parse_is_macro_name(c)) diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 209f60999..70c2c1a14 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -375,7 +375,7 @@ static inline bool parse_foreach_var(ParseContext *c, Ast *foreach) // If we don't get foreach (foo ... or foreach (*foo ... then a type is expected. if (!tok_is(c, TOKEN_IDENT) && !tok_is(c, TOKEN_AMP)) { - ASSIGN_TYPE_OR_RET(type, parse_failable_type(c), false); + ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), false); // Add the failable to the type for nicer error reporting. RANGE_EXTEND_PREV(type); diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 2a324f6d2..0efe2f86a 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -24,7 +24,7 @@ Expr *parse_type_expression_with_path(ParseContext *c, Path *path); Expr *parse_expr(ParseContext *c); bool consume_ident(ParseContext *c, const char* name); TypeInfo *parse_type(ParseContext *c); -TypeInfo *parse_failable_type(ParseContext *c); +TypeInfo *parse_optional_type(ParseContext *c); TypeInfo *parse_type_with_base(ParseContext *c, TypeInfo *type_info); Expr* parse_constant_expr(ParseContext *c); void parse_imports(ParseContext *c); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index f57b7dd66..df5d7263a 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -49,8 +49,20 @@ static bool sema_check_section(SemaContext *context, Attr *attr) return true; } -static inline bool sema_check_no_duplicate_parameter(Decl **decls, Decl *current, unsigned current_index, unsigned count) +static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *current, unsigned current_index, unsigned count) { + if (current->type && current->type == type_void) + { + if (count == 1 && !current->name && current->var.kind == VARDECL_PARAM) + { + SEMA_ERROR(current, "C-style 'foo(void)' style argument declarations are not valid, please remove 'void'."); + } + else + { + SEMA_ERROR(current, "Parameters may not be of type 'void'."); + } + return false; + } const char *name = current->name; if (!name) return true; for (int i = 0; i < current_index; i++) @@ -637,7 +649,7 @@ static inline Type *sema_analyse_function_signature(SemaContext *context, CallAB all_ok = false; continue; } - if (!sema_check_no_duplicate_parameter(params, param, i, param_count)) + if (!sema_check_param_uniqueness_and_type(params, param, i, param_count)) { all_ok = false; continue; @@ -1980,7 +1992,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) case VARDECL_ERASE: UNREACHABLE } - if (!sema_check_no_duplicate_parameter(parameters, param, i, param_count)) return decl_poison(decl); + if (!sema_check_param_uniqueness_and_type(parameters, param, i, param_count)) return decl_poison(decl); param->resolve_status = RESOLVE_DONE; } DeclId body_param = decl->macro_decl.body_param; @@ -2021,7 +2033,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) case VARDECL_ERASE: UNREACHABLE } - if (!sema_check_no_duplicate_parameter(body_parameters, param, i, body_param_count)) return decl_poison(decl); + if (!sema_check_param_uniqueness_and_type(body_parameters, param, i, body_param_count)) return decl_poison(decl); param->resolve_status = RESOLVE_DONE; } bool pure = false; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 65ce79c47..101672070 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1416,12 +1416,27 @@ FAIL_MISSING: if (params && params[i]->var.vararg) continue; // 17d. Argument missing, that's bad. - if (is_func_ptr) + if (!uses_named_parameters || is_func_ptr || !params[i]->name) { - SEMA_ERROR(call, "The call is missing parameter(s), please check the definition."); + if (entries_needed == 1) + { + SEMA_ERROR(call, "A parameter was expected for the call."); + return false; + } + if (num_args) + { + unsigned needed = entries_needed - num_args; + SEMA_ERROR(args[num_args - 1], + "Expected %d more %s after this one, did you forget %s?", + needed, needed > 1 ? "arguments" : "argument", needed > 1 ? "them" : "it"); + } + else + { + SEMA_ERROR(call, "The call needs %d parameters, please provide them.", entries_needed); + } return false; } - SEMA_ERROR(call, "The mandatory parameter '%s' was not set, please add it.", params[i]->name); + SEMA_ERROR(call, "The parameter '%s' must be set, did you forget it?", params[i]->name); return false; } call->call_expr.arguments = actual_args; diff --git a/test/test_suite/abi/darwinx64_2.c3t b/test/test_suite/abi/darwinx64_2.c3t index 144a8b9b4..713557505 100644 --- a/test/test_suite/abi/darwinx64_2.c3t +++ b/test/test_suite/abi/darwinx64_2.c3t @@ -6,7 +6,7 @@ struct St12 { int a @align(16); } -fn St12 f12_0(void) { while (1) {}; unreachable(); } +fn St12 f12_0() { while (1) {}; unreachable(); } fn void f12_1(St12 a0) {} struct St13_0 { long[3] f0; } diff --git a/test/test_suite/clang/2002-04.c3t b/test/test_suite/clang/2002-04.c3t index 8e499d99d..a54556205 100644 --- a/test/test_suite/clang/2002-04.c3t +++ b/test/test_suite/clang/2002-04.c3t @@ -39,7 +39,7 @@ struct St { extern fn St func_returning_struct(); -fn void loop(void) { +fn void loop() { func_returning_struct(); } diff --git a/test/test_suite/functions/failable_param.c3 b/test/test_suite/functions/failable_param.c3 new file mode 100644 index 000000000..be2aa6dd6 --- /dev/null +++ b/test/test_suite/functions/failable_param.c3 @@ -0,0 +1,3 @@ +module test; + +fn void test4(void!); // #error: Parameters may not be failable. diff --git a/test/test_suite/functions/void_params.c3 b/test/test_suite/functions/void_params.c3 new file mode 100644 index 000000000..e52d2c9a6 --- /dev/null +++ b/test/test_suite/functions/void_params.c3 @@ -0,0 +1,2 @@ +fn void test(int, void) {} // #error: Parameters may not be of type 'void'. +fn void test3(void); // #error: C-style 'foo(void)' style argument declarations are not valid \ No newline at end of file diff --git a/test/test_suite2/abi/darwinx64_2.c3t b/test/test_suite2/abi/darwinx64_2.c3t index baa795825..59a6cdd43 100644 --- a/test/test_suite2/abi/darwinx64_2.c3t +++ b/test/test_suite2/abi/darwinx64_2.c3t @@ -6,7 +6,7 @@ struct St12 { int a @align(16); } -fn St12 f12_0(void) { while (1) {}; unreachable(); } +fn St12 f12_0() { while (1) {}; unreachable(); } fn void f12_1(St12 a0) {} struct St13_0 { long[3] f0; } diff --git a/test/test_suite2/clang/2002-04.c3t b/test/test_suite2/clang/2002-04.c3t index 5f7b3b9ee..d58cf8508 100644 --- a/test/test_suite2/clang/2002-04.c3t +++ b/test/test_suite2/clang/2002-04.c3t @@ -39,7 +39,7 @@ struct St { extern fn St func_returning_struct(); -fn void loop(void) { +fn void loop() { func_returning_struct(); } diff --git a/test/test_suite2/functions/failable_param.c3 b/test/test_suite2/functions/failable_param.c3 new file mode 100644 index 000000000..be2aa6dd6 --- /dev/null +++ b/test/test_suite2/functions/failable_param.c3 @@ -0,0 +1,3 @@ +module test; + +fn void test4(void!); // #error: Parameters may not be failable. diff --git a/test/test_suite2/functions/void_params.c3 b/test/test_suite2/functions/void_params.c3 new file mode 100644 index 000000000..e52d2c9a6 --- /dev/null +++ b/test/test_suite2/functions/void_params.c3 @@ -0,0 +1,2 @@ +fn void test(int, void) {} // #error: Parameters may not be of type 'void'. +fn void test3(void); // #error: C-style 'foo(void)' style argument declarations are not valid \ No newline at end of file