summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast/ast.c17
-rw-r--r--src/ast/ast.h10
-rw-r--r--src/lexer/token.c4
-rw-r--r--src/lsp/lsp_analysis.c71
-rw-r--r--src/lsp/lsp_index.c52
-rw-r--r--src/parser/parser.h26
-rw-r--r--src/parser/parser_core.c30
-rw-r--r--src/parser/parser_decl.c64
-rw-r--r--src/parser/parser_expr.c133
-rw-r--r--src/parser/parser_struct.c12
-rw-r--r--src/parser/parser_type.c19
-rw-r--r--src/parser/parser_utils.c14
-rw-r--r--src/zprep.h1
13 files changed, 419 insertions, 34 deletions
diff --git a/src/ast/ast.c b/src/ast/ast.c
index 0799845..f4922a6 100644
--- a/src/ast/ast.c
+++ b/src/ast/ast.c
@@ -168,6 +168,18 @@ int type_eq(Type *a, Type *b)
{
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 || a->kind == TYPE_ARRAY)
{
return type_eq(a->inner, b->inner);
@@ -340,6 +352,8 @@ static char *type_to_string_impl(Type *t)
}
return xstrdup(t->name);
}
+ case TYPE_ALIAS:
+ return xstrdup(t->name);
default:
return xstrdup("unknown");
@@ -524,6 +538,9 @@ static char *type_to_c_string_impl(Type *t)
case TYPE_GENERIC:
return xstrdup(t->name);
+ case TYPE_ALIAS:
+ return type_to_c_string(t->inner);
+
case TYPE_ENUM:
return xstrdup(t->name);
diff --git a/src/ast/ast.h b/src/ast/ast.h
index b272cae..a868bf0 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -58,6 +58,7 @@ typedef enum
TYPE_ARRAY, ///< Fixed size array [N].
TYPE_FUNCTION, ///< Function pointer or reference.
TYPE_GENERIC, ///< Generic type parameter (T).
+ TYPE_ALIAS, ///< Opaque type alias.
TYPE_UNKNOWN ///< Unknown/unresolved type.
} TypeKind;
@@ -84,6 +85,11 @@ typedef struct Type
int has_drop; ///< 1 if type implements Drop trait (RAII).
int has_iterable; ///< 1 if type implements Iterable trait.
} traits;
+ struct
+ {
+ int is_opaque_alias;
+ char *alias_defined_in_file;
+ } alias;
};
} Type;
@@ -263,6 +269,8 @@ struct ASTNode
{
char *alias;
char *original_type;
+ int is_opaque;
+ char *defined_in_file;
} type_alias;
struct
@@ -436,6 +444,8 @@ struct ASTNode
Attribute *attributes; // Custom attributes
char **used_structs; // Names of structs used/mixed-in
int used_struct_count;
+ int is_opaque;
+ char *defined_in_file; // File where the struct is defined (for privacy check)
} strct;
struct
diff --git a/src/lexer/token.c b/src/lexer/token.c
index 6696a5c..095815d 100644
--- a/src/lexer/token.c
+++ b/src/lexer/token.c
@@ -201,6 +201,10 @@ Token lexer_next(Lexer *l)
{
return (Token){TOK_OR, s, 2, start_line, start_col};
}
+ if (len == 6 && strncmp(s, "opaque", 6) == 0)
+ {
+ return (Token){TOK_OPAQUE, s, 6, start_line, start_col};
+ }
// F-Strings
if (len == 1 && s[0] == 'f' && s[1] == '"')
diff --git a/src/lsp/lsp_analysis.c b/src/lsp/lsp_analysis.c
index 088bede..0367d93 100644
--- a/src/lsp/lsp_analysis.c
+++ b/src/lsp/lsp_analysis.c
@@ -454,11 +454,80 @@ void lsp_completion(const char *uri, int line, int col, int id)
cJSON_AddStringToObject(item, "label", s->name);
cJSON_AddNumberToObject(item, "kind", 22); // Struct
char detail[256];
- sprintf(detail, "struct %s", s->name);
+ sprintf(detail, "%sstruct %s",
+ (s->node && s->node->type == NODE_STRUCT && s->node->strct.is_opaque)
+ ? "opaque "
+ : "",
+ s->name);
cJSON_AddStringToObject(item, "detail", detail);
cJSON_AddItemToArray(items, item);
s = s->next;
}
+
+ // Globals and Constants
+ StructRef *g = g_project->ctx->parsed_globals_list;
+ while (g)
+ {
+ if (g->node)
+ {
+ cJSON *item = cJSON_CreateObject();
+ char *name =
+ (g->node->type == NODE_CONST) ? g->node->var_decl.name : g->node->var_decl.name;
+ cJSON_AddStringToObject(item, "label", name);
+ cJSON_AddNumberToObject(item, "kind", 21); // Constant/Variable
+ char detail[256];
+ sprintf(detail, "%s %s", (g->node->type == NODE_CONST) ? "const" : "var", name);
+ cJSON_AddStringToObject(item, "detail", detail);
+ cJSON_AddItemToArray(items, item);
+ }
+ g = g->next;
+ }
+
+ // Enums
+ StructRef *e = g_project->ctx->parsed_enums_list;
+ while (e)
+ {
+ if (e->node)
+ {
+ cJSON *item = cJSON_CreateObject();
+ cJSON_AddStringToObject(item, "label", e->node->enm.name);
+ cJSON_AddNumberToObject(item, "kind", 13); // Enum
+ char detail[256];
+ sprintf(detail, "enum %s", e->node->enm.name);
+ cJSON_AddStringToObject(item, "detail", detail);
+ cJSON_AddItemToArray(items, item);
+ }
+ e = e->next;
+ }
+
+ // Type Aliases
+ TypeAlias *ta = g_project->ctx->type_aliases;
+ while (ta)
+ {
+ cJSON *item = cJSON_CreateObject();
+ cJSON_AddStringToObject(item, "label", ta->alias);
+ cJSON_AddNumberToObject(item, "kind", 8); // Interface/Reference
+ char detail[256];
+ sprintf(detail, "alias %s = %s", ta->alias, ta->original_type);
+ cJSON_AddStringToObject(item, "detail", detail);
+ cJSON_AddItemToArray(items, item);
+ ta = ta->next;
+ }
+
+ // Keywords
+ const char *keywords[] = {
+ "fn", "struct", "enum", "alias", "return", "if", "else", "for", "while",
+ "break", "continue", "true", "false", "int", "char", "bool", "string", "void",
+ "import", "module", "test", "assert", "defer", "sizeof", "opaque", "unsafe", "asm",
+ "trait", "impl", "u8", "u16", "u32", "u64", "i8", "i16", "i32",
+ "i64", "f32", "f64", "usize", "isize", "const", "var", NULL};
+ for (int i = 0; keywords[i]; i++)
+ {
+ cJSON *item = cJSON_CreateObject();
+ cJSON_AddStringToObject(item, "label", keywords[i]);
+ cJSON_AddNumberToObject(item, "kind", 14); // Keyword
+ cJSON_AddItemToArray(items, item);
+ }
}
cJSON_AddItemToObject(root, "result", items);
diff --git a/src/lsp/lsp_index.c b/src/lsp/lsp_index.c
index 285dec3..975e153 100644
--- a/src/lsp/lsp_index.c
+++ b/src/lsp/lsp_index.c
@@ -151,7 +151,32 @@ void lsp_walk_node(LSPIndex *idx, ASTNode *node)
else if (node->type == NODE_STRUCT)
{
char hover[256];
- sprintf(hover, "struct %s", node->strct.name);
+ if (node->strct.is_opaque)
+ {
+ sprintf(hover, "opaque struct %s", node->strct.name);
+ }
+ else
+ {
+ sprintf(hover, "struct %s", node->strct.name);
+ }
+ lsp_index_add_def(idx, node->token, hover, node);
+ }
+ else if (node->type == NODE_ENUM)
+ {
+ char hover[256];
+ sprintf(hover, "enum %s", node->enm.name);
+ lsp_index_add_def(idx, node->token, hover, node);
+ }
+ else if (node->type == NODE_TYPE_ALIAS)
+ {
+ char hover[256];
+ sprintf(hover, "alias %s = %s", node->type_alias.alias, node->type_alias.original_type);
+ lsp_index_add_def(idx, node->token, hover, node);
+ }
+ else if (node->type == NODE_TRAIT)
+ {
+ char hover[256];
+ sprintf(hover, "trait %s", node->trait.name);
lsp_index_add_def(idx, node->token, hover, node);
}
@@ -196,6 +221,31 @@ void lsp_walk_node(LSPIndex *idx, ASTNode *node)
lsp_walk_node(idx, node->call.callee);
lsp_walk_node(idx, node->call.args);
break;
+ case NODE_MATCH:
+ lsp_walk_node(idx, node->match_stmt.expr);
+ lsp_walk_node(idx, node->match_stmt.cases);
+ break;
+ case NODE_MATCH_CASE:
+ lsp_walk_node(idx, node->match_case.guard);
+ lsp_walk_node(idx, node->match_case.body);
+ break;
+ case NODE_FOR:
+ lsp_walk_node(idx, node->for_stmt.init);
+ lsp_walk_node(idx, node->for_stmt.condition);
+ lsp_walk_node(idx, node->for_stmt.step);
+ lsp_walk_node(idx, node->for_stmt.body);
+ break;
+ case NODE_FOR_RANGE:
+ lsp_walk_node(idx, node->for_range.start);
+ lsp_walk_node(idx, node->for_range.end);
+ lsp_walk_node(idx, node->for_range.body);
+ break;
+ case NODE_LOOP:
+ lsp_walk_node(idx, node->loop_stmt.body);
+ break;
+ case NODE_DEFER:
+ lsp_walk_node(idx, node->defer_stmt.stmt);
+ break;
default:
break;
}
diff --git a/src/parser/parser.h b/src/parser/parser.h
index cf57971..262c359 100644
--- a/src/parser/parser.h
+++ b/src/parser/parser.h
@@ -261,6 +261,8 @@ typedef struct TypeAlias
char *alias; ///< New type name.
char *original_type; ///< Original type.
struct TypeAlias *next;
+ int is_opaque;
+ char *defined_in_file;
} TypeAlias;
/**
@@ -514,12 +516,9 @@ char *sanitize_mangled_name(const char *name);
/**
* @brief Registers a type alias.
*/
-void register_type_alias(ParserContext *ctx, const char *alias, const char *original);
-
-/**
- * @brief Finds a type alias.
- */
-const char *find_type_alias(ParserContext *ctx, const char *alias);
+TypeAlias *find_type_alias_node(ParserContext *ctx, const char *name);
+void register_type_alias(ParserContext *ctx, const char *alias, const char *original, int is_opaque,
+ const char *defined_in_file);
/**
* @brief Registers an implementation.
@@ -681,10 +680,6 @@ void register_selective_import(ParserContext *ctx, const char *symbol, const cha
SelectiveImport *find_selective_import(ParserContext *ctx, const char *name);
// Type Aliases
-/**
- * @brief Registers a type alias.
- */
-void register_type_alias(ParserContext *ctx, const char *alias, const char *original);
/**
* @brief Finds a type alias.
@@ -741,6 +736,11 @@ FuncSig *find_func(ParserContext *ctx, const char *name);
Type *parse_type_formal(ParserContext *ctx, Lexer *l);
/**
+ * @brief Checks compatibility of opaque aliases (allows access within defining file).
+ */
+int check_opaque_alias_compat(ParserContext *ctx, Type *a, Type *b);
+
+/**
* @brief Parses a type.
*/
char *parse_type(ParserContext *ctx, Lexer *l);
@@ -889,7 +889,7 @@ ASTNode *parse_def(ParserContext *ctx, Lexer *l);
/**
* @brief Parses a type alias.
*/
-ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l);
+ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l, int is_opaque);
/**
* @brief Parses a function definition.
@@ -899,7 +899,7 @@ ASTNode *parse_function(ParserContext *ctx, Lexer *l, int is_async);
/**
* @brief Parses a struct definition.
*/
-ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union);
+ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque);
/**
* @brief Parses an enum definition.
@@ -963,4 +963,4 @@ char *patch_self_args(const char *args, const char *struct_name);
*/
ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l);
-#endif // PARSER_H
+#endif // PARSER_H \ No newline at end of file
diff --git a/src/parser/parser_core.c b/src/parser/parser_core.c
index ba6f2fe..43137b1 100644
--- a/src/parser/parser_core.c
+++ b/src/parser/parser_core.c
@@ -338,7 +338,7 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
}
else if (0 == strncmp(t.start, "struct", 6) && 6 == t.len)
{
- s = parse_struct(ctx, l, 0);
+ s = parse_struct(ctx, l, 0, 0);
if (s && s->type == NODE_STRUCT)
{
s->strct.is_packed = attr_packed;
@@ -436,7 +436,7 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
}
else if (0 == strncmp(t.start, "type", 4) && 4 == t.len)
{
- s = parse_type_alias(ctx, l);
+ s = parse_type_alias(ctx, l, 0);
}
else if (0 == strncmp(t.start, "raw", 3) && 3 == t.len)
{
@@ -482,9 +482,31 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
lexer_next(l);
}
}
+ else if (t.type == TOK_OPAQUE)
+ {
+ lexer_next(l); // eat opaque
+ Token next = lexer_peek(l);
+ if (0 == strncmp(next.start, "struct", 6) && 6 == next.len)
+ {
+ s = parse_struct(ctx, l, 0, 1);
+ if (s && s->type == NODE_STRUCT)
+ {
+ s->strct.is_packed = attr_packed;
+ s->strct.align = attr_align;
+ }
+ }
+ else if (next.type == TOK_ALIAS)
+ {
+ s = parse_type_alias(ctx, l, 1);
+ }
+ else
+ {
+ zpanic_at(next, "Expected 'struct' or 'alias' after 'opaque'");
+ }
+ }
else if (t.type == TOK_ALIAS)
{
- s = parse_type_alias(ctx, l);
+ s = parse_type_alias(ctx, l, 0);
}
else if (t.type == TOK_ASYNC)
{
@@ -506,7 +528,7 @@ ASTNode *parse_program_nodes(ParserContext *ctx, Lexer *l)
else if (t.type == TOK_UNION)
{
- s = parse_struct(ctx, l, 1);
+ s = parse_struct(ctx, l, 1, 0);
}
else if (t.type == TOK_TRAIT)
{
diff --git a/src/parser/parser_decl.c b/src/parser/parser_decl.c
index 98f46e1..c96ca36 100644
--- a/src/parser/parser_decl.c
+++ b/src/parser/parser_decl.c
@@ -579,6 +579,10 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l)
{
type_obj->inner = init->type_info->inner; // Shallow copy for inner
}
+ if (init->type_info->kind == TYPE_ALIAS)
+ {
+ type_obj->alias = init->type_info->alias;
+ }
// Copy function type args for lambda/closure support
if (init->type_info->args && init->type_info->arg_count > 0)
{
@@ -631,6 +635,59 @@ ASTNode *parse_var_decl(ParserContext *ctx, Lexer *l)
// Register in symbol table with actual token
add_symbol_with_token(ctx, name, type, type_obj, name_tok);
+ if (init && type_obj)
+ {
+ Type *t = init->type_info;
+ if (!t && init->type == NODE_EXPR_VAR)
+ {
+ t = find_symbol_type_info(ctx, init->var_ref.name);
+ }
+
+ // Literal type construction for validation
+ Type *temp_literal_type = NULL;
+ if (!t && init->type == NODE_EXPR_LITERAL)
+ {
+ if (init->literal.type_kind == LITERAL_INT)
+ {
+ temp_literal_type = type_new(TYPE_INT);
+ }
+ else if (init->literal.type_kind == LITERAL_FLOAT)
+ {
+ temp_literal_type = type_new(TYPE_FLOAT);
+ }
+ else if (init->literal.type_kind == LITERAL_STRING)
+ {
+ temp_literal_type = type_new(TYPE_STRING);
+ }
+ else if (init->literal.type_kind == LITERAL_CHAR)
+ {
+ temp_literal_type = type_new(TYPE_CHAR);
+ }
+ t = temp_literal_type;
+ }
+
+ // Special case for literals: if implicit conversion works
+ if (t && !type_eq(type_obj, t))
+ {
+ // Allow integer compatibility if types are roughly ints (lax check in type_eq handles
+ // most, but let's be safe)
+ if (!check_opaque_alias_compat(ctx, type_obj, t))
+ {
+ char *expected = type_to_string(type_obj);
+ char *got = type_to_string(t);
+ zpanic_at(init->token, "Type validation failed. Expected '%s', but got '%s'",
+ expected, got);
+ free(expected);
+ free(got);
+ }
+ }
+
+ if (temp_literal_type)
+ {
+ free(temp_literal_type); // Simple free, shallow
+ }
+ }
+
// NEW: Capture Const Integer Values
if (init && init->type == NODE_EXPR_LITERAL && init->literal.type_kind == LITERAL_INT)
{
@@ -839,7 +896,7 @@ ASTNode *parse_def(ParserContext *ctx, Lexer *l)
return o;
}
-ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l)
+ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l, int is_opaque)
{
lexer_next(l); // consume 'type' or 'alias'
Token n = lexer_next(l);
@@ -859,8 +916,11 @@ ASTNode *parse_type_alias(ParserContext *ctx, Lexer *l)
strncpy(node->type_alias.alias, n.start, n.len);
node->type_alias.alias[n.len] = 0;
node->type_alias.original_type = o;
+ node->type_alias.is_opaque = is_opaque;
+ node->type_alias.defined_in_file = g_current_filename ? xstrdup(g_current_filename) : NULL;
- register_type_alias(ctx, node->type_alias.alias, o);
+ register_type_alias(ctx, node->type_alias.alias, o, is_opaque,
+ node->type_alias.defined_in_file);
return node;
}
diff --git a/src/parser/parser_expr.c b/src/parser/parser_expr.c
index ee1f96f..8f3579a 100644
--- a/src/parser/parser_expr.c
+++ b/src/parser/parser_expr.c
@@ -1,3 +1,43 @@
+#include "../codegen/codegen.h"
+
+int check_opaque_alias_compat(ParserContext *ctx, Type *a, Type *b)
+{
+ if (!a || !b)
+ {
+ return 0;
+ }
+
+ int a_is_opaque = (a->kind == TYPE_ALIAS && a->alias.is_opaque_alias);
+ int b_is_opaque = (b->kind == TYPE_ALIAS && b->alias.is_opaque_alias);
+
+ if (!a_is_opaque && !b_is_opaque)
+ {
+ return 1;
+ }
+
+ if (a_is_opaque)
+ {
+ if (a->alias.alias_defined_in_file && g_current_filename &&
+ strcmp(a->alias.alias_defined_in_file, g_current_filename) == 0)
+ {
+ return check_opaque_alias_compat(ctx, a->inner, b);
+ }
+ return 0;
+ }
+
+ if (b_is_opaque)
+ {
+ if (b->alias.alias_defined_in_file && g_current_filename &&
+ strcmp(b->alias.alias_defined_in_file, g_current_filename) == 0)
+ {
+ return check_opaque_alias_compat(ctx, a, b->inner);
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
#include "../zen/zen_facts.h"
#include "parser.h"
#include <ctype.h>
@@ -1083,6 +1123,7 @@ static ASTNode *create_fstring_block(ParserContext *ctx, const char *content)
static ASTNode *parse_int_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_INT;
node->type_info = type_new(TYPE_INT);
char *s = token_strdup(t);
@@ -1104,6 +1145,7 @@ static ASTNode *parse_int_literal(Token t)
static ASTNode *parse_float_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_FLOAT;
node->literal.float_val = atof(t.start);
node->type_info = type_new(TYPE_F64);
@@ -1136,6 +1178,7 @@ static ASTNode *parse_string_literal(ParserContext *ctx, Token t)
}
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_STRING;
node->literal.string_val = xmalloc(t.len);
strncpy(node->literal.string_val, t.start + 1, t.len - 2);
@@ -1159,6 +1202,7 @@ static ASTNode *parse_fstring_literal(ParserContext *ctx, Token t)
static ASTNode *parse_char_literal(Token t)
{
ASTNode *node = ast_create(NODE_EXPR_LITERAL);
+ node->token = t;
node->literal.type_kind = LITERAL_CHAR;
node->literal.string_val = token_strdup(t);
node->type_info = type_new(TYPE_I8);
@@ -2051,6 +2095,20 @@ ASTNode *parse_primary(ParserContext *ctx, Lexer *l)
sprintf(prefixed, "%s_%s", ctx->current_module_prefix, acc);
struct_name = prefixed;
}
+
+ // Opaque Struct Check
+ ASTNode *def = find_struct_def(ctx, struct_name);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(lexer_peek(l),
+ "Cannot initialize opaque struct '%s' outside its module",
+ struct_name);
+ }
+ }
lexer_next(l);
node = ast_create(NODE_EXPR_STRUCT_INIT);
node->struct_init.struct_name = struct_name;
@@ -4045,6 +4103,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 1;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
if (node->type_info)
{
@@ -4074,6 +4156,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 2;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->type_info = get_field_type(ctx, lhs->type_info, node->member.field);
if (node->type_info)
{
@@ -4653,6 +4759,30 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
+ // Opaque Check
+ int is_ptr_dummy = 0;
+ char *alloc_name = NULL;
+ char *sname =
+ resolve_struct_name_from_type(ctx, lhs->type_info, &is_ptr_dummy, &alloc_name);
+ if (sname)
+ {
+ ASTNode *def = find_struct_def(ctx, sname);
+ if (def && def->type == NODE_STRUCT && def->strct.is_opaque)
+ {
+ if (!def->strct.defined_in_file ||
+ (g_current_filename &&
+ strcmp(def->strct.defined_in_file, g_current_filename) != 0))
+ {
+ zpanic_at(field, "Cannot access private field '%s' of opaque struct '%s'",
+ node->member.field, sname);
+ }
+ }
+ if (alloc_name)
+ {
+ free(alloc_name);
+ }
+ }
+
node->member.field = token_strdup(field);
node->member.is_pointer_access = 0;
@@ -5540,7 +5670,8 @@ ASTNode *parse_expr_prec(ParserContext *ctx, Lexer *l, Precedence min_prec)
}
else
{
- if (type_eq(lhs->type_info, rhs->type_info))
+ if (type_eq(lhs->type_info, rhs->type_info) ||
+ check_opaque_alias_compat(ctx, lhs->type_info, rhs->type_info))
{
bin->type_info = lhs->type_info;
}
diff --git a/src/parser/parser_struct.c b/src/parser/parser_struct.c
index 84450ba..82dd346 100644
--- a/src/parser/parser_struct.c
+++ b/src/parser/parser_struct.c
@@ -652,7 +652,7 @@ ASTNode *parse_impl(ParserContext *ctx, Lexer *l)
}
}
-ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
+ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union, int is_opaque)
{
lexer_next(l); // eat struct or union
@@ -705,6 +705,7 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
n->strct.is_union = is_union;
n->strct.fields = NULL;
n->strct.is_incomplete = 1;
+ n->strct.is_opaque = is_opaque;
return n;
}
@@ -800,10 +801,6 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
ASTNode *nf = ast_create(NODE_FIELD);
nf->field.name = xstrdup(f->field.name);
nf->field.type = xstrdup(f->field.type);
- // Copy type info? Ideally deep copy or ref
- // For now, we leave it NULL or shallow copy if needed, but mixins usually
- // aren't generic params themselves in the same way.
- // Let's shallow copy for safety if it exists.
nf->type_info = f->type_info;
if (!h)
@@ -829,7 +826,6 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
free(use_name);
continue;
}
- // ---------------------------------------
if (t.type == TOK_IDENT)
{
@@ -904,8 +900,10 @@ ASTNode *parse_struct(ParserContext *ctx, Lexer *l, int is_union)
node->strct.generic_params = gps;
node->strct.generic_param_count = gp_count;
node->strct.is_union = is_union;
+ node->strct.is_opaque = is_opaque;
node->strct.used_structs = temp_used_structs;
node->strct.used_struct_count = temp_used_count;
+ node->strct.defined_in_file = g_current_filename ? xstrdup(g_current_filename) : NULL;
if (gp_count > 0)
{
@@ -1102,7 +1100,7 @@ ASTNode *parse_enum(ParserContext *ctx, Lexer *l)
node->enm.name = ename;
node->enm.variants = h;
- node->enm.generic_param = gp; // 3. Store generic param
+ node->enm.generic_param = gp; // Store generic param
if (gp)
{
diff --git a/src/parser/parser_type.c b/src/parser/parser_type.c
index c01f061..65f2848 100644
--- a/src/parser/parser_type.c
+++ b/src/parser/parser_type.c
@@ -33,12 +33,25 @@ Type *parse_type_base(ParserContext *ctx, Lexer *l)
char *name = token_strdup(t);
// Check for alias
- const char *aliased = find_type_alias(ctx, name);
- if (aliased)
+ TypeAlias *alias_node = find_type_alias_node(ctx, name);
+ if (alias_node)
{
free(name);
Lexer tmp;
- lexer_init(&tmp, aliased);
+ lexer_init(&tmp, alias_node->original_type);
+
+ if (alias_node->is_opaque)
+ {
+ Type *underlying = parse_type_formal(ctx, &tmp);
+ Type *wrapper = type_new(TYPE_ALIAS);
+ wrapper->name = xstrdup(alias_node->alias);
+ wrapper->inner = underlying;
+ wrapper->alias.is_opaque_alias = 1;
+ wrapper->alias.alias_defined_in_file =
+ alias_node->defined_in_file ? xstrdup(alias_node->defined_in_file) : NULL;
+ return wrapper;
+ }
+
return parse_type_formal(ctx, &tmp);
}
diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c
index 4e85500..48418b6 100644
--- a/src/parser/parser_utils.c
+++ b/src/parser/parser_utils.c
@@ -402,23 +402,33 @@ void add_to_struct_list(ParserContext *ctx, ASTNode *node)
ctx->parsed_structs_list = r;
}
-void register_type_alias(ParserContext *ctx, const char *alias, const char *original)
+void register_type_alias(ParserContext *ctx, const char *alias, const char *original, int is_opaque,
+ const char *defined_in_file)
{
TypeAlias *ta = xmalloc(sizeof(TypeAlias));
ta->alias = xstrdup(alias);
ta->original_type = xstrdup(original);
+ ta->is_opaque = is_opaque;
+ ta->defined_in_file = defined_in_file ? xstrdup(defined_in_file) : NULL;
ta->next = ctx->type_aliases;
ctx->type_aliases = ta;
}
const char *find_type_alias(ParserContext *ctx, const char *alias)
{
+ TypeAlias *ta = find_type_alias_node(ctx, alias);
+ return ta ? ta->original_type : NULL;
+}
+
+TypeAlias *find_type_alias_node(ParserContext *ctx, const char *alias)
+{
TypeAlias *ta = ctx->type_aliases;
while (ta)
{
if (strcmp(ta->alias, alias) == 0)
{
- return ta->original_type;
+ // printf("DEBUG: Found Alias '%s' (Opaque: %d)\n", alias, ta->is_opaque);
+ return ta;
}
ta = ta->next;
}
diff --git a/src/zprep.h b/src/zprep.h
index e248871..a943f3f 100644
--- a/src/zprep.h
+++ b/src/zprep.h
@@ -108,6 +108,7 @@ typedef enum
TOK_PREPROC, ///< Preprocessor directive (#...).
TOK_ALIAS, ///< 'alias' keyword.
TOK_COMMENT, ///< Comment (usually skipped).
+ TOK_OPAQUE, ///< 'opaque' keyword.
TOK_UNKNOWN ///< Unknown token.
} TokenType;