diff options
| -rw-r--r-- | src/ast/ast.h | 5 | ||||
| -rw-r--r-- | src/codegen/codegen_decl.c | 10 | ||||
| -rw-r--r-- | src/codegen/codegen_stmt.c | 2 | ||||
| -rw-r--r-- | src/parser/parser_decl.c | 8 | ||||
| -rw-r--r-- | src/parser/parser_expr.c | 135 | ||||
| -rw-r--r-- | src/parser/parser_struct.c | 52 | ||||
| -rw-r--r-- | src/parser/parser_utils.c | 1 | ||||
| -rw-r--r-- | tests/generics/test_generics_fn.zc | 14 |
8 files changed, 210 insertions, 17 deletions
diff --git a/src/ast/ast.h b/src/ast/ast.h index 523c1fb..6ef5d7a 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -160,8 +160,9 @@ struct ASTNode struct { char *name; - char *args; // Legacy string args. - char *ret_type; // Legacy string return type. + char *generic_params; // <T, U> + char *args; // Legacy string args. + char *ret_type; // Legacy string return type. ASTNode *body; Type **arg_types; char **defaults; diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c index 109aa9a..18d81f5 100644 --- a/src/codegen/codegen_decl.c +++ b/src/codegen/codegen_decl.c @@ -732,6 +732,11 @@ void emit_protos(ASTNode *node, FILE *out) ASTNode *m = f->impl.methods; while (m) { + if (m->func.generic_params) + { + m = m->next; + continue; + } char *fname = m->func.name; char *proto = xmalloc(strlen(fname) + strlen(sname) + 2); int slen = strlen(sname); @@ -810,6 +815,11 @@ void emit_protos(ASTNode *node, FILE *out) ASTNode *m = f->impl_trait.methods; while (m) { + if (m->func.generic_params) + { + m = m->next; + continue; + } if (m->func.is_async) { fprintf(out, "Async %s(%s);\n", m->func.name, m->func.args); diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c index 983130c..54c6a14 100644 --- a/src/codegen/codegen_stmt.c +++ b/src/codegen/codegen_stmt.c @@ -551,7 +551,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out) fprintf(out, ";\n"); break; case NODE_FUNCTION: - if (!node->func.body) + if (!node->func.body || node->func.generic_params) { break; } 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<T> + 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, diff --git a/tests/generics/test_generics_fn.zc b/tests/generics/test_generics_fn.zc index ac1cbb5..03f751a 100644 --- a/tests/generics/test_generics_fn.zc +++ b/tests/generics/test_generics_fn.zc @@ -23,3 +23,17 @@ test "Generics" { var b = Box<int> { val: 100 }; assert(b.get() == 100, "Generic impl failed"); } + +struct Container { } + +impl Container { + fn check_size<T>(self, value: T) -> int { + return sizeof(value); + } +} + +test "Generic Method in Regular Impl" { + var c = Container{}; + assert(c.check_size<int>(10) == sizeof(int), "Generic method int size"); + assert(c.check_size<double>(3.14) == sizeof(double), "Generic method double size"); +} |
