From b5de2799d0ef9943f039119238727d36e2b5d9e0 Mon Sep 17 00:00:00 2001 From: Arjen de Vos Date: Thu, 26 Feb 2026 22:12:46 +0100 Subject: [PATCH 1/4] feat: add support for private symbols using #local declaration --- nls/src/analyzer/common.rs | 3 ++ nls/src/analyzer/semantic.rs | 2 + nls/src/analyzer/symbol.rs | 1 + nls/src/analyzer/syntax.rs | 48 ++++++++++++++++++- src/ast.h | 3 ++ src/module.c | 4 ++ src/semantic/analyzer.c | 35 ++++++++++---- src/symbol/symbol.h | 1 + src/syntax/parser.c | 34 ++++++++++++- tests/features/20260226_00_local_const_var.c | 15 ++++++ .../cases/20260226_00_local_const_var/main.n | 9 ++++ .../private_module.n | 33 +++++++++++++ 12 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 tests/features/20260226_00_local_const_var.c create mode 100644 tests/features/cases/20260226_00_local_const_var/main.n create mode 100644 tests/features/cases/20260226_00_local_const_var/private_module.n diff --git a/nls/src/analyzer/common.rs b/nls/src/analyzer/common.rs index a9083359..29acefb7 100644 --- a/nls/src/analyzer/common.rs +++ b/nls/src/analyzer/common.rs @@ -1049,6 +1049,7 @@ pub struct VarDeclExpr { pub type_: Type, pub be_capture: bool, pub heap_ident: Option, + pub is_private: bool, } #[derive(Debug, Clone)] @@ -1060,6 +1061,7 @@ pub struct AstConstDef { pub symbol_id: NodeId, pub symbol_start: usize, pub symbol_end: usize, + pub is_private: bool, } #[derive(Debug, Clone)] @@ -1177,6 +1179,7 @@ pub struct TypedefStmt { pub is_tagged_union: bool, pub impl_interfaces: Vec, pub method_table: HashMap>>, // key = ident, value = ast_fndef_t + pub is_private: bool, pub symbol_start: usize, pub symbol_end: usize, diff --git a/nls/src/analyzer/semantic.rs b/nls/src/analyzer/semantic.rs index 360c37bb..37405f52 100644 --- a/nls/src/analyzer/semantic.rs +++ b/nls/src/analyzer/semantic.rs @@ -935,6 +935,7 @@ impl<'a> Semantic<'a> { symbol_start: fndef.symbol_start, symbol_end: fndef.symbol_end, symbol_id: 0, + is_private: false, }; new_params.push(Arc::new(Mutex::new(self_vardecl))); @@ -1740,6 +1741,7 @@ impl<'a> Semantic<'a> { symbol_start: start, symbol_end: end, symbol_id: 0, + is_private: false, })); Box::new(Stmt { diff --git a/nls/src/analyzer/symbol.rs b/nls/src/analyzer/symbol.rs index 130ee4cc..1dc99fee 100644 --- a/nls/src/analyzer/symbol.rs +++ b/nls/src/analyzer/symbol.rs @@ -199,6 +199,7 @@ impl SymbolTable { is_tagged_union: false, impl_interfaces: Vec::new(), method_table: HashMap::new(), + is_private: false, symbol_start: 0, symbol_end: 0, symbol_id: 0, diff --git a/nls/src/analyzer/syntax.rs b/nls/src/analyzer/syntax.rs index 6822f965..a16d884e 100644 --- a/nls/src/analyzer/syntax.rs +++ b/nls/src/analyzer/syntax.rs @@ -1473,6 +1473,7 @@ impl<'a> Syntax { impl_interfaces, method_table: HashMap::new(), symbol_id: 0, + is_private: false, }))); stmt.end = self.prev().unwrap().end; @@ -1524,6 +1525,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, }))) } @@ -1833,6 +1835,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, }; let catch_body = self.parser_body(true)?; @@ -2360,6 +2363,7 @@ impl<'a> Syntax { be_capture: false, symbol_id: 0, heap_ident: None, + is_private: false, }; let second = if self.consume(TokenType::Comma) { @@ -2372,6 +2376,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, }))) } else { None @@ -2926,6 +2931,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, }))); expr.end = self.prev().unwrap().end; expr @@ -2971,6 +2977,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, })), self.parser_expr()?, ); @@ -3004,6 +3011,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, })), self.parser_expr()?, ); @@ -3027,6 +3035,7 @@ impl<'a> Syntax { symbol_start: const_ident.start, symbol_end: const_ident.end, symbol_id: 0, + is_private: false, }))); Ok(stmt) @@ -3321,6 +3330,7 @@ impl<'a> Syntax { fn parser_label(&mut self) -> Result, SyntaxError> { let mut fndef = AstFnDef::default(); + let mut is_private = false; while self.is(TokenType::Label) { let token = self.must(TokenType::Label)?; @@ -3334,6 +3344,7 @@ impl<'a> Syntax { fndef.linkid = Some(literal.literal.clone()); } } else if token.literal == "local" { + is_private = true; fndef.is_private = true; } else if token.literal == "where" { if fndef.pending_where_params.is_some() { @@ -3368,14 +3379,44 @@ impl<'a> Syntax { if fndef.pending_where_params.is_some() { return Err(SyntaxError(self.peek().start, self.peek().end, "#where can only be applied to fn".to_string())); } - self.parser_typedef_stmt() + let result = self.parser_typedef_stmt()?; + if is_private { + if let AstNode::Typedef(ref typedef) = result.node { + typedef.lock().unwrap().is_private = true; + } + } + Ok(result) } else if self.is(TokenType::Fn) { self.parser_fndef_stmt(fndef) + } else if self.is(TokenType::Const) { + let result = self.parser_constdef_stmt()?; + if is_private { + if let AstNode::ConstDef(ref constdef) = result.node { + constdef.lock().unwrap().is_private = true; + } + } + Ok(result) + } else if self.is(TokenType::Var) { + let result = self.parser_var_begin_stmt()?; + if is_private { + if let AstNode::VarDef(ref var_decl, _) = result.node { + var_decl.lock().unwrap().is_private = true; + } + } + Ok(result) + } else if self.is_type_begin_stmt() { + let result = self.parser_type_begin_stmt()?; + if is_private { + if let AstNode::VarDef(ref var_decl, _) = result.node { + var_decl.lock().unwrap().is_private = true; + } + } + Ok(result) } else { Err(SyntaxError( self.peek().start, self.peek().end, - format!("the label can only be used in type alias or fn"), + format!("the label can only be applied to type, fn, const, or var"), )) } } @@ -3731,6 +3772,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, })), call_expr.clone(), ); @@ -3807,6 +3849,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, })); let catch_body = self.parser_body(false)?; @@ -3905,6 +3948,7 @@ impl<'a> Syntax { be_capture: false, heap_ident: None, symbol_id: 0, + is_private: false, }))); } diff --git a/src/ast.h b/src/ast.h index 46974de9..76e098d1 100644 --- a/src/ast.h +++ b/src/ast.h @@ -314,6 +314,7 @@ typedef struct { type_t type; ast_expr_t *right; bool processing; + bool is_private; } ast_constdef_stmt_t; typedef struct { @@ -327,6 +328,7 @@ typedef struct { ast_var_decl_t var_decl; // 左值 ast_expr_t *right; // 右值 uint8_t *global_data; // global_eval pass 生成的编译期初始化数据 + bool is_private; } ast_vardef_stmt_t; typedef struct { @@ -666,6 +668,7 @@ typedef struct { list_t *impl_interfaces; // type_t, typedef 可以实现多个接口, 对于 interface 来说则是自身扩展 struct sc_map_sv method_table; // key = ident, value = ast_fndef_t int64_t hash; + bool is_private; } ast_typedef_stmt_t; // 这里包含 body, 所以属于 def diff --git a/src/module.c b/src/module.c index 7492d95d..356229e6 100644 --- a/src/module.c +++ b/src/module.c @@ -141,6 +141,7 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty var_decl->ident = ident_with_prefix(m->ident, var_decl->ident); symbol_t *s = symbol_table_set(var_decl->ident, SYMBOL_VAR, var_decl, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", var_decl->ident); + s->is_private = vardef->is_private; continue; } @@ -149,6 +150,7 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty const_def->ident = ident_with_prefix(m->ident, const_def->ident); symbol_t *s = symbol_table_set(const_def->ident, SYMBOL_CONST, const_def, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", const_def->ident); + s->is_private = const_def->is_private; continue; } @@ -157,6 +159,7 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty typedef_stmt->ident = ident_with_prefix(m->ident, typedef_stmt->ident); symbol_t *s = symbol_table_set(typedef_stmt->ident, SYMBOL_TYPE, typedef_stmt, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", typedef_stmt->ident); + s->is_private = typedef_stmt->is_private; continue; } @@ -167,6 +170,7 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty fndef->symbol_name = ident_with_prefix(m->ident, fndef->symbol_name); // 全局函数改名 symbol_t *s = symbol_table_set(fndef->symbol_name, SYMBOL_FN, fndef, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", fndef->symbol_name); + s->is_private = fndef->is_private; } else { // Delay to analyzer module and then process it... } diff --git a/src/semantic/analyzer.c b/src/semantic/analyzer.c index f1c44e00..63794b88 100644 --- a/src/semantic/analyzer.c +++ b/src/semantic/analyzer.c @@ -1435,6 +1435,17 @@ static bool analyzer_local_ident(module_t *m, ast_expr_t *expr) { return false; } +/** + * Check if a symbol is marked as private (#local). + * Returns true if the symbol is private and should not be importable. + */ +static bool symbol_is_private(symbol_t *sym) { + if (!sym) { + return false; + } + return sym->is_private; +} + static bool analyzer_as_star_or_builtin_ident(module_t *m, ast_ident *ident) { // - import xxx as * 产生的全局符号 for (int i = 0; i < m->imports->count; ++i) { @@ -1442,7 +1453,12 @@ static bool analyzer_as_star_or_builtin_ident(module_t *m, ast_ident *ident) { if (str_equal(import->as, "*")) { char *temp = ident_with_prefix(import->module_ident, ident->literal); - if (symbol_table_get(temp)) { + symbol_t *sym = symbol_table_get(temp); + if (sym) { + // Check if symbol is private (#local) + if (symbol_is_private(sym)) { + ANALYZER_ASSERTF(false, "cannot access private symbol '%s'", ident->literal); + } ident->literal = temp; return true; } @@ -1493,12 +1509,9 @@ static bool analyzer_ident(module_t *m, ast_expr_t *expr) { ANALYZER_ASSERTF(false, "symbol '%s' not found in module", select_ref->original_ident); } - // Check if symbol is private (only for functions) - if (sym->type == SYMBOL_FN) { - ast_fndef_t *fndef = sym->ast_value; - if (fndef->is_private) { - ANALYZER_ASSERTF(false, "cannot import private function '%s'", select_ref->original_ident); - } + // Check if symbol is private (#local) + if (symbol_is_private(sym)) { + ANALYZER_ASSERTF(false, "cannot import private symbol '%s'", select_ref->original_ident); } ident->literal = global_ident; @@ -1550,10 +1563,16 @@ static void rewrite_select_expr(module_t *m, ast_expr_t *expr) { char *unique_ident = ident_with_prefix(import->module_ident, select->key); // 检测 import ident 是否存在 - if (!symbol_table_get(unique_ident)) { + symbol_t *sym = symbol_table_get(unique_ident); + if (!sym) { ANALYZER_ASSERTF(false, "identifier '%s' undeclared \n", unique_ident); } + // Check if symbol is private (#local) + if (symbol_is_private(sym)) { + ANALYZER_ASSERTF(false, "cannot access private symbol '%s'", select->key); + } + expr->assert_type = AST_EXPR_IDENT; expr->value = ast_new_ident(unique_ident); return; diff --git a/src/symbol/symbol.h b/src/symbol/symbol.h index be485c2b..fa1b55da 100644 --- a/src/symbol/symbol.h +++ b/src/symbol/symbol.h @@ -63,6 +63,7 @@ typedef enum { typedef struct { string ident; // 符号唯一标识 bool is_local; // 对应 elf 符号中的 global/local, 表示能否被外部链接链接到 + bool is_private; // #local 标记,表示不能被其他模块导入 symbol_type_t type; void *ast_value; // ast_typedef_stmt/ast_var_decl/ast_fndef_t/closure_t/ast_constdef_stmt_t int64_t ref_count; // 引用计数 diff --git a/src/syntax/parser.c b/src/syntax/parser.c index c146562f..b80f2370 100644 --- a/src/syntax/parser.c +++ b/src/syntax/parser.c @@ -2971,6 +2971,7 @@ static ast_stmt_t *parser_test_stmt(module_t *m) { */ static ast_stmt_t *parser_label(module_t *m) { ast_fndef_t *fndef = ast_fndef_new(m, parser_peek(m)->line, parser_peek(m)->column); + bool is_private = false; do { token_t *token = parser_must(m, TOKEN_LABEL); @@ -2983,6 +2984,7 @@ static ast_stmt_t *parser_label(module_t *m) { fndef->linkid = literal->literal; } } else if (str_equal(token->literal, MACRO_LOCAL)) { + is_private = true; fndef->is_private = true; } else if (str_equal(token->literal, MACRO_WHERE)) { PARSER_ASSERTF(!fndef->pending_where_params, "#where redeclared"); @@ -3010,11 +3012,39 @@ static ast_stmt_t *parser_label(module_t *m) { if (parser_is(m, TOKEN_TYPE)) { PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); - return parser_typedef_stmt(m); + ast_stmt_t *result = parser_typedef_stmt(m); + ast_typedef_stmt_t *typedef_stmt = result->value; + typedef_stmt->is_private = is_private; + return result; } else if (parser_is(m, TOKEN_FN)) { return parser_fndef_stmt(m, fndef); + } else if (parser_is(m, TOKEN_CONST)) { + PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); + PARSER_ASSERTF(!fndef->linkid, "#linkid can only be applied to fn"); + ast_stmt_t *result = parser_constdef_stmt(m); + ast_constdef_stmt_t *constdef = result->value; + constdef->is_private = is_private; + return result; + } else if (parser_is(m, TOKEN_VAR)) { + PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); + PARSER_ASSERTF(!fndef->linkid, "#linkid can only be applied to fn"); + ast_stmt_t *result = parser_var_begin_stmt(m); + if (result->assert_type == AST_STMT_VARDEF) { + ast_vardef_stmt_t *vardef = result->value; + vardef->is_private = is_private; + } + return result; + } else if (is_type_begin_stmt(m)) { + PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); + PARSER_ASSERTF(!fndef->linkid, "#linkid can only be applied to fn"); + ast_stmt_t *result = parser_type_begin_stmt(m); + if (result->assert_type == AST_STMT_VARDEF) { + ast_vardef_stmt_t *vardef = result->value; + vardef->is_private = is_private; + } + return result; } else { - PARSER_ASSERTF(false, "the label can only be applied to type alias or fn."); + PARSER_ASSERTF(false, "the label can only be applied to type, fn, const, or var"); } } diff --git a/tests/features/20260226_00_local_const_var.c b/tests/features/20260226_00_local_const_var.c new file mode 100644 index 00000000..aa0dccc5 --- /dev/null +++ b/tests/features/20260226_00_local_const_var.c @@ -0,0 +1,15 @@ +#include "tests/test.h" +#include "utils/assertf.h" +#include "utils/exec.h" +#include + +static void test_basic() { + char *raw = exec_output(); + printf("%s", raw); + + assert_string_equal(raw, "PUBLIC_VAR: 100\nPUBLIC_CONST: hello\npublic_fn: 84\nuse_private_type: 3\n"); +} + +int main(void) { + TEST_BASIC +} diff --git a/tests/features/cases/20260226_00_local_const_var/main.n b/tests/features/cases/20260226_00_local_const_var/main.n new file mode 100644 index 00000000..a0de6f3c --- /dev/null +++ b/tests/features/cases/20260226_00_local_const_var/main.n @@ -0,0 +1,9 @@ +import 'private_module.n' as mymod + +fn main() { + // Access public symbols + println('PUBLIC_VAR:', mymod.PUBLIC_VAR) + println('PUBLIC_CONST:', mymod.PUBLIC_CONST) + println('public_fn:', mymod.public_fn()) + println('use_private_type:', mymod.use_private_type()) +} diff --git a/tests/features/cases/20260226_00_local_const_var/private_module.n b/tests/features/cases/20260226_00_local_const_var/private_module.n new file mode 100644 index 00000000..45af4559 --- /dev/null +++ b/tests/features/cases/20260226_00_local_const_var/private_module.n @@ -0,0 +1,33 @@ +// Module with #local const and var declarations + +// Public symbols +int PUBLIC_VAR = 100 +const PUBLIC_CONST = "hello" + +fn public_fn():int { + // can use private symbols within the same module + return PRIVATE_VAR + internal_helper() +} + +// Private symbols - should not be importable +#local +const PRIVATE_CONST = "secret" + +#local +var PRIVATE_VAR = 42 + +#local +fn internal_helper():int { + return PRIVATE_VAR +} + +#local +type private_point = struct { + int x + int y +} + +fn use_private_type():int { + var p = private_point{x: 1, y: 2} + return p.x + p.y +} From 5ab81ea5b2e08c2e7ab1a28833b7e1f2e4e459ca Mon Sep 17 00:00:00 2001 From: Arjen de Vos Date: Fri, 27 Feb 2026 20:44:25 +0100 Subject: [PATCH 2/4] refactor: remove is_private field from various AST structures and related logic --- src/ast.h | 3 -- src/module.c | 4 --- src/semantic/analyzer.c | 14 -------- src/symbol/symbol.h | 1 - src/syntax/parser.c | 34 ++----------------- tests/features/20260226_00_local_const_var.c | 15 -------- .../cases/20260226_00_local_const_var/main.n | 9 ----- .../private_module.n | 33 ------------------ 8 files changed, 2 insertions(+), 111 deletions(-) delete mode 100644 tests/features/20260226_00_local_const_var.c delete mode 100644 tests/features/cases/20260226_00_local_const_var/main.n delete mode 100644 tests/features/cases/20260226_00_local_const_var/private_module.n diff --git a/src/ast.h b/src/ast.h index 76e098d1..46974de9 100644 --- a/src/ast.h +++ b/src/ast.h @@ -314,7 +314,6 @@ typedef struct { type_t type; ast_expr_t *right; bool processing; - bool is_private; } ast_constdef_stmt_t; typedef struct { @@ -328,7 +327,6 @@ typedef struct { ast_var_decl_t var_decl; // 左值 ast_expr_t *right; // 右值 uint8_t *global_data; // global_eval pass 生成的编译期初始化数据 - bool is_private; } ast_vardef_stmt_t; typedef struct { @@ -668,7 +666,6 @@ typedef struct { list_t *impl_interfaces; // type_t, typedef 可以实现多个接口, 对于 interface 来说则是自身扩展 struct sc_map_sv method_table; // key = ident, value = ast_fndef_t int64_t hash; - bool is_private; } ast_typedef_stmt_t; // 这里包含 body, 所以属于 def diff --git a/src/module.c b/src/module.c index 356229e6..7492d95d 100644 --- a/src/module.c +++ b/src/module.c @@ -141,7 +141,6 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty var_decl->ident = ident_with_prefix(m->ident, var_decl->ident); symbol_t *s = symbol_table_set(var_decl->ident, SYMBOL_VAR, var_decl, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", var_decl->ident); - s->is_private = vardef->is_private; continue; } @@ -150,7 +149,6 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty const_def->ident = ident_with_prefix(m->ident, const_def->ident); symbol_t *s = symbol_table_set(const_def->ident, SYMBOL_CONST, const_def, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", const_def->ident); - s->is_private = const_def->is_private; continue; } @@ -159,7 +157,6 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty typedef_stmt->ident = ident_with_prefix(m->ident, typedef_stmt->ident); symbol_t *s = symbol_table_set(typedef_stmt->ident, SYMBOL_TYPE, typedef_stmt, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", typedef_stmt->ident); - s->is_private = typedef_stmt->is_private; continue; } @@ -170,7 +167,6 @@ module_t *module_build(ast_import_t *import, char *source_path, module_type_t ty fndef->symbol_name = ident_with_prefix(m->ident, fndef->symbol_name); // 全局函数改名 symbol_t *s = symbol_table_set(fndef->symbol_name, SYMBOL_FN, fndef, false); ANALYZER_ASSERTF(s, "ident '%s' redeclared", fndef->symbol_name); - s->is_private = fndef->is_private; } else { // Delay to analyzer module and then process it... } diff --git a/src/semantic/analyzer.c b/src/semantic/analyzer.c index 63794b88..8fce9220 100644 --- a/src/semantic/analyzer.c +++ b/src/semantic/analyzer.c @@ -1455,10 +1455,6 @@ static bool analyzer_as_star_or_builtin_ident(module_t *m, ast_ident *ident) { char *temp = ident_with_prefix(import->module_ident, ident->literal); symbol_t *sym = symbol_table_get(temp); if (sym) { - // Check if symbol is private (#local) - if (symbol_is_private(sym)) { - ANALYZER_ASSERTF(false, "cannot access private symbol '%s'", ident->literal); - } ident->literal = temp; return true; } @@ -1509,11 +1505,6 @@ static bool analyzer_ident(module_t *m, ast_expr_t *expr) { ANALYZER_ASSERTF(false, "symbol '%s' not found in module", select_ref->original_ident); } - // Check if symbol is private (#local) - if (symbol_is_private(sym)) { - ANALYZER_ASSERTF(false, "cannot import private symbol '%s'", select_ref->original_ident); - } - ident->literal = global_ident; return true; } @@ -1568,11 +1559,6 @@ static void rewrite_select_expr(module_t *m, ast_expr_t *expr) { ANALYZER_ASSERTF(false, "identifier '%s' undeclared \n", unique_ident); } - // Check if symbol is private (#local) - if (symbol_is_private(sym)) { - ANALYZER_ASSERTF(false, "cannot access private symbol '%s'", select->key); - } - expr->assert_type = AST_EXPR_IDENT; expr->value = ast_new_ident(unique_ident); return; diff --git a/src/symbol/symbol.h b/src/symbol/symbol.h index fa1b55da..be485c2b 100644 --- a/src/symbol/symbol.h +++ b/src/symbol/symbol.h @@ -63,7 +63,6 @@ typedef enum { typedef struct { string ident; // 符号唯一标识 bool is_local; // 对应 elf 符号中的 global/local, 表示能否被外部链接链接到 - bool is_private; // #local 标记,表示不能被其他模块导入 symbol_type_t type; void *ast_value; // ast_typedef_stmt/ast_var_decl/ast_fndef_t/closure_t/ast_constdef_stmt_t int64_t ref_count; // 引用计数 diff --git a/src/syntax/parser.c b/src/syntax/parser.c index b80f2370..c146562f 100644 --- a/src/syntax/parser.c +++ b/src/syntax/parser.c @@ -2971,7 +2971,6 @@ static ast_stmt_t *parser_test_stmt(module_t *m) { */ static ast_stmt_t *parser_label(module_t *m) { ast_fndef_t *fndef = ast_fndef_new(m, parser_peek(m)->line, parser_peek(m)->column); - bool is_private = false; do { token_t *token = parser_must(m, TOKEN_LABEL); @@ -2984,7 +2983,6 @@ static ast_stmt_t *parser_label(module_t *m) { fndef->linkid = literal->literal; } } else if (str_equal(token->literal, MACRO_LOCAL)) { - is_private = true; fndef->is_private = true; } else if (str_equal(token->literal, MACRO_WHERE)) { PARSER_ASSERTF(!fndef->pending_where_params, "#where redeclared"); @@ -3012,39 +3010,11 @@ static ast_stmt_t *parser_label(module_t *m) { if (parser_is(m, TOKEN_TYPE)) { PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); - ast_stmt_t *result = parser_typedef_stmt(m); - ast_typedef_stmt_t *typedef_stmt = result->value; - typedef_stmt->is_private = is_private; - return result; + return parser_typedef_stmt(m); } else if (parser_is(m, TOKEN_FN)) { return parser_fndef_stmt(m, fndef); - } else if (parser_is(m, TOKEN_CONST)) { - PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); - PARSER_ASSERTF(!fndef->linkid, "#linkid can only be applied to fn"); - ast_stmt_t *result = parser_constdef_stmt(m); - ast_constdef_stmt_t *constdef = result->value; - constdef->is_private = is_private; - return result; - } else if (parser_is(m, TOKEN_VAR)) { - PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); - PARSER_ASSERTF(!fndef->linkid, "#linkid can only be applied to fn"); - ast_stmt_t *result = parser_var_begin_stmt(m); - if (result->assert_type == AST_STMT_VARDEF) { - ast_vardef_stmt_t *vardef = result->value; - vardef->is_private = is_private; - } - return result; - } else if (is_type_begin_stmt(m)) { - PARSER_ASSERTF(!fndef->pending_where_params, "#where can only be applied to fn"); - PARSER_ASSERTF(!fndef->linkid, "#linkid can only be applied to fn"); - ast_stmt_t *result = parser_type_begin_stmt(m); - if (result->assert_type == AST_STMT_VARDEF) { - ast_vardef_stmt_t *vardef = result->value; - vardef->is_private = is_private; - } - return result; } else { - PARSER_ASSERTF(false, "the label can only be applied to type, fn, const, or var"); + PARSER_ASSERTF(false, "the label can only be applied to type alias or fn."); } } diff --git a/tests/features/20260226_00_local_const_var.c b/tests/features/20260226_00_local_const_var.c deleted file mode 100644 index aa0dccc5..00000000 --- a/tests/features/20260226_00_local_const_var.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "tests/test.h" -#include "utils/assertf.h" -#include "utils/exec.h" -#include - -static void test_basic() { - char *raw = exec_output(); - printf("%s", raw); - - assert_string_equal(raw, "PUBLIC_VAR: 100\nPUBLIC_CONST: hello\npublic_fn: 84\nuse_private_type: 3\n"); -} - -int main(void) { - TEST_BASIC -} diff --git a/tests/features/cases/20260226_00_local_const_var/main.n b/tests/features/cases/20260226_00_local_const_var/main.n deleted file mode 100644 index a0de6f3c..00000000 --- a/tests/features/cases/20260226_00_local_const_var/main.n +++ /dev/null @@ -1,9 +0,0 @@ -import 'private_module.n' as mymod - -fn main() { - // Access public symbols - println('PUBLIC_VAR:', mymod.PUBLIC_VAR) - println('PUBLIC_CONST:', mymod.PUBLIC_CONST) - println('public_fn:', mymod.public_fn()) - println('use_private_type:', mymod.use_private_type()) -} diff --git a/tests/features/cases/20260226_00_local_const_var/private_module.n b/tests/features/cases/20260226_00_local_const_var/private_module.n deleted file mode 100644 index 45af4559..00000000 --- a/tests/features/cases/20260226_00_local_const_var/private_module.n +++ /dev/null @@ -1,33 +0,0 @@ -// Module with #local const and var declarations - -// Public symbols -int PUBLIC_VAR = 100 -const PUBLIC_CONST = "hello" - -fn public_fn():int { - // can use private symbols within the same module - return PRIVATE_VAR + internal_helper() -} - -// Private symbols - should not be importable -#local -const PRIVATE_CONST = "secret" - -#local -var PRIVATE_VAR = 42 - -#local -fn internal_helper():int { - return PRIVATE_VAR -} - -#local -type private_point = struct { - int x - int y -} - -fn use_private_type():int { - var p = private_point{x: 1, y: 2} - return p.x + p.y -} From 58e75f79e61ae7795058269bede6040fe69f2e01 Mon Sep 17 00:00:00 2001 From: Arjen de Vos Date: Fri, 27 Feb 2026 20:45:58 +0100 Subject: [PATCH 3/4] refactor: remove symbol_is_private function and streamline private symbol checks --- src/semantic/analyzer.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/semantic/analyzer.c b/src/semantic/analyzer.c index 8fce9220..1fc90af4 100644 --- a/src/semantic/analyzer.c +++ b/src/semantic/analyzer.c @@ -1435,17 +1435,6 @@ static bool analyzer_local_ident(module_t *m, ast_expr_t *expr) { return false; } -/** - * Check if a symbol is marked as private (#local). - * Returns true if the symbol is private and should not be importable. - */ -static bool symbol_is_private(symbol_t *sym) { - if (!sym) { - return false; - } - return sym->is_private; -} - static bool analyzer_as_star_or_builtin_ident(module_t *m, ast_ident *ident) { // - import xxx as * 产生的全局符号 for (int i = 0; i < m->imports->count; ++i) { @@ -1453,8 +1442,7 @@ static bool analyzer_as_star_or_builtin_ident(module_t *m, ast_ident *ident) { if (str_equal(import->as, "*")) { char *temp = ident_with_prefix(import->module_ident, ident->literal); - symbol_t *sym = symbol_table_get(temp); - if (sym) { + if (symbol_table_get(temp)) { ident->literal = temp; return true; } @@ -1505,6 +1493,14 @@ static bool analyzer_ident(module_t *m, ast_expr_t *expr) { ANALYZER_ASSERTF(false, "symbol '%s' not found in module", select_ref->original_ident); } + // Check if symbol is private (only for functions) + if (sym->type == SYMBOL_FN) { + ast_fndef_t *fndef = sym->ast_value; + if (fndef->is_private) { + ANALYZER_ASSERTF(false, "cannot import private function '%s'", select_ref->original_ident); + } + } + ident->literal = global_ident; return true; } @@ -1554,8 +1550,7 @@ static void rewrite_select_expr(module_t *m, ast_expr_t *expr) { char *unique_ident = ident_with_prefix(import->module_ident, select->key); // 检测 import ident 是否存在 - symbol_t *sym = symbol_table_get(unique_ident); - if (!sym) { + if (!symbol_table_get(unique_ident)) { ANALYZER_ASSERTF(false, "identifier '%s' undeclared \n", unique_ident); } From 3d80c7b1c3f3544d7e8873052f9fe52a47d0cab9 Mon Sep 17 00:00:00 2001 From: Arjen de Vos Date: Fri, 27 Feb 2026 20:46:23 +0100 Subject: [PATCH 4/4] refactor: update comment for private symbol check in analyzer_ident function --- src/semantic/analyzer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/semantic/analyzer.c b/src/semantic/analyzer.c index 1fc90af4..f1c44e00 100644 --- a/src/semantic/analyzer.c +++ b/src/semantic/analyzer.c @@ -1493,7 +1493,7 @@ static bool analyzer_ident(module_t *m, ast_expr_t *expr) { ANALYZER_ASSERTF(false, "symbol '%s' not found in module", select_ref->original_ident); } - // Check if symbol is private (only for functions) + // Check if symbol is private (only for functions) if (sym->type == SYMBOL_FN) { ast_fndef_t *fndef = sym->ast_value; if (fndef->is_private) {