summaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorZuhaitz <zuhaitz.zechhub@gmail.com>2026-01-31 17:22:17 +0000
committerGitHub <noreply@github.com>2026-01-31 17:22:17 +0000
commit962d659c61212b1a23acfe56dda7cb92b721feda (patch)
treeba1637d3885213095b312f81a477c33b1ebca6aa /src/codegen
parente521ee7d175393ef37579ebd61ccb7e8d56a397f (diff)
parent91ed9fdd65e09bd6cd32e44dd07c390f2cf79c22 (diff)
Merge branch 'main' into main
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/codegen.c266
-rw-r--r--src/codegen/codegen.h4
-rw-r--r--src/codegen/codegen_decl.c35
-rw-r--r--src/codegen/codegen_main.c35
-rw-r--r--src/codegen/codegen_stmt.c15
-rw-r--r--src/codegen/codegen_utils.c52
-rw-r--r--src/codegen/compat.h84
7 files changed, 343 insertions, 148 deletions
diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c
index 7c58943..37415c2 100644
--- a/src/codegen/codegen.c
+++ b/src/codegen/codegen.c
@@ -1,4 +1,3 @@
-
#include "codegen.h"
#include "zprep.h"
#include "../constants.h"
@@ -59,12 +58,58 @@ static void codegen_var_expr(ParserContext *ctx, ASTNode *node, FILE *out)
if (node->var_ref.suggestion && !ctx->silent_warnings)
{
char msg[256];
- sprintf(msg, "Undefined variable '%s'", node->var_ref.name);
char help[256];
- sprintf(help, "Did you mean '%s'?", node->var_ref.suggestion);
+ snprintf(msg, sizeof(msg), "Undefined variable '%s'", node->var_ref.name);
+ snprintf(help, sizeof(help), "Did you mean '%s'?", node->var_ref.suggestion);
zwarn_at(node->token, "%s\n = help: %s", msg, help);
}
}
+
+ // Check for static method call pattern: Type::method or Slice<T>::method
+ char *double_colon = strstr(node->var_ref.name, "::");
+ if (double_colon)
+ {
+ // Extract type name and method name
+ int type_len = double_colon - node->var_ref.name;
+ char *type_name = xmalloc(type_len + 1);
+ strncpy(type_name, node->var_ref.name, type_len);
+ type_name[type_len] = 0;
+
+ char *method_name = double_colon + 2; // Skip ::
+
+ // Handle generic types: Slice<int> -> Slice_int
+ char mangled_type[256];
+ if (strchr(type_name, '<'))
+ {
+ // Generic type - need to mangle it
+ char *lt = strchr(type_name, '<');
+ char *gt = strchr(type_name, '>');
+
+ if (lt && gt)
+ {
+ // Extract base type and type argument
+ *lt = 0;
+ char *type_arg = lt + 1;
+ *gt = 0;
+
+ sprintf(mangled_type, "%s_%s", type_name, type_arg);
+ }
+ else
+ {
+ strcpy(mangled_type, type_name);
+ }
+ }
+ else
+ {
+ strcpy(mangled_type, type_name);
+ }
+
+ // Output as Type__method
+ fprintf(out, "%s__%s", mangled_type, method_name);
+ free(type_name);
+ return;
+ }
+
fprintf(out, "%s", node->var_ref.name);
}
@@ -146,7 +191,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else if ((strcmp(node->binary.op, "==") == 0 || strcmp(node->binary.op, "!=") == 0))
{
char *t1 = infer_type(ctx, node->binary.left);
-
int is_ptr = 0;
if (t1)
{
@@ -161,19 +205,16 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
int resolved = 0;
ASTNode *alias = global_user_structs;
- if (alias)
+ while (alias)
{
- while (alias)
+ if (alias->type == NODE_TYPE_ALIAS &&
+ strcmp(check, alias->type_alias.alias) == 0)
{
- if (alias->type == NODE_TYPE_ALIAS &&
- strcmp(check, alias->type_alias.alias) == 0)
- {
- check = alias->type_alias.original_type;
- resolved = 1;
- break;
- }
- alias = alias->next;
+ check = alias->type_alias.original_type;
+ resolved = 1;
+ break;
}
+ alias = alias->next;
}
if (!resolved)
{
@@ -183,10 +224,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
int is_basic = IS_BASIC_TYPE(t1);
-
ASTNode *def = t1 ? find_struct_def(ctx, t1) : NULL;
- if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic &&
- !is_ptr)
+
+ if (t1 && def && (def->type == NODE_STRUCT || def->type == NODE_ENUM) && !is_basic && !is_ptr)
{
char *base = t1;
if (strncmp(base, "struct ", 7) == 0)
@@ -239,7 +279,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else if (t1 && (strcmp(t1, "string") == 0 || strcmp(t1, "char*") == 0 ||
strcmp(t1, "const char*") == 0))
{
- // Check if comparing to NULL - don't use strcmp for NULL comparisons
int is_null_compare = 0;
if (node->binary.right->type == NODE_EXPR_VAR &&
strcmp(node->binary.right->var_ref.name, "NULL") == 0)
@@ -251,10 +290,21 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
is_null_compare = 1;
}
+ else if (node->binary.right->type == NODE_EXPR_LITERAL &&
+ node->binary.right->literal.type_kind == LITERAL_INT &&
+ node->binary.right->literal.int_val == 0)
+ {
+ is_null_compare = 1;
+ }
+ else if (node->binary.left->type == NODE_EXPR_LITERAL &&
+ node->binary.left->literal.type_kind == LITERAL_INT &&
+ node->binary.left->literal.int_val == 0)
+ {
+ is_null_compare = 1;
+ }
if (is_null_compare)
{
- // Direct pointer comparison for NULL
fprintf(out, "(");
codegen_expression(ctx, node->binary.left, out);
fprintf(out, " %s ", node->binary.op);
@@ -285,6 +335,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
codegen_expression(ctx, node->binary.right, out);
fprintf(out, ")");
}
+ if (t1) free(t1);
}
else
{
@@ -348,7 +399,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (def && def->type == NODE_ENUM)
{
char mangled[256];
- sprintf(mangled, "%s_%s", target->var_ref.name, method);
+ snprintf(mangled, sizeof(mangled), "%s_%s", target->var_ref.name, method);
FuncSig *sig = find_func(ctx, mangled);
if (sig)
{
@@ -357,15 +408,13 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
int arg_idx = 0;
while (arg)
{
- if (arg_idx > 0 && arg)
+ if (arg_idx > 0)
{
fprintf(out, ", ");
}
- Type *param_t =
- (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL;
+ Type *param_t = (arg_idx < sig->total_args) ? sig->arg_types[arg_idx] : NULL;
- // Tuple Packing Logic
if (param_t && param_t->kind == TYPE_STRUCT &&
strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 &&
node->call.arg_count > 1)
@@ -383,7 +432,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
arg = arg->next;
}
fprintf(out, "}");
- break; // All args consumed
+ break;
}
codegen_expression(ctx, arg, out);
@@ -403,7 +452,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
char *ptr = strchr(clean, '*');
if (ptr)
{
- *ptr = 0;
+ *ptr = '\0';
}
char *base = clean;
@@ -412,11 +461,43 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
base += 7;
}
+ char *mangled_base = base;
+ char base_buf[256];
+
+ // Mangle generic types: Slice<int> -> Slice_int, Vec<Point> -> Vec_Point
+ char *lt = strchr(base, '<');
+ if (lt)
+ {
+ char *gt = strchr(lt, '>');
+ if (gt)
+ {
+ int prefix_len = lt - base;
+ int arg_len = gt - lt - 1;
+ snprintf(base_buf, 255, "%.*s_%.*s", prefix_len, base, arg_len, lt + 1);
+ mangled_base = base_buf;
+ }
+ }
+
if (!strchr(type, '*') && target->type == NODE_EXPR_CALL)
{
- fprintf(out, "({ %s _t = ", type);
+ char *type_mangled = type;
+ char type_buf[256];
+ char *t_lt = strchr(type, '<');
+ if (t_lt)
+ {
+ char *t_gt = strchr(t_lt, '>');
+ if (t_gt)
+ {
+ int p_len = t_lt - type;
+ int a_len = t_gt - t_lt - 1;
+ snprintf(type_buf, 255, "%.*s_%.*s", p_len, type, a_len, t_lt + 1);
+ type_mangled = type_buf;
+ }
+ }
+
+ fprintf(out, "({ %s _t = ", type_mangled);
codegen_expression(ctx, target, out);
- fprintf(out, "; %s__%s(&_t", base, method);
+ fprintf(out, "; %s__%s(&_t", mangled_base, method);
ASTNode *arg = node->call.args;
while (arg)
{
@@ -429,36 +510,34 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else
{
// Mixin Lookup Logic
- char *call_base = base;
+ char *call_base = mangled_base;
+
int need_cast = 0;
char mixin_func_name[128];
- sprintf(mixin_func_name, "%s__%s", base, method);
+ snprintf(mixin_func_name, sizeof(mixin_func_name), "%s__%s", call_base, method);
char *resolved_method_suffix = NULL;
if (!find_func(ctx, mixin_func_name))
{
- // Try resolving as a trait method: Struct__Trait_Method
StructRef *ref = ctx->parsed_impls_list;
while (ref)
{
- if (ref->node && ref->node->type == NODE_IMPL_TRAIT)
+ if (ref->node && ref->node->type == NODE_IMPL_TRAIT &&
+ strcmp(ref->node->impl_trait.target_type, base) == 0)
{
- if (strcmp(ref->node->impl_trait.target_type, base) == 0)
+ char trait_mangled[256];
+ snprintf(trait_mangled, sizeof(trait_mangled), "%s__%s_%s", base,
+ ref->node->impl_trait.trait_name, method);
+ if (find_func(ctx, trait_mangled))
{
- char trait_mangled[256];
- sprintf(trait_mangled, "%s__%s_%s", base,
- ref->node->impl_trait.trait_name, method);
- if (find_func(ctx, trait_mangled))
- {
- char *suffix =
- xmalloc(strlen(ref->node->impl_trait.trait_name) +
- strlen(method) + 2);
- sprintf(suffix, "%s_%s", ref->node->impl_trait.trait_name,
- method);
- resolved_method_suffix = suffix;
- break;
- }
+ size_t suffix_len = strlen(ref->node->impl_trait.trait_name) +
+ strlen(method) + 2;
+ char *suffix = xmalloc(suffix_len);
+ snprintf(suffix, suffix_len, "%s_%s",
+ ref->node->impl_trait.trait_name, method);
+ resolved_method_suffix = suffix;
+ break;
}
}
ref = ref->next;
@@ -473,15 +552,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (it->impl_node && it->impl_node->type == NODE_IMPL_TRAIT)
{
tname = it->impl_node->impl_trait.trait_name;
- }
- if (tname)
- {
char trait_mangled[512];
- sprintf(trait_mangled, "%s__%s_%s", base, tname, method);
+ snprintf(trait_mangled, sizeof(trait_mangled),
+ "%s__%s_%s", base, tname, method);
if (find_func(ctx, trait_mangled))
{
- char *suffix = xmalloc(strlen(tname) + strlen(method) + 2);
- sprintf(suffix, "%s_%s", tname, method);
+ size_t suffix_len = strlen(tname) + strlen(method) + 2;
+ char *suffix = xmalloc(suffix_len);
+ snprintf(suffix, suffix_len, "%s_%s", tname, method);
resolved_method_suffix = suffix;
break;
}
@@ -496,15 +574,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- // Method not found on primary struct, check mixins
ASTNode *def = find_struct_def(ctx, base);
if (def && def->type == NODE_STRUCT && def->strct.used_structs)
{
for (int k = 0; k < def->strct.used_struct_count; k++)
{
char mixin_check[128];
- sprintf(mixin_check, "%s__%s", def->strct.used_structs[k],
- method);
+ snprintf(mixin_check, sizeof(mixin_check), "%s__%s",
+ def->strct.used_structs[k], method);
if (find_func(ctx, mixin_check))
{
call_base = def->strct.used_structs[k];
@@ -534,11 +611,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
arg = arg->next;
}
fprintf(out, ")");
+
+ if (resolved_method_suffix)
+ {
+ free(resolved_method_suffix);
+ }
}
free(clean);
+ free(type);
return;
}
+ if (type) free(type);
}
+
if (node->call.callee->type == NODE_EXPR_VAR)
{
ASTNode *def = find_struct_def(ctx, node->call.callee->var_ref.name);
@@ -594,26 +679,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
if (node->call.arg_names && node->call.callee->type == NODE_EXPR_VAR)
{
- char *fn_name = node->call.callee->var_ref.name;
- FuncSig *sig = find_func(ctx, fn_name);
-
- if (sig && sig->arg_types)
- {
- for (int p = 0; p < sig->total_args; p++)
- {
- ASTNode *arg = node->call.args;
-
- for (int i = 0; i < node->call.arg_count && arg; i++, arg = arg->next)
- {
- if (node->call.arg_names[i] && p < node->call.arg_count)
- {
-
- // For now, emit in order provided...
- }
- }
- }
- }
-
ASTNode *arg = node->call.args;
int first = 1;
while (arg)
@@ -660,11 +725,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
strncmp(param_t->name, "Tuple_", 6) == 0 && sig->total_args == 1 &&
node->call.arg_count > 1)
{
- // Implicit Tuple Packing:
- // Function expects 1 Tuple argument, but call has multiple args -> Pack
- // them
fprintf(out, "(%s){", param_t->name);
-
ASTNode *curr = arg;
int first_field = 1;
while (curr)
@@ -679,8 +740,6 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
fprintf(out, "}");
handled = 1;
-
- // Advance main loop iterator to end
arg = NULL;
}
}
@@ -689,7 +748,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
if (arg == NULL)
{
- break; // Tuple packed all args
+ break;
}
}
else
@@ -714,16 +773,12 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
case NODE_EXPR_MEMBER:
if (strcmp(node->member.field, "len") == 0)
{
- if (node->member.target->type_info)
+ if (node->member.target->type_info &&
+ node->member.target->type_info->kind == TYPE_ARRAY &&
+ node->member.target->type_info->array_size > 0)
{
- if (node->member.target->type_info->kind == TYPE_ARRAY)
- {
- if (node->member.target->type_info->array_size > 0)
- {
- fprintf(out, "%d", node->member.target->type_info->array_size);
- break;
- }
- }
+ fprintf(out, "%d", node->member.target->type_info->array_size);
+ break;
}
}
@@ -746,17 +801,14 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
else
{
codegen_expression(ctx, node->member.target, out);
- // Verify actual type instead of trusting is_pointer_access flag
char *lt = infer_type(ctx, node->member.target);
int actually_ptr = 0;
if (lt && (lt[strlen(lt) - 1] == '*' || strstr(lt, "*")))
{
actually_ptr = 1;
}
- if (lt)
- {
- free(lt);
- }
+ if (lt) free(lt);
+
char *field = node->member.field;
if (field && field[0] >= '0' && field[0] <= '9')
{
@@ -779,7 +831,8 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
is_slice_struct = 1;
}
}
- if (node->index.array->resolved_type)
+
+ if (!is_slice_struct && node->index.array->resolved_type)
{
if (strncmp(node->index.array->resolved_type, "Slice_", 6) == 0)
{
@@ -794,10 +847,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
is_slice_struct = 1;
}
- if (inferred)
- {
- free(inferred);
- }
+ if (inferred) free(inferred);
}
if (is_slice_struct)
@@ -896,7 +946,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, "/* UNSAFE: Full Slice on unknown size */ 0; ");
+ fprintf(out, "0; ");
}
}
@@ -912,6 +962,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
fprintf(out, "(Slice_%s){ .data = _arr + _start, .len = _len, .cap = _len }; })",
tname);
}
+ if (tname && strcmp(tname, "unknown") != 0) free(tname);
break;
}
case NODE_BLOCK:
@@ -1002,9 +1053,7 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
break;
case NODE_PLUGIN:
{
- // Plugin registry - declare external plugins
ZPlugin *found = zptr_find_plugin(node->plugin_stmt.plugin_name);
-
if (found)
{
ZApi api = {.filename = g_current_filename ? g_current_filename : "input.zc",
@@ -1084,9 +1133,9 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
break;
}
case NODE_EXPR_CAST:
- fprintf(out, "(%s)(", node->cast.target_type);
+ fprintf(out, "((%s)(", node->cast.target_type);
codegen_expression(ctx, node->cast.expr, out);
- fprintf(out, ")");
+ fprintf(out, "))");
break;
case NODE_EXPR_SIZEOF:
if (node->size_of.target_type)
@@ -1117,20 +1166,19 @@ void codegen_expression(ParserContext *ctx, ASTNode *node, FILE *out)
{
Type *t = node->reflection.target_type;
if (node->reflection.kind == 0)
- { // @type_name
+ {
char *s = codegen_type_to_string(t);
fprintf(out, "\"%s\"", s);
free(s);
}
else
- { // @fields
+ {
if (t->kind != TYPE_STRUCT || !t->name)
{
fprintf(out, "((void*)0)");
break;
}
char *sname = t->name;
- // Find definition
ASTNode *def = find_struct_def(ctx, sname);
if (!def)
{
diff --git a/src/codegen/codegen.h b/src/codegen/codegen.h
index b3e971d..89614f7 100644
--- a/src/codegen/codegen.h
+++ b/src/codegen/codegen.h
@@ -48,7 +48,7 @@ char *replace_string_type(const char *args);
const char *parse_original_method_name(const char *mangled);
void emit_auto_type(ParserContext *ctx, ASTNode *init_expr, Token t, FILE *out);
char *codegen_type_to_string(Type *t);
-void emit_func_signature(FILE *out, ASTNode *func, const char *name_override);
+void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override);
char *strip_template_suffix(const char *name);
int emit_move_invalidation(ParserContext *ctx, ASTNode *node, FILE *out);
void codegen_expression_with_move(ParserContext *ctx, ASTNode *node, FILE *out);
@@ -66,7 +66,7 @@ void emit_trait_defs(ASTNode *node, FILE *out);
void emit_enum_protos(ASTNode *node, FILE *out);
void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out);
void emit_lambda_defs(ParserContext *ctx, FILE *out);
-void emit_protos(ASTNode *node, FILE *out);
+void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out);
void emit_impl_vtables(ParserContext *ctx, FILE *out);
/**
diff --git a/src/codegen/codegen_decl.c b/src/codegen/codegen_decl.c
index 5fb9f54..31bd2ee 100644
--- a/src/codegen/codegen_decl.c
+++ b/src/codegen/codegen_decl.c
@@ -3,6 +3,7 @@
#include "../parser/parser.h"
#include "../zprep.h"
#include "codegen.h"
+#include "compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -12,7 +13,7 @@ static void emit_freestanding_preamble(FILE *out)
fputs("#include <stddef.h>\n#include <stdint.h>\n#include "
"<stdbool.h>\n#include <stdarg.h>\n",
out);
- fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+ fputs(ZC_TCC_COMPAT_STR, out);
fputs("typedef size_t usize;\ntypedef char* string;\n", out);
fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 "
"int16_t\n#define U16 uint16_t\n",
@@ -49,6 +50,7 @@ void emit_preamble(ParserContext *ctx, FILE *out)
else
{
// Standard hosted preamble.
+ fputs("#define _GNU_SOURCE\n", out);
fputs("#include <stdio.h>\n#include <stdlib.h>\n#include "
"<stddef.h>\n#include <string.h>\n",
out);
@@ -84,20 +86,18 @@ void emit_preamble(ParserContext *ctx, FILE *out)
else
{
// C mode
+ fputs("#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202300L\n", out);
+ fputs("#define ZC_AUTO auto\n", out);
+ fputs("#else\n", out);
fputs("#define ZC_AUTO __auto_type\n", out);
+ fputs("#endif\n", out);
fputs("#define ZC_CAST(T, x) ((T)(x))\n", out);
- fputs("#ifdef __TINYC__\n#define __auto_type __typeof__\n#endif\n", out);
+ fputs(ZC_TCC_COMPAT_STR, out);
fputs("static inline const char* _z_bool_str(_Bool b) { return b ? \"true\" : "
"\"false\"; }\n",
out);
- fputs("#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", "
- "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", "
- "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", "
- "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", "
- "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", "
- "char*: \"%s\", void*: \"%p\")\n",
- out);
- fputs("#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x), default: (x))\n", out);
+ fputs(ZC_C_GENERIC_STR, out);
+ fputs(ZC_C_ARG_GENERIC_STR, out);
}
fputs("typedef size_t usize;\ntypedef char* string;\n", out);
@@ -107,12 +107,11 @@ void emit_preamble(ParserContext *ctx, FILE *out)
fputs("typedef struct { pthread_t thread; void *result; } Async;\n", out);
}
fputs("typedef struct { void *func; void *ctx; } z_closure_T;\n", out);
- fputs("#define U0 void\n#define I8 int8_t\n#define U8 uint8_t\n#define I16 "
- "int16_t\n#define U16 uint16_t\n",
+ fputs("typedef void U0;\ntypedef int8_t I8;\ntypedef uint8_t U8;\ntypedef "
+ "int16_t I16;\ntypedef uint16_t U16;\n",
out);
- fputs("#define I32 int32_t\n#define U32 uint32_t\n#define I64 "
- "int64_t\n#define U64 "
- "uint64_t\n",
+ fputs("typedef int32_t I32;\ntypedef uint32_t U32;\ntypedef int64_t I64;\ntypedef "
+ "uint64_t U64;\n",
out);
fputs("#define F32 float\n#define F64 double\n", out);
@@ -700,7 +699,7 @@ void emit_globals(ParserContext *ctx, ASTNode *node, FILE *out)
}
// Emit function prototypes
-void emit_protos(ASTNode *node, FILE *out)
+void emit_protos(ParserContext *ctx, ASTNode *node, FILE *out)
{
ASTNode *f = node;
while (f)
@@ -723,7 +722,7 @@ void emit_protos(ASTNode *node, FILE *out)
}
else
{
- emit_func_signature(out, f, NULL);
+ emit_func_signature(ctx, out, f, NULL);
fprintf(out, ";\n");
}
}
@@ -801,7 +800,7 @@ void emit_protos(ASTNode *node, FILE *out)
}
else
{
- emit_func_signature(out, m, proto);
+ emit_func_signature(ctx, out, m, proto);
fprintf(out, ";\n");
}
diff --git a/src/codegen/codegen_main.c b/src/codegen/codegen_main.c
index a140070..82fc3ce 100644
--- a/src/codegen/codegen_main.c
+++ b/src/codegen/codegen_main.c
@@ -448,6 +448,39 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out)
emit_type_aliases(kids, out); // Emit local aliases (redundant but safe)
emit_trait_defs(kids, out);
+ // Also emit traits from parsed_globals_list (from auto-imported files like std/mem.zc)
+ // but only if they weren't already emitted from kids
+ StructRef *trait_ref = ctx->parsed_globals_list;
+ while (trait_ref)
+ {
+ if (trait_ref->node && trait_ref->node->type == NODE_TRAIT)
+ {
+ // Check if this trait was already in kids (explicitly imported)
+ int already_in_kids = 0;
+ ASTNode *k = kids;
+ while (k)
+ {
+ if (k->type == NODE_TRAIT && k->trait.name && trait_ref->node->trait.name &&
+ strcmp(k->trait.name, trait_ref->node->trait.name) == 0)
+ {
+ already_in_kids = 1;
+ break;
+ }
+ k = k->next;
+ }
+
+ if (!already_in_kids)
+ {
+ // Create a temporary single-node list for emit_trait_defs
+ ASTNode *saved_next = trait_ref->node->next;
+ trait_ref->node->next = NULL;
+ emit_trait_defs(trait_ref->node, out);
+ trait_ref->node->next = saved_next;
+ }
+ }
+ trait_ref = trait_ref->next;
+ }
+
// Track emitted raw statements to prevent duplicates
EmittedContent *emitted_raw = NULL;
@@ -616,7 +649,7 @@ void codegen_node(ParserContext *ctx, ASTNode *node, FILE *out)
}
}
- emit_protos(merged_funcs, out);
+ emit_protos(ctx, merged_funcs, out);
emit_impl_vtables(ctx, out);
diff --git a/src/codegen/codegen_stmt.c b/src/codegen/codegen_stmt.c
index bd0c816..7828ecf 100644
--- a/src/codegen/codegen_stmt.c
+++ b/src/codegen/codegen_stmt.c
@@ -750,7 +750,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
{
fprintf(out, "inline ");
}
- emit_func_signature(out, node, NULL);
+ emit_func_signature(ctx, out, node, NULL);
fprintf(out, "\n");
fprintf(out, "{\n");
char *prev_ret = g_current_func_ret_type;
@@ -884,8 +884,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[0], id,
- check);
+ fprintf(out, " ZC_AUTO %s = _tmp_%d.%s;\n", node->destruct.names[0], id, check);
}
}
else
@@ -903,8 +902,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, " __auto_type %s = _tmp_%d.%s;\n", node->destruct.names[i],
- id, field);
+ fprintf(out, " ZC_AUTO %s = _tmp_%d.%s;\n", node->destruct.names[i], id,
+ field);
}
}
else
@@ -916,8 +915,8 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, " __auto_type %s = _tmp_%d.v%d;\n", node->destruct.names[i],
- id, i);
+ fprintf(out, " ZC_AUTO %s = _tmp_%d.v%d;\n", node->destruct.names[i], id,
+ i);
}
}
}
@@ -1528,7 +1527,7 @@ void codegen_node_single(ParserContext *ctx, ASTNode *node, FILE *out)
}
else
{
- fprintf(out, "__auto_type");
+ fprintf(out, "ZC_AUTO");
}
fprintf(out, " _z_ret_mv = ");
codegen_expression(ctx, node->ret.value, out);
diff --git a/src/codegen/codegen_utils.c b/src/codegen/codegen_utils.c
index 8de3cf6..08707cc 100644
--- a/src/codegen/codegen_utils.c
+++ b/src/codegen/codegen_utils.c
@@ -41,7 +41,7 @@ char *strip_template_suffix(const char *name)
}
// Helper to emit C declaration (handle arrays, function pointers correctly)
-void emit_c_decl(FILE *out, const char *type_str, const char *name)
+void emit_c_decl(ParserContext *ctx, FILE *out, const char *type_str, const char *name)
{
char *bracket = strchr(type_str, '[');
char *generic = strchr(type_str, '<');
@@ -64,9 +64,34 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name)
}
else if (generic && (!bracket || generic < bracket))
{
- // Strip generic part for C output
- int base_len = generic - type_str;
- fprintf(out, "%.*s %s", base_len, type_str, name);
+ char mangled_candidate[256];
+ char *gt = strchr(generic, '>');
+ int success = 0;
+
+ if (gt)
+ {
+ int base_len = generic - type_str;
+ int arg_len = gt - generic - 1;
+
+ // Limit check
+ if (base_len + arg_len + 2 < 256)
+ {
+ snprintf(mangled_candidate, 256, "%.*s_%.*s", base_len, type_str, arg_len,
+ generic + 1);
+
+ if (find_struct_def_codegen(ctx, mangled_candidate))
+ {
+ fprintf(out, "%s %s", mangled_candidate, name);
+ success = 1;
+ }
+ }
+ }
+
+ if (!success)
+ {
+ int base_len = generic - type_str;
+ fprintf(out, "%.*s %s", base_len, type_str, name);
+ }
if (bracket)
{
@@ -87,8 +112,7 @@ void emit_c_decl(FILE *out, const char *type_str, const char *name)
// Helper to emit variable declarations with array types.
void emit_var_decl_type(ParserContext *ctx, FILE *out, const char *type_str, const char *var_name)
{
- (void)ctx;
- emit_c_decl(out, type_str, var_name);
+ emit_c_decl(ctx, out, type_str, var_name);
}
// Find struct definition
@@ -411,6 +435,10 @@ char *infer_type(ParserContext *ctx, ASTNode *node)
char *inner = infer_type(ctx, node->unary.operand);
if (inner)
{
+ if (strcmp(inner, "string") == 0)
+ {
+ return xstrdup("char");
+ }
char *ptr = strchr(inner, '*');
if (ptr)
{
@@ -640,7 +668,7 @@ char *codegen_type_to_string(Type *t)
}
// Emit function signature using Type info for correct C codegen
-void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
+void emit_func_signature(ParserContext *ctx, FILE *out, ASTNode *func, const char *name_override)
{
if (!func || func->type != NODE_FUNCTION)
{
@@ -710,7 +738,12 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
}
char *type_str = NULL;
- if (func->func.arg_types && func->func.arg_types[i])
+ // Check for @ctype override first
+ if (func->func.c_type_overrides && func->func.c_type_overrides[i])
+ {
+ type_str = xstrdup(func->func.c_type_overrides[i]);
+ }
+ else if (func->func.arg_types && func->func.arg_types[i])
{
type_str = codegen_type_to_string(func->func.arg_types[i]);
}
@@ -720,13 +753,14 @@ void emit_func_signature(FILE *out, ASTNode *func, const char *name_override)
}
const char *name = "";
+
if (func->func.param_names && func->func.param_names[i])
{
name = func->func.param_names[i];
}
// check if array type
- emit_c_decl(out, type_str, name);
+ emit_c_decl(ctx, out, type_str, name);
free(type_str);
}
if (func->func.is_varargs)
diff --git a/src/codegen/compat.h b/src/codegen/compat.h
index f2d221a..f8d9a4e 100644
--- a/src/codegen/compat.h
+++ b/src/codegen/compat.h
@@ -14,7 +14,11 @@
#define ZC_EXTERN_C_END }
#else
/* C mode */
-#define ZC_AUTO __auto_type ///< Auto type inference.
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202300L
+#define ZC_AUTO auto ///< C23 standard auto.
+#else
+#define ZC_AUTO __auto_type ///< GCC/Clang extension.
+#endif
#define ZC_CAST(T, x) ((T)(x)) ///< Explicit cast.
#define ZC_REINTERPRET(T, x) ((T)(x)) ///< Reinterpret cast.
#define ZC_EXTERN_C ///< Extern "C" (no-op in C).
@@ -22,6 +26,58 @@
#define ZC_EXTERN_C_END
#endif
+#ifdef __TINYC__
+/* TCC compatibility */
+#ifndef __auto_type
+#define __auto_type __typeof__
+#endif
+
+#ifndef __builtin_expect
+#define __builtin_expect(x, v) (x)
+#endif
+
+#ifndef __builtin_unreachable
+#define __builtin_unreachable()
+#endif
+#endif
+
+/* Centralized string definition for codegen emission */
+#define ZC_TCC_COMPAT_STR \
+ "#ifdef __TINYC__\n" \
+ "#ifndef __auto_type\n" \
+ "#define __auto_type __typeof__\n" \
+ "#endif\n" \
+ "\n" \
+ "#ifndef __builtin_expect\n" \
+ "#define __builtin_expect(x, v) (x)\n" \
+ "#endif\n" \
+ "\n" \
+ "#ifndef __builtin_unreachable\n" \
+ "#define __builtin_unreachable()\n" \
+ "#endif\n" \
+ "#endif\n"
+
+/* Generic selection string for C mode */
+#define ZC_C_GENERIC_STR \
+ "#ifdef __OBJC__\n" \
+ "#define _z_objc_map ,id: \"%s\", Class: \"%s\", SEL: \"%s\"\n" \
+ "#define _z_objc_arg_map(x) ,id: [(id)(x) description].UTF8String, Class: " \
+ "class_getName((Class)(x)), SEL: sel_getName((SEL)(x))\n" \
+ "#else\n" \
+ "#define _z_objc_map\n" \
+ "#define _z_objc_arg_map(x)\n" \
+ "#endif\n" \
+ "\n" \
+ "#define _z_str(x) _Generic((x), _Bool: \"%s\", char: \"%c\", " \
+ "signed char: \"%c\", unsigned char: \"%u\", short: \"%d\", " \
+ "unsigned short: \"%u\", int: \"%d\", unsigned int: \"%u\", " \
+ "long: \"%ld\", unsigned long: \"%lu\", long long: \"%lld\", " \
+ "unsigned long long: \"%llu\", float: \"%f\", double: \"%f\", " \
+ "char*: \"%s\", void*: \"%p\" _z_objc_map)\n"
+
+#define ZC_C_ARG_GENERIC_STR \
+ "#define _z_arg(x) _Generic((x), _Bool: _z_bool_str(x) _z_objc_arg_map(x), default: (x))\n"
+
#ifdef __cplusplus
#include <type_traits>
@@ -95,6 +151,32 @@ inline const char *_zc_fmt(void *)
}
#define _z_str(x) _zc_fmt(x)
+
+#ifdef __OBJC__
+#include <objc/objc.h>
+#include <objc/runtime.h>
+#include <objc/message.h> // for direct calls if needed, but [x description] is fine
+
+inline const char *_zc_fmt(id x)
+{
+ return [[x description] UTF8String];
+}
+inline const char *_zc_fmt(Class x)
+{
+ return class_getName(x);
+}
+inline const char *_zc_fmt(SEL x)
+{
+ return sel_getName(x);
+}
+// BOOL is signed char usually, already handled?
+// "typedef signed char BOOL;" on standard apple headers.
+// If it maps to signed char, `_zc_fmt(signed char)` handles it ("%c").
+// We might want "YES"/"NO" for BOOL.
+// But we can't distinguish typedefs in C++ function overloads easily if underlying type is same.
+// We'll leave BOOL as %c or %d for now to avoid ambiguity errors.
+#endif
+
#endif
#endif