From a7eb36e4ac1eb437f99e2b1d8d8aab93f0767fc1 Mon Sep 17 00:00:00 2001 From: Zuhaitz Méndez Fernández de Aránguiz Date: Fri, 23 Jan 2026 18:20:25 +0000 Subject: Fix for #93 --- src/parser/parser_decl.c | 8 ++- src/parser/parser_expr.c | 135 +++++++++++++++++++++++++++++++++++++++++++++ src/parser/parser_struct.c | 52 +++++++++++++---- src/parser/parser_utils.c | 1 + 4 files changed, 182 insertions(+), 14 deletions(-) (limited to 'src/parser') diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c index 0cd2990..5cac0b4 100644 --- a/src/parser/parser_decl.c +++ b/src/parser/parser_decl.c @@ -187,8 +187,12 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async) if (gen_param) { - register_func_template(ctx, name, gen_param, node); - return NULL; + node->func.generic_params = xstrdup(gen_param); + if (!ctx->current_impl_struct) + { + register_func_template(ctx, name, gen_param, node); + return NULL; + } } if (!ctx->current_impl_struct) { diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c index ae7abe9..585d68c 100644 --- a/src/parser/parser_expr.c +++ b/src/parser/parser_expr.c @@ -4330,6 +4330,141 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec) node->resolved_type = xstrdup("unknown"); } + // Handle Generic Method Call: object.method + if (lexer_peek(l).type == TOK_LANGLE) + { + Lexer lookahead = *l; + lexer_next(&lookahead); + + int valid_generic = 0; + int saved = ctx->is_speculative; + ctx->is_speculative = 1; + + // Speculatively check if it's a valid generic list + while (1) + { + parse_type(ctx, &lookahead); + if (lexer_peek(&lookahead).type == TOK_COMMA) + { + lexer_next(&lookahead); + continue; + } + if (lexer_peek(&lookahead).type == TOK_RANGLE) + { + valid_generic = 1; + } + break; + } + ctx->is_speculative = saved; + + if (valid_generic) + { + lexer_next(l); // consume < + + char **concrete = xmalloc(sizeof(char *) * 8); + char **unmangled = xmalloc(sizeof(char *) * 8); + int argc = 0; + while (1) + { + Type *t = parse_type_formal(ctx, l); + concrete[argc] = type_to_string(t); + unmangled[argc] = type_to_c_string(t); + argc++; + if (lexer_peek(l).type == TOK_COMMA) + { + lexer_next(l); + } + else + { + break; + } + } + if (lexer_next(l).type != TOK_RANGLE) + { + zpanic_at(lexer_peek(l), "Expected >"); + } + + // Locate the generic template + char *mn = NULL; // method name + char full_name[1024]; + + // If logic above found a sig, we have a mangled name in node->type_info->name + // But for templates, find_func might have failed. + // Construct potential template name: Struct__method + char *struct_name = NULL; + if (lhs->type_info) + { + if (lhs->type_info->kind == TYPE_STRUCT) + { + struct_name = lhs->type_info->name; + } + else if (lhs->type_info->kind == TYPE_POINTER && lhs->type_info->inner && + lhs->type_info->inner->kind == TYPE_STRUCT) + { + struct_name = lhs->type_info->inner->name; + } + } + + if (struct_name) + { + sprintf(full_name, "%s__%s", struct_name, node->member.field); + + // Join types + char all_concrete[1024] = {0}; + char all_unmangled[1024] = {0}; + for (int i = 0; i < argc; i++) + { + if (i > 0) + { + strcat(all_concrete, ","); + strcat(all_unmangled, ","); + } + strcat(all_concrete, concrete[i]); + strcat(all_unmangled, unmangled[i]); + free(concrete[i]); + free(unmangled[i]); + } + free(concrete); + free(unmangled); + + mn = instantiate_function_template(ctx, full_name, all_concrete, + all_unmangled); + if (mn) + { + // Ensure member field reflects the instantiated name (suffix only) + // The instantiate returns Struct__method_int. We need method_int part? + // Actually member access codegen expects .field to be unmangled or + // checks lookup. But here we are resolving a specific method instance. + + // AST doesn't support generic member node well, typical approach: + // Replace member node with a special "MEMBER_GENERIC" or hack the field + // name. Hack: Update field name to include mangle suffix? But codegen + // does "Struct__Field". If full_name is Struct__method, mn is + // Struct__method_int. Codegen does: struct_name + "__" + field. So if + // we set field to "method_int", codegen does Struct__method_int. + + char *p = strstr(mn, "__"); + if (p) + { + free(node->member.field); + node->member.field = xstrdup(p + 2); + } + + // Update Type Info + Type *ft = type_new(TYPE_FUNCTION); + ft->name = xstrdup(mn); + // Look up return type from instantiated func + FuncSig *isig = find_func(ctx, mn); + if (isig) + { + ft->inner = isig->ret_type; + } + node->type_info = ft; + } + } + } + } + lhs = node; continue; } diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c index 01d1156..19bfd47 100644 --- a/src/parser/parser_struct.c +++ b/src/parser/parser_struct.c @@ -252,9 +252,16 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) f->func.args = na; // Register function for lookup - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, f->func.arg_types, - f->func.ret_type_info, f->func.is_varargs, f->func.is_async, - f->token); + if (f->func.generic_params) + { + register_func_template(ctx, mangled, f->func.generic_params, f); + } + else + { + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + f->func.is_async, f->token); + } if (!h) { @@ -284,9 +291,16 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) f->func.args = na; // Register function for lookup - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, - f->func.is_async, f->token); + if (f->func.generic_params) + { + register_func_template(ctx, mangled, f->func.generic_params, f); + } + else + { + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + f->func.is_async, f->token); + } if (!h) { @@ -505,9 +519,16 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) free(f->func.args); f->func.args = na; - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, 0, - f->token); + if (f->func.generic_params) + { + register_func_template(ctx, mangled, f->func.generic_params, f); + } + else + { + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, + 0, f->token); + } if (!h) { @@ -534,9 +555,16 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l) char *na = patch_self_args(f->func.args, name1); free(f->func.args); f->func.args = na; - register_func(ctx, mangled, f->func.arg_count, f->func.defaults, - f->func.arg_types, f->func.ret_type_info, f->func.is_varargs, - 1, f->token); + if (f->func.generic_params) + { + register_func_template(ctx, mangled, f->func.generic_params, f); + } + else + { + register_func(ctx, mangled, f->func.arg_count, f->func.defaults, + f->func.arg_types, f->func.ret_type_info, + f->func.is_varargs, 1, f->token); + } if (!h) { h = f; diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c index b322881..a11324d 100644 --- a/src/parser/parser_utils.c +++ b/src/parser/parser_utils.c @@ -1980,6 +1980,7 @@ char *instantiate_function_template(ParserContext *ctx, const char *name, const } free(new_fn->func.name); new_fn->func.name = xstrdup(mangled); + new_fn->func.generic_params = NULL; register_func(ctx, mangled, new_fn->func.arg_count, new_fn->func.defaults, new_fn->func.arg_types, new_fn->func.ret_type_info, new_fn->func.is_varargs, 0, -- cgit v1.2.3