From aa910a1c441957df023351a44a55308ba70e852a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 5 Aug 2025 12:50:36 +0200 Subject: [PATCH] Compiler assert when using generic parameters list without any parameters. #2369 --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 2 +- src/compiler/copying.c | 2 +- src/compiler/parse_expr.c | 26 ++++++++++++++-- src/compiler/parse_global.c | 3 +- src/compiler/parser_internal.h | 2 +- src/compiler/sema_decls.c | 2 +- src/compiler/sema_expr.c | 2 +- test/test_suite/generic/generic_no_params.c3 | 31 ++++++++++++++++++++ test/test_suite/struct/struct_bad_member.c3 | 4 +-- 10 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 test/test_suite/generic/generic_no_params.c3 diff --git a/releasenotes.md b/releasenotes.md index a287f963b..833391983 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -16,6 +16,7 @@ - Fixed: regression in comments for `@deprecated` and `@pure`. - Detect recursive creation of generics #2366. - Compiler assertion when defining a function with return type untyped_list #2368. +- Compiler assert when using generic parameters list without any parameters. #2369 ### Stdlib changes - Add `==` to `Pair`, `Triple` and TzDateTime. Add print to `Pair` and `Triple`. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3adf4ea11..c47a1de61 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -872,7 +872,7 @@ typedef struct typedef struct { ExprId parent; - Expr **parmeters; + Expr **parameters; } ExprGenericIdent; typedef struct diff --git a/src/compiler/copying.c b/src/compiler/copying.c index e23b8db76..c41b3a3e5 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -319,7 +319,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) return expr; case EXPR_GENERIC_IDENT: MACRO_COPY_EXPRID(expr->generic_ident_expr.parent); - MACRO_COPY_EXPR_LIST(expr->generic_ident_expr.parmeters); + MACRO_COPY_EXPR_LIST(expr->generic_ident_expr.parameters); return expr; case EXPR_MACRO_BODY_EXPANSION: MACRO_COPY_EXPR_LIST(expr->body_expansion_expr.values); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index b11ae2244..cfc6ca869 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -73,8 +73,29 @@ bool parse_range(ParseContext *c, Range *range) range->end = 0; return true; } +bool parse_generic_expr_list(ParseContext *c, Expr ***exprs_ref) +{ + SourceSpan start = c->span; + advance_and_verify(c, TOKEN_LBRACE); + if (try_consume(c, TOKEN_RBRACE)) + { + print_error_at(extend_span_with_token(start, c->prev_span), "At least one generic parameter was expected here."); + return false; + } + do + { + ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), false); + vec_add(*exprs_ref, expr); + if (!try_consume(c, TOKEN_COMMA)) + { + CONSUME_OR_RET(TOKEN_RBRACE, false); + return true; + } + } while (!try_consume(c, TOKEN_RBRACE)); + return true; +} -bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_token) +static bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_token) { while (!try_consume(c, end_token)) { @@ -1080,8 +1101,7 @@ static Expr *parse_generic_expr(ParseContext *c, Expr *left, SourceSpan lhs_star ASSERT(left && expr_ok(left)); Expr *subs_expr = expr_new(EXPR_GENERIC_IDENT, lhs_start); subs_expr->generic_ident_expr.parent = exprid(left); - advance_and_verify(c, TOKEN_LBRACE); - if (!parse_expr_list(c, &subs_expr->generic_ident_expr.parmeters, TOKEN_RBRACE)) return poisoned_expr; + if (!parse_generic_expr_list(c, &subs_expr->generic_ident_expr.parameters)) return poisoned_expr; RANGE_EXTEND_PREV(subs_expr); return subs_expr; } diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index be3f2befd..485454f9f 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -499,8 +499,7 @@ static inline TypeInfo *parse_generic_type(ParseContext *c, TypeInfo *type) { ASSERT(type_info_ok(type)); TypeInfo *generic_type = type_info_new(TYPE_INFO_GENERIC, type->span); - advance_and_verify(c, TOKEN_LBRACE); - if (!parse_expr_list(c, &generic_type->generic.params, TOKEN_RBRACE)) return poisoned_type_info; + if (!parse_generic_expr_list(c, &generic_type->generic.params)) return poisoned_type_info; generic_type->generic.base = type; return generic_type; } diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 61fe7b2b1..c09d6ba44 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -55,7 +55,7 @@ Decl *parse_local_decl_after_type(ParseContext *c, TypeInfo *type); Decl *parse_var_decl(ParseContext *c); bool parse_current_is_expr(ParseContext *c); bool parse_joined_strings(ParseContext *c, const char **str_ref, size_t *len_ref); -bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_token); +bool parse_generic_expr_list(ParseContext *c, Expr ***exprs_ref); bool parse_parameters(ParseContext *c, Decl ***params_ref, Variadic *variadic, int *vararg_index_ref, ParameterParseKind parse_kind); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 5c6135146..7d92607bb 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -5164,7 +5164,7 @@ Decl *sema_analyse_parameterized_identifier(SemaContext *c, Path *decl_path, con ASSERT(parameter_count > 0); if (parameter_count != vec_size(params)) { - ASSERT(vec_size(params)); + ASSERT_AT(span, vec_size(params)); sema_error_at(c, extend_span_with_token(params[0]->span, vectail(params)->span), "The generic module expected %d arguments, but you supplied %d, did you make a mistake?", parameter_count, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 04243d900..de4a0a7b5 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -10073,7 +10073,7 @@ static inline bool sema_expr_analyse_generic_ident(SemaContext *context, Expr *e } Decl *symbol = sema_analyse_parameterized_identifier(context, parent->unresolved_ident_expr.path, parent->unresolved_ident_expr.ident, parent->span, - expr->generic_ident_expr.parmeters, NULL); + expr->generic_ident_expr.parameters, NULL); if (!decl_ok(symbol)) return false; expr_resolve_ident(expr, symbol); return true; diff --git a/test/test_suite/generic/generic_no_params.c3 b/test/test_suite/generic/generic_no_params.c3 new file mode 100644 index 000000000..3b8864612 --- /dev/null +++ b/test/test_suite/generic/generic_no_params.c3 @@ -0,0 +1,31 @@ +module values {Type}; + +struct Values +{ + Type[] values; + int count; +} + +fn void test() +{ + +} +module main; + +import values; +import std::collections::list; + +fn void test1() +{ + int x = values::test{}(); // #error: At least one generic parameter +} + +fn void test2() +{ + List{Values{ }} v1s; // #error: At least one generic parameter +} + +fn void main() +{ + List{Values{int}} v2s; +} \ No newline at end of file diff --git a/test/test_suite/struct/struct_bad_member.c3 b/test/test_suite/struct/struct_bad_member.c3 index d32b70691..f3c7cf6cc 100644 --- a/test/test_suite/struct/struct_bad_member.c3 +++ b/test/test_suite/struct/struct_bad_member.c3 @@ -1,10 +1,10 @@ struct Foo { - void bar; // #error: Members cannot be of + void bar; // #error: Members cannot } alias Void = void; struct Foo2 { - Void bar; // #error: Members cannot be of + Void bar; // #error: Members cannot } typedef Void2 = void;