Ctrl K
ComunidadGuías…

src/ast/ast.c

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// SPDX-License-Identifier: MIT
#include "ast.h"
#include "../parser/parser.h"
#include "../constants.h"
#include "../arena.h"
#include "../utils/colors.h"
#include "../utils/utils.h"
#include <stdlib.h>
#include <string.h>

// Local trait registry — avoids dependency on g_parser_ctx.
static TraitReg *registered_traits_local = NULL;

// Set by the language server while indexing a document; the parser uses it to
// resolve tuple-indexing expressions during LSP completion. Defined here (not in
// the LSP) so the compiler core stays self-contained.
int g_is_indexing = 0;

void register_trait(const char *name)
{
    TraitReg *r = xmalloc(sizeof(TraitReg));
    r->name = xstrdup(name);
    r->next = registered_traits_local;
    registered_traits_local = r;
}

void clear_registered_traits(void)
{
    // The TraitReg nodes are arena-allocated, so the arena reset/restore in the
    // fuzz harness and the LSP reclaims them. The list head must be cleared
    // there too, otherwise it dangles into reused arena memory and is_trait()
    // walks a cyclic list forever.
    registered_traits_local = NULL;
}

int is_trait(const char *name)
{
    if (!name)
    {
        return 0;
    }

    // Strip trailing stars for pointer types (e.g., IAnimal*)
    char *base = xstrdup(name);

    // Strip "struct " or "union " if present
    if (strncmp(base, "struct ", 7) == 0)
    {
        char *nb = xstrdup(base + 7);
        zfree(base);
        base = nb;
    }
    else if (strncmp(base, "union ", 6) == 0)
    {
        char *nb = xstrdup(base + 6);
        zfree(base);
        base = nb;
    }

    char *p = (char *)strchr(base, '*');
    if (p)
    {
        *p = '\0';
    }

    TraitReg *r = registered_traits_local;
    while (r)
    {
        if (0 == strcmp(r->name, base))
        {
            zfree(base);
            return 1;
        }
        r = r->next;
    }
    zfree(base);
    return 0;
}

ASTNode *ast_create(NodeType type)
{
    ASTNode *node = xmalloc(sizeof(ASTNode));
    memset(node, 0, sizeof(ASTNode));
    node->kind = type;
    return node;
}

void ast_free(ASTNode *node)
{
    if (node->kind == NODE_AST_COMMENT)
    {
        if (node->comment.content)
        {
            zfree(node->comment.content);
        }
    }
    zfree(node);
}

static void add_child(ASTChildren *c, ASTNode **slot)
{
    c->slots[c->count] = slot;
    c->count++;
}

// Every NodeType must have a case: a new kind fails to compile until it says
// which children it has (with GCC and Clang; TCC does not know these pragmas).
#if defined(__GNUC__) && !defined(__TINYC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch-enum"
#endif
ASTChildren ast_children(ASTNode *node)
{
    ASTChildren c = {0};
    if (!node)
    {
        return c;
    }

    switch (node->kind)
    {
    // Declarations and blocks.
    case NODE_ROOT:
        add_child(&c, &node->root.children);
        break;
    case NODE_FUNCTION:
        add_child(&c, &node->func.body);
        break;
    case NODE_BLOCK:
        add_child(&c, &node->block.statements);
        break;
    case NODE_VAR_DECL:
    case NODE_CONST:
        add_child(&c, &node->var_decl.init_expr);
        break;
    case NODE_DESTRUCT_VAR:
        add_child(&c, &node->destruct.init_expr);
        add_child(&c, &node->destruct.else_block);
        break;
    case NODE_STRUCT:
        add_child(&c, &node->strct.fields);
        break;
    case NODE_ENUM:
        add_child(&c, &node->enm.variants);
        break;
    case NODE_TRAIT:
        add_child(&c, &node->trait.methods);
        break;
    case NODE_IMPL:
        add_child(&c, &node->impl.methods);
        break;
    case NODE_IMPL_TRAIT:
        add_child(&c, &node->impl_trait.methods);
        break;
    case NODE_TEST:
        add_child(&c, &node->test_stmt.body);
        break;
    case NODE_COMPTIME:
        add_child(&c, &node->comptime.body);
        add_child(&c, &node->comptime.generated);
        break;

    // Statements.
    case NODE_RETURN:
        add_child(&c, &node->ret.value);
        break;
    case NODE_IF:
        add_child(&c, &node->if_stmt.condition);
        add_child(&c, &node->if_stmt.then_body);
        add_child(&c, &node->if_stmt.else_body);
        break;
    case NODE_WHILE:
        add_child(&c, &node->while_stmt.condition);
        add_child(&c, &node->while_stmt.body);
        break;
    case NODE_FOR:
        add_child(&c, &node->for_stmt.init);
        add_child(&c, &node->for_stmt.condition);
        add_child(&c, &node->for_stmt.step);
        add_child(&c, &node->for_stmt.body);
        break;
    case NODE_FOR_RANGE:
        add_child(&c, &node->for_range.start);
        add_child(&c, &node->for_range.end);
        add_child(&c, &node->for_range.body);
        break;
    case NODE_LOOP:
        add_child(&c, &node->loop_stmt.body);
        break;
    case NODE_MATCH:
        add_child(&c, &node->match_stmt.expr);
        add_child(&c, &node->match_stmt.cases);
        break;
    case NODE_MATCH_CASE:
        add_child(&c, &node->match_case.guard);
        add_child(&c, &node->match_case.body);
        break;
    case NODE_DEFER:
        add_child(&c, &node->defer_stmt.stmt);
        break;
    case NODE_ASSERT:
    case NODE_EXPECT:
        add_child(&c, &node->assert_stmt.condition);
        break;
    case NODE_GOTO:
        add_child(&c, &node->goto_stmt.goto_expr);
        break;
    case NODE_REPL_PRINT:
        add_child(&c, &node->repl_print.expr);
        break;

    // Expressions.
    case NODE_EXPR_BINARY:
        add_child(&c, &node->binary.left);
        add_child(&c, &node->binary.right);
        break;
    case NODE_EXPR_UNARY:
        add_child(&c, &node->unary.operand);
        break;
    case NODE_EXPR_CALL:
        add_child(&c, &node->call.callee);
        add_child(&c, &node->call.args);
        break;
    case NODE_EXPR_MEMBER:
        add_child(&c, &node->member.target);
        break;
    case NODE_EXPR_INDEX:
        add_child(&c, &node->index.array);
        add_child(&c, &node->index.index);
        add_child(&c, &node->index.extra_indices);
        break;
    case NODE_EXPR_SLICE:
        add_child(&c, &node->slice.array);
        add_child(&c, &node->slice.start);
        add_child(&c, &node->slice.end);
        break;
    case NODE_EXPR_CAST:
        add_child(&c, &node->cast.expr);
        break;
    case NODE_EXPR_SIZEOF:
    case NODE_TYPEOF:
        add_child(&c, &node->size_of.expr);
        break;
    case NODE_EXPR_STRUCT_INIT:
        add_child(&c, &node->struct_init.fields);
        break;
    case NODE_FIELD_INIT:
        add_child(&c, &node->var_decl.init_expr);
        break;
    case NODE_EXPR_ARRAY_LITERAL:
        add_child(&c, &node->array_literal.elements);
        break;
    case NODE_EXPR_TUPLE_LITERAL:
        add_child(&c, &node->tuple_literal.elements);
        break;
    case NODE_TERNARY:
        add_child(&c, &node->ternary.cond);
        add_child(&c, &node->ternary.true_expr);
        add_child(&c, &node->ternary.false_expr);
        break;
    case NODE_LAMBDA:
        add_child(&c, &node->lambda.body);
        break;
    case NODE_TRY:
        add_child(&c, &node->try_stmt.expr);
        break;
    case NODE_CUDA_LAUNCH:
        add_child(&c, &node->cuda_launch.call);
        add_child(&c, &node->cuda_launch.grid);
        add_child(&c, &node->cuda_launch.block);
        add_child(&c, &node->cuda_launch.shared_mem);
        add_child(&c, &node->cuda_launch.stream);
        break;
    case NODE_VA_START:
        add_child(&c, &node->va_start_args.ap);
        add_child(&c, &node->va_start_args.last_arg);
        break;
    case NODE_VA_END:
        add_child(&c, &node->va_end_args.ap);
        break;
    case NODE_VA_COPY:
        add_child(&c, &node->va_copy_args.dest);
        add_child(&c, &node->va_copy_args.src);
        break;
    case NODE_VA_ARG:
        add_child(&c, &node->va_arg_val.ap);
        break;

    // No children. NODE_IMPORT and NODE_RAW_STMT do hold nodes, but not as
    // children: see ast_children() in ast.h.
    case NODE_EXPR_LITERAL:
    case NODE_EXPR_VAR:
    case NODE_BREAK:
    case NODE_CONTINUE:
    case NODE_LABEL:
    case NODE_TYPE_ALIAS:
    case NODE_FIELD:
    case NODE_ENUM_VARIANT:
    case NODE_INCLUDE:
    case NODE_LINK:
    case NODE_IMPORT:
    case NODE_RAW_STMT:
    case NODE_PREPROC_DIRECTIVE:
    case NODE_ASM:
    case NODE_PLUGIN:
    case NODE_REFLECTION:
    case NODE_AST_COMMENT:
    case NODE_ERRONEOUS:
        break;

    default:
        break;
    }
    return c;
}
#if defined(__GNUC__) && !defined(__TINYC__)
#pragma GCC diagnostic pop
#endif

Type *type_new(TypeKind kind)
{
    Type *t = xmalloc(sizeof(Type));
    memset(t, 0, sizeof(Type));
    t->kind = kind;
    if (kind == TYPE_FUNCTION)
    {
        t->traits.has_drop = 1;
    }
    t->name = NULL;
    t->link_name = NULL;
    t->inner = NULL;
    t->args = NULL;
    t->count = 0;
    t->is_const = 0;
    t->is_explicit_struct = 0;
    t->is_raw = 0;
    t->array_size = 0;
    return t;
}

Type *type_new_ptr(Type *inner)
{
    Type *t = type_new(TYPE_POINTER);
    t->inner = inner;
    return t;
}

Type *type_new_array(Type *inner, int size)
{
    Type *t = type_new(TYPE_ARRAY);
    t->inner = inner;
    t->array_size = size;
    return t;
}

Type *type_clone(Type *t)
{
    if (!t)
    {
        return NULL;
    }
    Type *clone = xmalloc(sizeof(Type));
    memcpy(clone, t, sizeof(Type));

    // In Zen C, we only clone the top-level Type struct to isolate usage-site metadata
    // like lifetime_depth. However, we SHIELD the recursive structures (inner, args)
    // by sharing their pointers. This ensures that type inference (which may happen
    // at a usage site) correctly propagates back to the canonical Type objects
    // that the codegen and other passes depend on.
    // clone->inner = t->inner; // Already done by memcpy
    // clone->args = t->args;   // Already done by memcpy

    // Note: Strings like name and link_name are shared (shallow copy)
    // but names are usually static or managed by pctx string pool.
    return clone;
}

static int is_char_ptr(Type *t)
{
    if (!t)
    {
        return 0;
    }
    // Handle both primitive char* and legacy struct char*.
    if (TYPE_POINTER == t->kind && t->inner && TYPE_CHAR == t->inner->kind)
    {
        return 1;
    }
    if (TYPE_POINTER == t->kind && t->inner && TYPE_STRUCT == t->inner->kind && t->inner->name &&
        0 == strcmp(t->inner->name, "char"))
    {
        return 1;
    }
    return 0;
}

int is_integer_type(Type *t)
{
    if (!t)
    {
        return 0;
    }

    if (t->kind == TYPE_ALIAS && !t->alias.is_opaque_alias)
    {
        return is_integer_type(t->inner);
    }

    int res =
        (t->kind == TYPE_INT || t->kind == TYPE_CHAR || t->kind == TYPE_BOOL ||
         t->kind == TYPE_I8 || t->kind == TYPE_U8 || t->kind == TYPE_I16 || t->kind == TYPE_U16 ||
         t->kind == TYPE_I32 || t->kind == TYPE_U32 || t->kind == TYPE_I64 || t->kind == TYPE_U64 ||
         t->kind == TYPE_USIZE || t->kind == TYPE_ISIZE || t->kind == TYPE_BYTE ||
         t->kind == TYPE_RUNE || t->kind == TYPE_UINT || t->kind == TYPE_I128 ||
         t->kind == TYPE_U128 || t->kind == TYPE_BITINT || t->kind == TYPE_UBITINT ||
         t->kind == TYPE_C_INT || t->kind == TYPE_C_UINT || t->kind == TYPE_C_LONG ||
         t->kind == TYPE_C_ULONG || t->kind == TYPE_C_LONGLONG || t->kind == TYPE_C_ULONGLONG ||
         t->kind == TYPE_C_SHORT || t->kind == TYPE_C_USHORT || t->kind == TYPE_C_CHAR ||
         t->kind == TYPE_C_UCHAR ||
         (t->kind == TYPE_STRUCT && t->name &&
          (0 == strcmp(t->name, "int8_t") || 0 == strcmp(t->name, "uint8_t") ||
           0 == strcmp(t->name, "int16_t") || 0 == strcmp(t->name, "uint16_t") ||
           0 == strcmp(t->name, "int32_t") || 0 == strcmp(t->name, "uint32_t") ||
           0 == strcmp(t->name, "int64_t") || 0 == strcmp(t->name, "uint64_t") ||
           0 == strcmp(t->name, "size_t") || 0 == strcmp(t->name, "ssize_t") ||
           0 == strcmp(t->name, "ptrdiff_t"))));
    return res;
}

int is_unsigned_type(Type *t)
{
    if (!t)
    {
        return 0;
    }

    if (t->kind == TYPE_ALIAS && !t->alias.is_opaque_alias)
    {
        return is_unsigned_type(t->inner);
    }

    int res =
        (t->kind == TYPE_U8 || t->kind == TYPE_U16 || t->kind == TYPE_U32 || t->kind == TYPE_U64 ||
         t->kind == TYPE_USIZE || t->kind == TYPE_UINT || t->kind == TYPE_U128 ||
         t->kind == TYPE_UBITINT || t->kind == TYPE_C_UINT || t->kind == TYPE_C_ULONG ||
         t->kind == TYPE_C_ULONGLONG || t->kind == TYPE_C_USHORT || t->kind == TYPE_C_UCHAR ||
         (t->kind == TYPE_STRUCT && t->name &&
          (0 == strcmp(t->name, "uint8_t") || 0 == strcmp(t->name, "uint16_t") ||
           0 == strcmp(t->name, "uint32_t") || 0 == strcmp(t->name, "uint64_t") ||
           0 == strcmp(t->name, "size_t"))));
    return res;
}

int is_signed_type(Type *t)
{
    if (!t)
    {
        return 0;
    }

    if (t->kind == TYPE_ALIAS && !t->alias.is_opaque_alias)
    {
        return is_signed_type(t->inner);
    }

    int res =
        (t->kind == TYPE_I8 || t->kind == TYPE_I16 || t->kind == TYPE_I32 || t->kind == TYPE_I64 ||
         t->kind == TYPE_ISIZE || t->kind == TYPE_INT || t->kind == TYPE_I128 ||
         t->kind == TYPE_BITINT || t->kind == TYPE_C_INT || t->kind == TYPE_C_LONG ||
         t->kind == TYPE_C_LONGLONG || t->kind == TYPE_C_SHORT || t->kind == TYPE_C_CHAR ||
         (t->kind == TYPE_STRUCT && t->name &&
          (0 == strcmp(t->name, "int8_t") || 0 == strcmp(t->name, "int16_t") ||
           0 == strcmp(t->name, "int32_t") || 0 == strcmp(t->name, "int64_t") ||
           0 == strcmp(t->name, "ssize_t") || 0 == strcmp(t->name, "ptrdiff_t"))));
    return res;
}

int is_boolean_type(Type *t)
{
    if (!t)
    {
        return 0;
    }

    if (t->kind == TYPE_ALIAS && !t->alias.is_opaque_alias)
    {
        return is_boolean_type(t->inner);
    }

    return (t->kind == TYPE_BOOL);
}

int is_float_type(Type *t)
{
    if (!t)
    {
        return 0;
    }

    if (t->kind == TYPE_ALIAS && !t->alias.is_opaque_alias)
    {
        return is_float_type(t->inner);
    }

    int res = (t->kind == TYPE_FLOAT || t->kind == TYPE_F32 || t->kind == TYPE_F64);
    return res;
}

int is_incomplete_type(struct ParserContext *ctx, Type *t)
{
    if (!t || t->kind != TYPE_STRUCT || !t->name)
    {
        return 0;
    }
    ASTNode *def = find_struct_def(ctx, t->name);
    if (!def)
    {
        return 1;
    }
    return def->strct.is_incomplete;
}

int is_composite_expression(ASTNode *node)
{
    if (!node)
    {
        return 0;
    }

    switch (node->kind)
    {
    case NODE_EXPR_BINARY:
        return 1;
    case NODE_EXPR_UNARY:
        return 1;
    case NODE_TERNARY:
        return 1;
    default:
        return 0;
    }
}

int type_eq(Type *a, Type *b)
{
    if (!a || !b)
    {
        return 0;
    }

    if (a == b)
    {
        return 1;
    }

    if (a->kind == TYPE_UNKNOWN || b->kind == TYPE_UNKNOWN)
    {
        return 1;
    }

    // Lax integer matching (bool == int, char == i8, etc.).
    if (is_integer_type(a) && is_integer_type(b))
    {
        return 1;
    }

    // Lax float matching.
    if (is_float_type(a) && is_float_type(b))
    {
        return 1;
    }

    // String Literal vs char*
    if (a->kind == TYPE_STRING && is_char_ptr(b))
    {
        return 1;
    }

    if (b->kind == TYPE_STRING && is_char_ptr(a))
    {
        return 1;
    }

    if (a->kind != b->kind)
    {
        if (!((a->kind == TYPE_STRUCT && b->kind == TYPE_ENUM) ||
              (a->kind == TYPE_ENUM && b->kind == TYPE_STRUCT)))
        {
            return 0;
        }
    }

    if (a->kind == TYPE_STRUCT || a->kind == TYPE_GENERIC || a->kind == TYPE_ENUM)
    {
        if (!a->name || !b->name)
        {
            return 0;
        }
        return 0 == strcmp(a->name, b->name);
    }
    if (a->kind == TYPE_ALIAS)
    {
        if (a->alias.is_opaque_alias)
        {
            if (b->kind != TYPE_ALIAS || !b->alias.is_opaque_alias)
            {
                return 0;
            }
            return 0 == strcmp(a->name, b->name);
        }
        return type_eq(a->inner, b);
    }
    if (a->kind == TYPE_POINTER)
    {
        return type_eq(a->inner, b->inner);
    }
    if (a->kind == TYPE_FUNCTION)
    {
        if (a->is_raw != b->is_raw)
        {
            return 0;
        }
        if (a->count != b->count)
        {
            return 0;
        }
        if (!type_eq(a->inner, b->inner))
        {
            return 0;
        }
        for (int i = 0; i < a->count; i++)
        {
            if (!type_eq(a->args[i], b->args[i]))
            {
                return 0;
            }
        }
        return 1;
    }
    if (a->kind == TYPE_ARRAY || a->kind == TYPE_VECTOR)
    {
        return a->array_size == b->array_size && type_eq(a->inner, b->inner);
    }

    return 1;
}

static char *type_to_string_impl(Type *t);

char *type_to_string(Type *t)
{
    if (!t)
    {
        return xstrdup("void");
    }
    char *res = type_to_string_impl(t);
    if (t->is_const)
    {
        char *final = xmalloc(strlen(res) + 7);
        sprintf(final, "const %s", res); /* safe */
        zfree(res);
        return final;
    }
    return res;
}

static char *type_to_string_impl(Type *t)
{
    if (!t)
    {
        return xstrdup("void");
    }

    switch (t->kind)
    {
    case TYPE_VOID:
        return xstrdup("void");
    case TYPE_BOOL:
        return xstrdup("bool");
    case TYPE_STRING:
        return xstrdup("string");
    case TYPE_CHAR:
        return xstrdup("char");
    case TYPE_I8:
        return xstrdup("int8_t");
    case TYPE_U8:
        return xstrdup("uint8_t");
    case TYPE_I16:
        return xstrdup("int16_t");
    case TYPE_U16:
        return xstrdup("uint16_t");
    case TYPE_I32:
        return xstrdup("int32_t");
    case TYPE_U32:
        return xstrdup("uint32_t");
    case TYPE_I64:
        return xstrdup("int64_t");
    case TYPE_U64:
        return xstrdup("uint64_t");
    case TYPE_F32:
        return xstrdup("float");
    case TYPE_F64:
        return xstrdup("double");
    case TYPE_USIZE:
        return xstrdup("size_t");
    case TYPE_ISIZE:
        return xstrdup("ptrdiff_t");
    case TYPE_BYTE:
        return xstrdup("uint8_t");
    case TYPE_I128:
        return xstrdup("__int128");
    case TYPE_U128:
        return xstrdup("unsigned __int128");
    case TYPE_RUNE:
        return xstrdup("int32_t");
    case TYPE_UINT:
        return xstrdup("unsigned int");

    // Portable C Types
    case TYPE_C_INT:
        return xstrdup("c_int");
    case TYPE_C_UINT:
        return xstrdup("c_uint");
    case TYPE_C_LONG:
        return xstrdup("c_long");
    case TYPE_C_ULONG:
        return xstrdup("c_ulong");
    case TYPE_C_LONGLONG:
        return xstrdup("c_longlong");
    case TYPE_C_ULONGLONG:
        return xstrdup("c_ulonglong");
    case TYPE_C_SHORT:
        return xstrdup("c_short");
    case TYPE_C_USHORT:
        return xstrdup("c_ushort");
    case TYPE_C_CHAR:
        return xstrdup("c_char");
    case TYPE_C_UCHAR:
        return xstrdup("c_uchar");

    case TYPE_INT:
        return xstrdup("int");
    case TYPE_FLOAT:
        return xstrdup("float");
    case TYPE_BITINT:
    {
        char *res = xmalloc(32);
        sprintf(res, "i%d", t->array_size); /* safe */
        return res;
    }
    case TYPE_UBITINT:
    {
        char *res = xmalloc(32);
        sprintf(res, "u%d", t->array_size); /* safe */
        return res;
    }

    case TYPE_VECTOR:
    {
        if (t->name)
        {
            return xstrdup(t->name);
        }
        char *inner = type_to_string(t->inner);
        char *res = xmalloc(strlen(inner) + 20);
        sprintf(res, "%sx%d", inner, t->array_size); /* safe */
        zfree(inner);
        return res;
    }

    case TYPE_POINTER:
    {
        char *inner = type_to_string(t->inner);
        if (t->is_restrict)
        {
            char *res = xmalloc(strlen(inner) + 16);
            sprintf(res, "%s* __restrict", inner); /* safe */
            return res;
        }
        else
        {
            char *res = xmalloc(strlen(inner) + 2);
            sprintf(res, "%s*", inner); /* safe */
            return res;
        }
    }

    case TYPE_ARRAY:
    {
        if (t->array_size == 0)
        {
            char *inner = type_to_string(t->inner);
            char *res = xmalloc(strlen(inner) + 8);
            sprintf(res, "Slice__%s", inner); /* safe */
            return res;
        }

        Type *base = t;
        int *dims = NULL;
        int dims_cap = 0;
        int dims_count = 0;

        while (base->kind == TYPE_ARRAY && base->array_size > 0)
        {
            if (dims_count == dims_cap)
            {
                dims_cap = dims_cap == 0 ? 4 : dims_cap * 2;
                dims = xrealloc(dims, sizeof(int) * (size_t)(dims_cap));
            }
            dims[dims_count++] = base->array_size;
            base = base->inner;
        }

        char *inner = type_to_string(base);
        size_t total_len = strlen(inner) + 1;
        for (int i = 0; i < dims_count; i++)
        {
            total_len += 20;
        }

        char *res = xmalloc((size_t)(total_len));
        strcpy(res, inner);
        zfree(inner);

        char *p = res + strlen(res);
        for (int i = 0; i < dims_count; i++)
        {
            snprintf(p, 20, "[%d]", dims[i]);
            p += strlen(p);
        }

        if (dims)
        {
            zfree(dims);
        }
        return res;
    }

    case TYPE_FUNCTION:
    {
        if (t->is_raw)
        {
            // fn*(Args)->Ret
            char *ret = type_to_string(t->inner);
            char *res = xmalloc(strlen(ret) + 64);
            snprintf(res, strlen(ret) + 64, "fn*(");

            for (int i = 0; i < t->count; i++)
            {
                if (i > 0)
                {
                    char *tmp = xmalloc(strlen(res) + 3);
                    snprintf(tmp, strlen(res) + 3, "%s, ", res);
                    zfree(res);
                    res = tmp;
                }
                char *arg = type_to_string(t->args[i]);
                char *tmp = xmalloc(strlen(res) + strlen(arg) + 1);
                sprintf(tmp, "%s%s", res, arg); /* safe */
                zfree(res);
                res = tmp;
                zfree(arg);
            }
            if (t->is_varargs)
            {
                char *tmp = xmalloc(strlen(res) + 6);
                sprintf(tmp, "%s, ...", res); /* safe */
                zfree(res);
                res = tmp;
            }
            char *tmp = xmalloc(strlen(res) + strlen(ret) + 6); // ) -> Ret
            sprintf(tmp, "%s) -> %s", res, ret);                /* safe */
            zfree(res);
            res = tmp;
            zfree(ret);
            return res;
        }

        // fn(Args) -> Ret
        char *ret = type_to_string(t->inner);
        char *res = xmalloc(strlen(ret) + 64);
        snprintf(res, strlen(ret) + 64, "fn(");

        for (int i = 0; i < t->count; i++)
        {
            if (i > 0)
            {
                char *tmp = xmalloc(strlen(res) + 3);
                snprintf(tmp, strlen(res) + 3, "%s, ", res);
                zfree(res);
                res = tmp;
            }
            char *arg = type_to_string(t->args[i]);
            char *tmp = xmalloc(strlen(res) + strlen(arg) + 1);
            sprintf(tmp, "%s%s", res, arg); /* safe */
            zfree(res);
            res = tmp;
            zfree(arg);
        }
        char *tmp = xmalloc(strlen(res) + strlen(ret) + 6); // ) -> Ret
        sprintf(tmp, "%s) -> %s", res, ret);                /* safe */
        zfree(res);
        res = tmp;
        zfree(ret);
        return res;
    }

    case TYPE_STRUCT:
    case TYPE_GENERIC:
    {
        if (t->count > 0 && t->name && strstr(t->name, "__") == NULL)
        {
            char *base = t->name;
            size_t base_len = strlen(base);
            char *res = xmalloc((size_t)(base_len + 1));
            strcpy(res, base);

            for (int i = 0; i < t->count; i++)
            {
                char *arg = type_to_string(t->args[i]);
                char *clean_arg = sanitize_mangled_name(arg);

                size_t new_len = strlen(res) + strlen(clean_arg) + 3;
                char *new_res = xmalloc((size_t)(new_len));
                sprintf(new_res, "%s__%s", res, clean_arg); /* safe */

                zfree(res);
                res = new_res;
                zfree(arg);
                zfree(clean_arg);
            }
            return res;
        }
        return xstrdup(t->name);
    }
    case TYPE_ALIAS:
        return xstrdup(t->name);
    case TYPE_ENUM:
        return xstrdup(t->link_name ? t->link_name : t->name);

    default:
        return xstrdup("unknown");
    }
}

// C-compatible type stringifier.
// Strictly uses 'struct T' for explicit structs to support external types.
// Does NOT mangle pointers to 'Ptr'.
static char *type_to_c_string_impl(Type *t, int misra_mode);

char *type_to_c_string(Type *t, int misra_mode)
{
    if (!t)
    {
        return xstrdup("void");
    }
    char *res = type_to_c_string_impl(t, misra_mode);
    if (t->is_const)
    {
        char *final = xmalloc(strlen(res) + 7);
        sprintf(final, "const %s", res); /* safe */
        zfree(res);
        return final;
    }
    return res;
}

static char *type_to_c_string_impl(Type *t, int misra_mode)
{
    if (!t)
    {
        return xstrdup("void");
    }

    switch (t->kind)
    {
    case TYPE_VOID:
        return xstrdup("void");
    case TYPE_STRUCT:
    {
        if (t->link_name)
        {
            return xstrdup(
                t->link_name); // Only prepend 'struct' if explicitly requested (e.g. "struct Foo")
        }
        // otherwise assume it's a typedef/alias (e.g. "Foo").
        if (t->is_explicit_struct)
        {
            const char *final_name = t->link_name ? t->link_name : t->name;
            char *res = xmalloc(strlen(final_name) + 8);
            sprintf(res, "struct %s", final_name); /* safe */
            return res;
        }
        else
        {
            return xstrdup(t->link_name ? t->link_name : t->name);
        }
    }
    case TYPE_BOOL:
        return xstrdup("bool");
    case TYPE_STRING:
        return xstrdup("string");
    case TYPE_CHAR:
        return xstrdup("char");
    case TYPE_I8:
        return xstrdup("int8_t");
    case TYPE_U8:
        return xstrdup("uint8_t");
    case TYPE_I16:
        return xstrdup("int16_t");
    case TYPE_U16:
        return xstrdup("uint16_t");
    case TYPE_I32:
        return xstrdup("int32_t");
    case TYPE_U32:
        return xstrdup("uint32_t");
    case TYPE_I64:
        return xstrdup("int64_t");
    case TYPE_U64:
        return xstrdup("uint64_t");
    case TYPE_F32:
        return xstrdup("float");
    case TYPE_F64:
        return xstrdup("double");
    case TYPE_USIZE:
        return xstrdup("size_t");
    case TYPE_ISIZE:
        return xstrdup("ptrdiff_t");
    case TYPE_BYTE:
        return xstrdup("uint8_t");
    case TYPE_I128:
        return xstrdup("__int128");
    case TYPE_U128:
        return xstrdup("unsigned __int128");
    case TYPE_RUNE:
        return xstrdup("int32_t");
    case TYPE_UINT:
        return xstrdup("unsigned int");

    // Portable C Types (Map directly to C types)
    case TYPE_C_INT:
        return xstrdup(misra_mode ? "int32_t" : "int");
    case TYPE_C_UINT:
        return xstrdup(misra_mode ? "uint32_t" : "unsigned int");
    case TYPE_C_LONG:
        return xstrdup(misra_mode ? "int64_t" : "long");
    case TYPE_C_ULONG:
        return xstrdup(misra_mode ? "uint64_t" : "unsigned long");
    case TYPE_C_LONGLONG:
        return xstrdup(misra_mode ? "int64_t" : "long long");
    case TYPE_C_ULONGLONG:
        return xstrdup(misra_mode ? "uint64_t" : "unsigned long long");
    case TYPE_C_SHORT:
        return xstrdup(misra_mode ? "int16_t" : "short");
    case TYPE_C_USHORT:
        return xstrdup(misra_mode ? "uint16_t" : "unsigned short");
    case TYPE_C_CHAR:
        return xstrdup(misra_mode ? "int8_t" : "char");
    case TYPE_C_UCHAR:
        return xstrdup(misra_mode ? "uint8_t" : "unsigned char");

    case TYPE_INT:
        // 'int' in Zen C maps to 'i32' now for portability.
        // FFI should use c_int.
        return xstrdup("int32_t");
    case TYPE_FLOAT:
        return xstrdup("float");
    case TYPE_BITINT:
    {
        char *res = xmalloc(32);
        sprintf(res, "_BitInt(%d)", t->array_size); /* safe */
        return res;
    }
    case TYPE_UBITINT:
    {
        char *res = xmalloc(40);
        sprintf(res, "unsigned _BitInt(%d)", t->array_size); /* safe */
        return res;
    }

    case TYPE_VECTOR:
    {
        if (t->name)
        {
            return xstrdup(t->name);
        }
        char *inner = type_to_c_string(t->inner, misra_mode);
        char *res = xmalloc(strlen(inner) + 32);
        sprintf(res, "ZC_SIMD(%s, %d)", inner, t->array_size); /* safe */
        zfree(inner);
        return res;
    }

    case TYPE_POINTER:
    {
        char *inner = type_to_c_string(t->inner, misra_mode);
        char *ptr_token = (char *)strstr(inner, "(*");
        if (ptr_token)
        {
            ptrdiff_t prefix_len = ptr_token - inner + 2; // "void (*"
            char *res = xmalloc(strlen(inner) + 2);
            strncpy(res, inner, (size_t)(prefix_len));
            res[prefix_len] = 0;
            strcat(res, "*");
            strcat(res, ptr_token + 2);
            zfree(inner);
            return res;
        }

        if (t->is_restrict)
        {
            char *res = xmalloc(strlen(inner) + 16);
            sprintf(res, "%s* __restrict", inner); /* safe */
            return res;
        }
        else
        {
            char *res = xmalloc(strlen(inner) + 2);
            sprintf(res, "%s*", inner); /* safe */
            return res;
        }
    }

    case TYPE_ARRAY:
    {
        if (t->array_size == 0)
        {
            char *inner_zens = type_to_string(t->inner);
            char *res = xmalloc(strlen(inner_zens) + 8);
            sprintf(res, "Slice__%s", inner_zens); /* safe */
            zfree(inner_zens);
            return res;
        }

        Type *base = t;
        int *dims = NULL;
        int dims_cap = 0;
        int dims_count = 0;

        while (base->kind == TYPE_ARRAY && base->array_size > 0)
        {
            if (dims_count == dims_cap)
            {
                dims_cap = dims_cap == 0 ? 4 : dims_cap * 2;
                dims = xrealloc(dims, sizeof(int) * (size_t)(dims_cap));
            }
            dims[dims_count++] = base->array_size;
            base = base->inner;
        }

        char *inner = type_to_c_string(base, misra_mode);
        size_t total_len = strlen(inner) + 1;
        for (int i = 0; i < dims_count; i++)
        {
            total_len += 20;
        }

        char *res = xmalloc((size_t)(total_len));
        strcpy(res, inner);
        zfree(inner);

        char *p = res + strlen(res);
        for (int i = 0; i < dims_count; i++)
        {
            snprintf(p, 20, "[%d]", dims[i]);
            p += strlen(p);
        }

        if (dims)
        {
            zfree(dims);
        }
        return res;
    }

    case TYPE_FUNCTION:
        if (t->is_raw)
        {
            char *ret = type_to_c_string(t->inner, misra_mode);
            char *res = xmalloc(strlen(ret) + 64); // heuristic start buffer
            snprintf(res, strlen(ret) + 64, "%s (*)(", ret);

            for (int i = 0; i < t->count; i++)
            {
                if (i > 0)
                {
                    char *tmp = xmalloc(strlen(res) + 3);
                    snprintf(tmp, strlen(res) + 3, "%s, ", res);
                    zfree(res);
                    res = tmp;
                }
                char *arg = type_to_c_string(t->args[i], misra_mode);
                char *tmp = xmalloc(strlen(res) + strlen(arg) + 1);
                sprintf(tmp, "%s%s", res, arg); /* safe */
                zfree(res);
                res = tmp;
                zfree(arg);
            }
            if (t->is_varargs)
            {
                if (t->count > 0)
                {
                    char *tmp = xmalloc(strlen(res) + 6);
                    sprintf(tmp, "%s, ...", res); /* safe */
                    zfree(res);
                    res = tmp;
                }
                else
                {
                    char *tmp = xmalloc(strlen(res) + 4);
                    sprintf(tmp, "%s...", res); /* safe */
                    zfree(res);
                    res = tmp;
                }
            }
            char *tmp = xmalloc(strlen(res) + 2);
            sprintf(tmp, "%s)", res); /* safe */
            zfree(res);
            res = tmp;
            zfree(ret);
            return res;
        }
        if (t->inner)
        {
            zfree(type_to_c_string(t->inner, misra_mode));
        }
        return xstrdup("z_closure_T");

    case TYPE_GENERIC:
        // Use type_to_string to get the mangled name (e.g. Option_int) instead of raw C string
        // composition This ensures consistency with struct definitions.
        {
            char *s = type_to_string(t);
            return s;
        }

    case TYPE_ALIAS:
        if (t->alias.is_opaque_alias)
        {
            return xstrdup(t->name);
        }
        return type_to_c_string(t->inner, misra_mode);

    case TYPE_ENUM:
        return xstrdup(t->link_name ? t->link_name : t->name);

    case TYPE_UNSAFE_ANY:
        return xstrdup("any");
    default:
        return xstrdup("unknown");
    }
}

Type *get_inner_type(Type *t)
{
    while (t && t->kind == TYPE_ALIAS && !t->alias.is_opaque_alias)
    {
        t = t->inner;
    }
    return t;
}

// Type inference — resolve an expression node to its C type string.
// (Moved from codegen_utils.c this is part of core, not codegen)

char *infer_type(ParserContext *ctx, ASTNode *node)
{
    if (!node || !ctx)
    {
        return NULL;
    }

    if (node->type_info && node->type_info->kind != TYPE_UNKNOWN)
    {
        char *t = type_to_c_string(node->type_info, ctx->config->misra_mode);
        return t;
    }

    if (node->resolved_type && strcmp(node->resolved_type, "unknown") != 0)
    {
        if (strcmp(node->resolved_type, "c_int") == 0)
        {
            return "int";
        }
        if (strcmp(node->resolved_type, "c_uint") == 0)
        {
            return "unsigned int";
        }
        if (strcmp(node->resolved_type, "c_long") == 0)
        {
            return "long";
        }
        if (strcmp(node->resolved_type, "c_ulong") == 0)
        {
            return "unsigned long";
        }
        if (strcmp(node->resolved_type, "c_longlong") == 0)
        {
            return "long long";
        }
        if (strcmp(node->resolved_type, "c_ulonglong") == 0)
        {
            return "unsigned long long";
        }
        if (strcmp(node->resolved_type, "c_short") == 0)
        {
            return "short";
        }
        if (strcmp(node->resolved_type, "c_ushort") == 0)
        {
            return "unsigned short";
        }
        if (strcmp(node->resolved_type, "c_char") == 0)
        {
            return "char";
        }
        if (strcmp(node->resolved_type, "c_uchar") == 0)
        {
            return "unsigned char";
        }

        return node->resolved_type;
    }

    if (node->kind == NODE_EXPR_LITERAL)
    {
        if (node->type_info)
        {
            return type_to_c_string(node->type_info, ctx->config->misra_mode);
        }
        return NULL;
    }

    if (node->kind == NODE_EXPR_VAR)
    {
        ZenSymbol *sym = find_symbol_entry(ctx, node->var_ref.name);
        if (sym)
        {
            if (sym->type_name)
            {
                return sym->type_name;
            }
            if (sym->type_info)
            {
                return type_to_c_string(sym->type_info, ctx->config->misra_mode);
            }
        }
    }

    if (node->kind == NODE_EXPR_CALL)
    {
        if (node->call.callee->kind == NODE_EXPR_VAR)
        {
            FuncSig *sig = find_func(ctx, node->call.callee->var_ref.name);
            if (sig)
            {
                if (sig->ret_type)
                {
                    return type_to_c_string(sig->ret_type, ctx->config->misra_mode);
                }
            }

            // Fallback for known stdlib memory/file functions.
            if (strcmp(node->call.callee->var_ref.name, "malloc") == 0 ||
                strcmp(node->call.callee->var_ref.name, "calloc") == 0 ||
                strcmp(node->call.callee->var_ref.name, "realloc") == 0 ||
                strcmp(node->call.callee->var_ref.name, "fopen") == 0 ||
                strcmp(node->call.callee->var_ref.name, "popen") == 0 ||
                strcmp(node->call.callee->var_ref.name, "fdopen") == 0)
            {
                return "void*";
            }
            ASTNode *sdef = find_struct_def(ctx, node->call.callee->var_ref.name);
            if (sdef)
            {
                return node->call.callee->var_ref.name;
            }

            // Check for enum variants (constructors)
            EnumVariantReg *ev = find_enum_variant(ctx, node->call.callee->var_ref.name);
            if (ev)
            {
                return ev->enum_name;
            }
        }
        // Method call: target.method() - look up Type_method signature.
        if (node->call.callee->kind == NODE_EXPR_MEMBER)
        {
            char *target_type = infer_type(ctx, node->call.callee->member.target);
            if (target_type)
            {
                char clean_type[MAX_TYPE_NAME_LEN];
                snprintf(clean_type, sizeof(clean_type), "%s", target_type);

                // Robustly strip all pointer levels for method lookup
                char *ptr = (char *)strchr(clean_type, '*');
                if (ptr)
                {
                    *ptr = 0;
                }

                char *base = clean_type;
                if (strncmp(base, "struct ", 7) == 0)
                {
                    base += 7;
                }

                char func_base[MAX_MANGLED_NAME_LEN];
                snprintf(func_base, sizeof(func_base), "%s__%s", base,
                         node->call.callee->member.field);
                char *func_name = merge_underscores(func_base);

                FuncSig *sig = find_func(ctx, func_name);

                if (sig && sig->ret_type)
                {
                    char *ret = type_to_c_string(sig->ret_type, ctx->config->misra_mode);
                    zfree(func_name);
                    return ret;
                }
                zfree(func_name);
            }
        }

        if (node->call.callee->kind == NODE_EXPR_VAR)
        {
            ZenSymbol *sym = find_symbol_entry(ctx, node->call.callee->var_ref.name);
            if (sym && sym->type_info && sym->type_info->kind == TYPE_FUNCTION &&
                sym->type_info->inner)
            {
                return type_to_c_string(sym->type_info->inner, ctx->config->misra_mode);
            }
        }
    }

    if (node->kind == NODE_TRY)
    {
        char *inner_type = infer_type(ctx, node->try_stmt.expr);
        if (inner_type)
        {
            // Extract T from Result<T> or Option<T>
            char *start = (char *)strchr(inner_type, '<');
            if (start)
            {
                start++; // Skip <
                char *end = (char *)strrchr(inner_type, '>');
                if (end && end > start)
                {
                    ptrdiff_t len = end - start;
                    char *extracted = xmalloc((size_t)(len + 1));
                    strncpy(extracted, start, (size_t)(len));
                    extracted[len] = 0;
                    return extracted;
                }
            }

            // Find the struct/enum definition and look for "Ok" or "val"
            char *search_name = inner_type;
            if (strncmp(search_name, "struct ", 7) == 0)
            {
                search_name += 7;
            }

            ASTNode *def = find_struct_def(ctx, search_name);
            if (!def)
            {
                // check enums list explicitly if not found in instantiated list
                StructRef *er = ctx->parsed_enums_list;
                while (er)
                {
                    if (er->node && er->node->kind == NODE_ENUM &&
                        strcmp(er->node->enm.name, search_name) == 0)
                    {
                        def = er->node;
                        break;
                    }
                    er = er->next;
                }
            }

            if (def)
            {
                if (def->kind == NODE_ENUM)
                {
                    // Look for "Ok" variant
                    ASTNode *var = def->enm.variants;
                    while (var)
                    {
                        if (var->variant.name && strcmp(var->variant.name, "Ok") == 0)
                        {
                            if (var->variant.payload)
                            {
                                return type_to_c_string(var->variant.payload,
                                                        ctx->config->misra_mode);
                            }
                            // Ok with no payload? Then it's void/u0.
                            return "void";
                        }
                        var = var->next;
                    }
                }
                else if (def->kind == NODE_STRUCT)
                {
                    // Look for "val" field
                    ASTNode *field = def->strct.fields;
                    while (field)
                    {
                        if (field->field.name && strcmp(field->field.name, "val") == 0)
                        {
                            return xstrdup(field->field.type);
                        }
                        field = field->next;
                    }
                }
            }
        }
    }

    if (node->kind == NODE_EXPR_MEMBER)
    {
        char *parent_type = infer_type(ctx, node->member.target);
        if (!parent_type)
        {
            return NULL;
        }

        char clean_name[MAX_TYPE_NAME_LEN];
        snprintf(clean_name, sizeof(clean_name), "%s", parent_type);
        char *ptr = (char *)strchr(clean_name, '*');
        if (ptr)
        {
            *ptr = 0;
        }

        return get_field_type_str(ctx, clean_name, node->member.field);
    }

    if (node->kind == NODE_EXPR_BINARY)
    {
        if (strcmp(node->binary.op, "??") == 0)
        {
            return infer_type(ctx, node->binary.left);
        }

        const char *op = node->binary.op;
        char *left_type = infer_type(ctx, node->binary.left);
        char *right_type = infer_type(ctx, node->binary.right);

        int is_logical = (strcmp(op, "&&") == 0 || strcmp(op, "||") == 0 || strcmp(op, "==") == 0 ||
                          strcmp(op, "!=") == 0 || strcmp(op, "<") == 0 || strcmp(op, ">") == 0 ||
                          strcmp(op, "<=") == 0 || strcmp(op, ">=") == 0);

        if (is_logical)
        {
            return xstrdup("int");
        }

        if (left_type && strcmp(left_type, "usize") == 0)
        {
            return "usize";
        }
        if (right_type && strcmp(right_type, "usize") == 0)
        {
            return "usize";
        }
        if (left_type && strcmp(left_type, "double") == 0)
        {
            return "double";
        }

        return left_type ? left_type : right_type;
    }

    if (node->kind == NODE_MATCH)
    {
        ASTNode *case_node = node->match_stmt.cases;
        while (case_node)
        {
            char *type = infer_type(ctx, case_node->match_case.body);
            if (type && strcmp(type, "void") != 0 && strcmp(type, "unknown") != 0)
            {
                return type;
            }
            case_node = case_node->next;
        }
        return NULL;
    }

    if (node->kind == NODE_EXPR_INDEX)
    {
        char *array_type = infer_type(ctx, node->index.array);
        if (array_type)
        {
            // If T*, returns T. If T[], returns T.
            char *ptr = (char *)strrchr(array_type, '*');
            if (ptr)
            {
                ptrdiff_t len = ptr - array_type;
                char *buf = xmalloc((size_t)(len + 1));
                strncpy(buf, array_type, (size_t)(len));
                buf[len] = 0;
                return buf;
            }

            if (strncmp(array_type, "Slice__", 7) == 0)
            {
                return xstrdup(array_type + 7);
            }

            char *search_name = array_type;
            if (strncmp(search_name, "struct ", 7) == 0)
            {
                search_name += 7;
            }

            ASTNode *def = find_struct_def(ctx, search_name);
            if (def && def->type_info && def->type_info->kind == TYPE_VECTOR &&
                def->type_info->inner)
            {
                return type_to_c_string(def->type_info->inner, ctx->config->misra_mode);
            }
        }
        return "int";
    }

    if (node->kind == NODE_EXPR_UNARY)
    {
        if (strcmp(node->unary.op, "&") == 0)
        {
            char *inner = infer_type(ctx, node->unary.operand);
            if (inner)
            {
                char *buf = xmalloc(strlen(inner) + 2);
                sprintf(buf, "%s*", inner); /* safe */
                return buf;
            }
        }
        if (strcmp(node->unary.op, "*") == 0)
        {
            char *inner = infer_type(ctx, node->unary.operand);
            if (inner)
            {
                if (strcmp(inner, "string") == 0)
                {
                    return xstrdup("char");
                }
                char *ptr = (char *)strchr(inner, '*');
                if (ptr)
                {
                    // Return base type (naive)
                    ptrdiff_t len = ptr - inner;
                    char *dup = xmalloc((size_t)(len + 1));
                    strncpy(dup, inner, (size_t)(len));
                    dup[len] = 0;
                    return dup;
                }
            }
        }
        return infer_type(ctx, node->unary.operand);
    }

    if (node->kind == NODE_EXPR_CAST)
    {
        return node->cast.target_type;
    }

    if (node->kind == NODE_EXPR_STRUCT_INIT)
    {
        return node->struct_init.struct_name;
    }

    if (node->kind == NODE_EXPR_ARRAY_LITERAL)
    {
        if (node->type_info)
        {
            return type_to_c_string(node->type_info, ctx->config->misra_mode);
        }
        return NULL;
    }

    if (node->kind == NODE_EXPR_LITERAL)
    {
        if (node->literal.kind == LITERAL_STRING)
        {
            return xstrdup("string");
        }
        if (node->literal.kind == LITERAL_CHAR)
        {
            return xstrdup("char");
        }
        if (node->literal.kind == LITERAL_FLOAT)
        {
            return "double";
        }
        return "int";
    }

    return NULL;
}

// Extract variable names from argument string.

// Parse original method name from mangled name.

// Replace string type in arguments.

// Helper to emit auto type or fallback.

// Field type lookup — get the type string of a struct field.
// (Moved from codegen_utils.c)

char *get_field_type_str(ParserContext *ctx, const char *struct_name, const char *field_name)
{
    char clean_name[MAX_TYPE_NAME_LEN];
    strncpy(clean_name, struct_name, sizeof(clean_name) - 1);
    clean_name[sizeof(clean_name) - 1] = 0;

    char *ptr = (char *)strchr(clean_name, '<');
    if (ptr)
    {
        *ptr = 0;
    }

    ASTNode *def = find_struct_def(ctx, clean_name);
    if (!def)
    {
        return NULL;
    }

    ASTNode *f = def->strct.fields;
    while (f)
    {
        if (strcmp(f->field.name, field_name) == 0)
        {
            return f->field.type;
        }
        f = f->next;
    }
    return NULL;
}

// Type inference.