src/ast/ast.c
Rama main · réplica de lectura del repositorio original.
// 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.