From 03cd56e46bc3d076f80b222f8ad5fca9541d4d55 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 14 Feb 2023 12:08:14 +0100 Subject: [PATCH] Add @local and fix visibility issues for generic methods. --- src/compiler/ast.c | 10 ++ src/compiler/compiler_internal.h | 11 +- src/compiler/context.c | 2 +- src/compiler/enums.h | 8 +- src/compiler/llvm_codegen.c | 40 ++---- src/compiler/llvm_codegen_debug_info.c | 10 +- src/compiler/llvm_codegen_function.c | 2 +- src/compiler/llvm_codegen_internal.h | 1 - src/compiler/parse_expr.c | 2 +- src/compiler/parse_global.c | 60 +++++--- src/compiler/parse_stmt.c | 4 +- src/compiler/sema_decls.c | 131 +++++++++++------- src/compiler/sema_expr.c | 6 +- src/compiler/sema_name_resolution.c | 21 ++- src/compiler/semantic_analyser.c | 2 +- src/compiler/symtab.c | 1 + src/version.h | 2 +- .../methods/extending_with_visibility.c3 | 35 +++++ .../methods/extending_with_visibility_fail.c3 | 37 +++++ .../extending_with_visibility_fail_private.c3 | 37 +++++ 20 files changed, 295 insertions(+), 127 deletions(-) create mode 100644 test/test_suite/methods/extending_with_visibility.c3 create mode 100644 test/test_suite/methods/extending_with_visibility_fail.c3 create mode 100644 test/test_suite/methods/extending_with_visibility_fail_private.c3 diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 8f3c50188..3a08bf9ea 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -436,6 +436,16 @@ bool ast_is_compile_time(Ast *ast) } } +bool decl_is_externally_visible(Decl *decl) +{ + return decl->is_external_visible || decl->visibility == VISIBLE_PUBLIC || decl->is_export; +} + +bool decl_is_local(Decl *decl) +{ + return !decl->is_external_visible && decl->visibility != VISIBLE_PUBLIC && !decl->is_export; +} + Decl *decl_find_enum_constant(Decl *decl, const char *name) { VECEACH(decl->enums.values, i) diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 6e01a1b03..dd4787caa 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -659,9 +659,9 @@ typedef struct Decl_ SourceSpan span; DeclKind decl_kind : 7; ResolveStatus resolve_status : 3; + Visibility visibility : 3; bool is_packed : 1; bool is_extern : 1; - bool is_private : 1; bool is_substruct : 1; bool has_variable_array : 1; bool is_value : 1; @@ -1599,6 +1599,7 @@ struct CompilationUnit_ Decl *main_function; HTable local_symbols; int lambda_count; + Decl **local_method_extensions; struct { void *debug_file; @@ -2119,10 +2120,12 @@ INLINE Decl *decl_flatten(Decl *decl); INLINE const char *decl_get_extname(Decl *decl); static inline Decl *decl_raw(Decl *decl); static inline DeclKind decl_from_token(TokenType type); -static inline bool decl_is_local(Decl *decl); +static inline bool decl_is_var_local(Decl *decl); bool decl_is_ct_var(Decl *decl); Decl *decl_find_enum_constant(Decl *decl, const char *name); AlignSize decl_find_member_offset(Decl *decl, Decl *member); +bool decl_is_externally_visible(Decl *decl); +bool decl_is_local(Decl *decl); // --- Expression functions @@ -2240,7 +2243,7 @@ Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *in Decl *unit_resolve_parameterized_symbol(CompilationUnit *unit, NameResolve *name_resolve); Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); -Decl *sema_find_extension_method_in_module(Decl **extensions, Type *type, const char *method_name); +Decl *sema_find_extension_method_in_list(Decl **extensions, Type *type, const char *method_name); bool sema_resolve_type_decl(SemaContext *context, Type *type); Decl *sema_find_symbol(SemaContext *context, const char *symbol); @@ -3231,7 +3234,7 @@ INLINE bool decl_var_kind_is_ct(VarDeclKind kind) return kind >= VARDECL_FIRST_CT && kind <= VARDECL_LAST_CT; } -static inline bool decl_is_local(Decl *decl) +static inline bool decl_is_var_local(Decl *decl) { if (decl->decl_kind != DECL_VAR) return false; VarDeclKind kind = decl->var.kind; diff --git a/src/compiler/context.c b/src/compiler/context.c index 4625859c4..c24076328 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -129,7 +129,7 @@ void unit_register_external_symbol(CompilationUnit *unit, Decl *decl) void decl_register(Decl *decl) { - if (decl->is_private) return; + if (decl->visibility > VISIBLE_PUBLIC) return; switch (decl->decl_kind) { case DECL_INITIALIZE: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 36f1e131e..8222ab55e 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -719,15 +719,14 @@ typedef enum VARDECL_LAST_CT = VARDECL_LOCAL_CT_TYPE, } VarDeclKind; + typedef enum { - VISIBLE_LOCAL, - VISIBLE_MODULE, VISIBLE_PUBLIC, - VISIBLE_EXTERN, + VISIBLE_PRIVATE, + VISIBLE_LOCAL, } Visibility; - typedef enum { ATTR_FUNC = 1 << 0, @@ -769,6 +768,7 @@ typedef enum ATTRIBUTE_EXTNAME, ATTRIBUTE_INLINE, ATTRIBUTE_LITTLEENDIAN, + ATTRIBUTE_LOCAL, ATTRIBUTE_MAYDISCARD, ATTRIBUTE_NAKED, ATTRIBUTE_NODISCARD, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 761cbddc5..86538039f 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -332,7 +332,7 @@ void llvm_set_global_tls(Decl *decl) { if (!decl->var.is_threadlocal) return; LLVMThreadLocalMode thread_local_mode = LLVMGeneralDynamicTLSModel; - if (!decl->var.is_addr && decl->is_private && !decl->is_external_visible) + if (!decl->var.is_addr && decl_is_local(decl)) { thread_local_mode = LLVMLocalDynamicTLSModel; } @@ -411,9 +411,8 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) } else { - LLVMUnnamedAddr addr = LLVMGlobalUnnamedAddr; - if (!decl->is_private || decl->is_external_visible) addr = LLVMLocalUnnamedAddr; - LLVMSetUnnamedAddress(decl->backend_ref, addr); + LLVMSetUnnamedAddress(decl->backend_ref, + decl_is_local(decl) ? LLVMGlobalUnnamedAddr : LLVMLocalUnnamedAddr); } if (decl->section) { @@ -447,7 +446,12 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) LLVMSetLinkage(global_ref, LLVMExternalLinkage); if (optional_ref) LLVMSetLinkage(optional_ref, LLVMExternalLinkage); } - else if (decl->is_private && !decl->is_external_visible) + else if (decl_is_externally_visible(decl)) + { + LLVMSetVisibility(global_ref, LLVMDefaultVisibility); + if (optional_ref) LLVMSetVisibility(optional_ref, LLVMDefaultVisibility); + } + else { if (decl->var.kind == VARDECL_CONST || decl->var.kind == VARDECL_GLOBAL) { @@ -460,11 +464,6 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) if (optional_ref) LLVMSetLinkage(optional_ref, LLVMInternalLinkage); } } - else - { - LLVMSetVisibility(global_ref, LLVMDefaultVisibility); - if (optional_ref) LLVMSetVisibility(optional_ref, LLVMDefaultVisibility); - } decl->backend_ref = global_ref; LLVMReplaceAllUsesWith(old, global_ref); @@ -752,24 +751,6 @@ void llvm_set_weak(GenContext *c, LLVMValueRef global) llvm_set_comdat(c, global); } -void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) -{ - if (decl->unit->module != c->code_module) - { - llvm_set_linkonce(c, value); - return; - } - if (decl->is_private && !decl->is_external_visible) - { - LLVMSetVisibility(value, LLVMHiddenVisibility); - LLVMSetLinkage(value, LLVMLinkerPrivateLinkage); - } - else - { - llvm_set_linkonce(c, value); - } -} - void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i) { @@ -1067,8 +1048,9 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) case DECL_FUNC: backend_ref = decl->backend_ref = LLVMAddFunction(c->module, decl_get_extname(decl), llvm_get_type(c, decl->type)); llvm_append_function_attributes(c, decl); - if (decl->unit->module == c->code_module && !decl->is_external_visible && decl->is_private) + if (decl_is_local(decl)) { + assert(decl->unit->module == c->code_module); llvm_set_internal_linkage(backend_ref); } return backend_ref; diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 62cdb850f..e6a9a1bda 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -98,7 +98,7 @@ void llvm_emit_debug_global_var(GenContext *c, Decl *global) c->debug.file, loc.row ? loc.row : 1, llvm_get_debug_type(c, global->type), - global->is_private, + decl_is_local(global), LLVMDIBuilderCreateExpression(c->debug.builder, NULL, 0), NULL, global->alignment); @@ -108,13 +108,13 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) { LLVMDIFlags flags = LLVMDIFlagZero; if (!decl->func_decl.body) return; - if (decl->is_private) + if (decl_is_externally_visible(decl)) { - flags |= LLVMDIFlagPrivate; + flags |= LLVMDIFlagPublic; } else { - flags |= LLVMDIFlagPublic; + flags |= LLVMDIFlagPrivate; } flags |= LLVMDIFlagPrototyped; if (decl->func_decl.signature.attrs.noreturn) flags |= LLVMDIFlagNoReturn; @@ -128,7 +128,7 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) c->debug.file, row, llvm_get_debug_type(c, decl->type), - decl->is_private, + decl_is_local(decl), true, row, flags, diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 88606df46..2f5802c25 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -634,7 +634,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) LLVMSetVisibility(function, LLVMDefaultVisibility); return; } - if (decl->is_private && !decl->is_external_visible) + if (decl_is_local(decl)) { LLVMSetLinkage(function, decl->is_weak ? LLVMLinkerPrivateWeakLinkage : LLVMInternalLinkage); LLVMSetVisibility(function, LLVMDefaultVisibility); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 46e433b08..c665ab18b 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -302,7 +302,6 @@ void llvm_attribute_add_int(GenContext *c, LLVMValueRef value_to_add_attribute_t void llvm_set_linkonce(GenContext *c, LLVMValueRef global); void llvm_set_comdat(GenContext *c, LLVMValueRef global); void llvm_set_weak(GenContext *c, LLVMValueRef global); -void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value); void llvm_set_private_linkage(LLVMValueRef alloc); void llvm_set_internal_linkage(LLVMValueRef alloc); void llvm_set_global_tls(Decl *decl); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index ce153d978..c7371ea4a 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -342,7 +342,7 @@ static Expr *parse_lambda(ParseContext *c, Expr *left) advance_and_verify(c, TOKEN_FN); Decl *func = decl_calloc(); func->decl_kind = DECL_FUNC; - func->is_private = true; + func->visibility = VISIBLE_LOCAL; func->func_decl.generated_lambda = NULL; TypeInfo *return_type = NULL; if (!tok_is(c, TOKEN_LPAREN)) diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index e98cc9683..ee85d5cb8 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -826,7 +826,7 @@ Decl *parse_local_decl(ParseContext *c) if (tok_is(c, TOKEN_CONST)) { ASSIGN_DECL_OR_RET(Decl *decl, parse_const_declaration(c), poisoned_decl); - decl->is_private = true; + decl->visibility = VISIBLE_LOCAL; return decl; } @@ -843,7 +843,7 @@ Decl *parse_local_decl(ParseContext *c) } decl->var.is_static = is_static || is_threadlocal; decl->var.is_threadlocal = is_threadlocal; - decl->is_private = true; + decl->visibility = VISIBLE_LOCAL; return decl; } @@ -1045,7 +1045,28 @@ bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Decl *owner) SEMA_ERROR(attr, "'%s' cannot be used here."); return false; } - owner->is_private = true; + if (owner->visibility != VISIBLE_PUBLIC) + { + SEMA_ERROR(attr, "Only a single visibility attribute may be added."); + return false; + } + owner->visibility = VISIBLE_PRIVATE; + continue; + } + if (name == attribute_list[ATTRIBUTE_LOCAL]) + { + if (!owner) + { + SEMA_ERROR(attr, "'%s' cannot be used here."); + return false; + } + if (owner->visibility != VISIBLE_PUBLIC) + { + SEMA_ERROR(attr, "Only a single visibility attribute may be added."); + return false; + } + owner->visibility = VISIBLE_LOCAL; + continue; } FOREACH_BEGIN(Attr *other_attr, *attributes_ref) if (other_attr->name == name) @@ -2073,7 +2094,7 @@ static inline Decl *parse_fault_declaration(ParseContext *c, bool is_private) // advance_and_verify(context, TOKEN_ERRTYPE); Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_FAULT); - decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; if (!consume_type_name(c, "fault")) return poisoned_decl; TypeInfo *type = NULL; @@ -2085,7 +2106,7 @@ static inline Decl *parse_fault_declaration(ParseContext *c, bool is_private) while (!try_consume(c, TOKEN_RBRACE)) { Decl *fault_const = decl_new(DECL_FAULTVALUE, symstr(c), c->span); - fault_const->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; if (!consume_const_name(c, "fault value")) { return poisoned_decl; @@ -2171,7 +2192,7 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_private) advance_and_verify(c, TOKEN_ENUM); Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_ENUM); - decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; if (!consume_type_name(c, "enum")) return poisoned_decl; TypeInfo *type = NULL; @@ -2181,14 +2202,14 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_private) } if (!parse_attributes(c, &decl->attributes, decl)) return poisoned_decl; - + Visibility visibility = decl->visibility; CONSUME_OR_RET(TOKEN_LBRACE, poisoned_decl); decl->enums.type_info = type ? type : type_info_new_base(type_int, decl->span); while (!try_consume(c, TOKEN_RBRACE)) { Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, symstr(c), c->span); - enum_const->is_private = is_private; + enum_const->visibility = visibility; const char *name = enum_const->name; if (!consume_const_name(c, "enum constant")) { @@ -2373,6 +2394,11 @@ static inline bool parse_import(ParseContext *c) Path *path = parse_module_path(c); if (!path) return false; unit_add_import(c->unit, path, private); + if (tok_is(c, TOKEN_COLON) && peek(c) == TOKEN_IDENT) + { + SEMA_ERROR_HERE("'::' was expected here, did you make a mistake?"); + return false; + } if (!try_consume(c, TOKEN_COMMA)) break; } @@ -2777,13 +2803,13 @@ AFTER_VISIBILITY: case TOKEN_DEFINE: { ASSIGN_DECL_OR_RET(decl, parse_define(c), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_FN: { ASSIGN_DECL_OR_RET(decl, parse_func_definition(c, docs, false), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_STATIC: @@ -2871,45 +2897,43 @@ AFTER_VISIBILITY: case TOKEN_BITSTRUCT: { ASSIGN_DECL_OR_RET(decl, parse_bitstruct_declaration(c), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_CONST: { ASSIGN_DECL_OR_RET(decl, parse_top_level_const_declaration(c), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_STRUCT: case TOKEN_UNION: { ASSIGN_DECL_OR_RET(decl, parse_struct_declaration(c), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_GENERIC: case TOKEN_MACRO: { ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, docs), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_ENUM: { ASSIGN_DECL_OR_RET(decl, parse_enum_declaration(c, is_private), poisoned_decl); - if (is_private) decl->is_private = is_private; break; } case TOKEN_FAULT: { ASSIGN_DECL_OR_RET(decl, parse_fault_declaration(c, is_private), poisoned_decl); - if (is_private) decl->is_private = is_private; break; } case TOKEN_IDENT: { ASSIGN_DECL_OR_RET(decl, parse_global_declaration(c), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_EOF: @@ -2931,7 +2955,7 @@ AFTER_VISIBILITY: case TYPELIKE_TOKENS: { ASSIGN_DECL_OR_RET(decl, parse_global_declaration(c), poisoned_decl); - if (is_private) decl->is_private = is_private; + if (is_private) decl->visibility = VISIBLE_PRIVATE; break; } case TOKEN_EOS: diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index e2204afc6..f551a03d2 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -101,13 +101,13 @@ static inline Ast *parse_declaration_stmt(ParseContext *c) { result->declare_stmt->var.is_threadlocal = is_threadlocal; result->declare_stmt->var.is_static = is_static || is_threadlocal; - result->declare_stmt->is_private = true; + result->declare_stmt->visibility = VISIBLE_LOCAL; return result; } FOREACH_BEGIN(Decl *var, result->decls_stmt) var->var.is_threadlocal = is_threadlocal; var->var.is_static = is_static || is_threadlocal; - var->is_private = true; + var->visibility = VISIBLE_LOCAL; FOREACH_END(); return result; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 77c47cbf4..b971ade0a 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1282,34 +1282,48 @@ static bool sema_check_operator_method_validity(Decl *method) UNREACHABLE } -static inline bool unit_add_base_extension_method(CompilationUnit *unit, Type *parent_type, Decl *method_like) +INLINE void sema_set_method_ext_name(CompilationUnit *unit, const char *parent_name, Decl *method_like) { - if (!method_like->has_extname) + if (method_like->has_extname) return; + scratch_buffer_clear(); + switch (method_like->visibility) { - scratch_buffer_clear(); - if (method_like->is_private) - { - scratch_buffer_append(parent_type->name); - scratch_buffer_append_char('$'); - scratch_buffer_append(method_like->name); - } - else - { - scratch_buffer_append(parent_type->name); + case VISIBLE_PUBLIC: + scratch_buffer_append(parent_name); scratch_buffer_append("_"); scratch_buffer_append(method_like->name); - } - method_like->extname = scratch_buffer_copy(); + break; + case VISIBLE_PRIVATE: + scratch_buffer_append(parent_name); + scratch_buffer_append_char('$'); + scratch_buffer_append(method_like->name); + break; + case VISIBLE_LOCAL: + scratch_buffer_append(unit->file->name); + scratch_buffer_append_char('.'); + scratch_buffer_append(parent_name); + scratch_buffer_append_char('.'); + scratch_buffer_append(method_like->name); + break; + } + method_like->extname = scratch_buffer_copy(); +} +static inline bool unit_add_base_extension_method(CompilationUnit *unit, Type *parent_type, Decl *method_like) +{ + sema_set_method_ext_name(unit, parent_type->name, method_like); + switch (method_like->visibility) + { + case VISIBLE_PUBLIC: + vec_add(global_context.method_extensions, method_like); + break; + case VISIBLE_PRIVATE: + vec_add(unit->module->private_method_extensions, method_like); + break; + case VISIBLE_LOCAL: + vec_add(unit->local_method_extensions, method_like); + break; } DEBUG_LOG("Method-like '%s.%s' analysed.", parent_type->name, method_like->name); - if (method_like->is_private) - { - vec_add(unit->module->private_method_extensions, method_like); - } - else - { - vec_add(global_context.method_extensions, method_like); - } return true; } @@ -1317,8 +1331,9 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type { assert(parent_type->canonical == parent_type); const char *name = method_like->name; - Decl *method = sema_find_extension_method_in_module(unit->module->private_method_extensions, parent_type, name); - if (!method) sema_find_extension_method_in_module(global_context.method_extensions, parent_type, name); + Decl *method = sema_find_extension_method_in_list(unit->local_method_extensions, parent_type, name); + if (!method) sema_find_extension_method_in_list(unit->module->private_method_extensions, parent_type, name); + if (!method) sema_find_extension_method_in_list(global_context.method_extensions, parent_type, name); if (method) { SEMA_ERROR(method_like, "This %s is already defined.", method_name_by_decl(method_like)); @@ -1340,31 +1355,31 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type } if (method_like->operator && !sema_check_operator_method_validity(method_like)) return false; REMINDER("Check multiple operator"); - if (!method_like->has_extname) - { - scratch_buffer_clear(); - if (method_like->is_private) - { - scratch_buffer_append(parent->extname); - scratch_buffer_append_char('$'); - scratch_buffer_append(method_like->name); - } - else - { - scratch_buffer_append(parent->extname); - scratch_buffer_append("_"); - scratch_buffer_append(method_like->name); - } - method_like->extname = scratch_buffer_copy(); - } + sema_set_method_ext_name(unit, parent->extname, method_like); DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method_like->name); - if (parent->unit->module == unit->module || !method_like->is_private) + switch (method_like->visibility) { - vec_add(parent->methods, method_like); - } - else - { - vec_add(unit->module->private_method_extensions, method_like); + case VISIBLE_PUBLIC: + vec_add(parent->methods, method_like); + break; + case VISIBLE_PRIVATE: + if (parent->unit->module == unit->module && parent->visibility >= VISIBLE_PRIVATE) + { + vec_add(parent->methods, method_like); + break; + } + vec_add(unit->module->private_method_extensions, method_like); + break; + case VISIBLE_LOCAL: + if (parent->unit == unit && parent->visibility >= VISIBLE_LOCAL) + { + vec_add(parent->methods, method_like); + break; + } + vec_add(unit->local_method_extensions, method_like); + break; + default: + UNREACHABLE } return true; @@ -1447,6 +1462,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_EXTERN] = (AttributeDomain)~(ATTR_CALL | ATTR_BITSTRUCT | ATTR_DEFINE | ATTR_MACRO | ATTR_XXLIZER), [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, [ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT, + [ATTRIBUTE_LOCAL] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEFINE, [ATTRIBUTE_MAYDISCARD] = ATTR_FUNC | ATTR_MACRO, [ATTRIBUTE_NAKED] = ATTR_FUNC, [ATTRIBUTE_NODISCARD] = ATTR_FUNC | ATTR_MACRO, @@ -1516,7 +1532,20 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, } break; case ATTRIBUTE_PRIVATE: - decl->is_private = true; + if (decl->visibility != VISIBLE_PUBLIC) + { + SEMA_ERROR(decl, "Multiple visibility attributes cannot be combined."); + return false; + } + decl->visibility = VISIBLE_PRIVATE; + break; + case ATTRIBUTE_LOCAL: + if (decl->visibility != VISIBLE_PUBLIC) + { + SEMA_ERROR(decl, "Multiple visibility attributes cannot be combined."); + return false; + } + decl->visibility = VISIBLE_LOCAL; break; case ATTRIBUTE_TEST: decl->func_decl.attr_test = true; @@ -2125,9 +2154,9 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) assert(decl != context->unit->main_function); bool is_winmain = decl->func_decl.attr_winmain; bool is_win32 = platform_target.os == OS_TYPE_WIN32; - if (decl->is_private) + if (decl->visibility != VISIBLE_PUBLIC) { - SEMA_ERROR(decl, "A main function may not be private."); + SEMA_ERROR(decl, "A main function must be public."); return false; } Signature *signature = &decl->func_decl.signature; @@ -2736,7 +2765,7 @@ static CompilationUnit *unit_copy(Module *module, CompilationUnit *unit) static Module *module_instantiate_generic(Module *module, Path *path, Expr **params) { Module *new_module = compiler_find_or_create_module(path, NULL, module->is_private); - new_module->is_generic = true; + new_module->is_generic = false; CompilationUnit **units = module->units; VECEACH(units, i) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 1c09032c5..25a379e5f 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3655,6 +3655,10 @@ CHECK_DEEPER: return true; } Decl *private = NULL; + if (strcmp(kw, "put_all_for_create") == 0) + { + int x = 123; + } if (!member) { Decl *ambiguous = NULL; @@ -6389,7 +6393,7 @@ static inline bool sema_expr_analyse_ct_nameof(SemaContext *context, Expr *expr) expr_rewrite_to_string(expr, decl->extname); return true; } - if (!decl->unit || name_type == TOKEN_CT_NAMEOF || decl_is_local(decl)) + if (!decl->unit || name_type == TOKEN_CT_NAMEOF || decl_is_var_local(decl)) { expr_rewrite_to_string(expr, decl->name); return true; diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 9565374fd..d5eb53616 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -150,8 +150,10 @@ static Decl *sema_find_decl_in_imports(Decl **imports, NameResolve *name_resolve // No match, so continue if (!found) continue; + assert(found->visibility != VISIBLE_LOCAL); + // If we found something private but we don't import privately? - if (found->is_private && !import->import.private && !decl) + if (found->visibility == VISIBLE_PRIVATE && !import->import.private && !decl) { // Register this as a possible private decl. name_resolve->private_decl = found; @@ -568,7 +570,7 @@ INLINE Decl *sema_resolve_symbol_common(SemaContext *context, NameResolve *name_ return decl; } -Decl *sema_find_extension_method_in_module(Decl **extensions, Type *type, const char *method_name) +Decl *sema_find_extension_method_in_list(Decl **extensions, Type *type, const char *method_name) { VECEACH(extensions, i) { @@ -592,13 +594,14 @@ Decl *sema_resolve_method_in_module(Module *module, Type *actual_type, const cha Decl **private_found, Decl **ambiguous, MethodSearchType search_type) { if (module->is_generic) return NULL; - Decl *found = sema_find_extension_method_in_module(module->private_method_extensions, actual_type, method_name); + Decl *found = sema_find_extension_method_in_list(module->private_method_extensions, actual_type, method_name); // The found one might not be visible - if (found && search_type < METHOD_SEARCH_CURRENT && found->is_private) + if (found && search_type < METHOD_SEARCH_CURRENT && found->visibility == VISIBLE_PRIVATE) { *private_found = found; found = NULL; } + assert(!found || found->visibility != VISIBLE_LOCAL); if (found && search_type == METHOD_SEARCH_CURRENT) return found; // We are now searching submodules, so hide the private ones. if (search_type == METHOD_SEARCH_CURRENT) search_type = METHOD_SEARCH_SUBMODULE_CURRENT; @@ -622,7 +625,10 @@ Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_ VECEACH(type->methods, i) { Decl *func = type->methods[i]; - if (method_name == func->name) return func; + if (method_name == func->name) + { + return func; + } } return sema_resolve_type_method(unit, type->type, method_name, ambiguous_ref, private_ref); @@ -681,7 +687,8 @@ Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *me assert(type == type->canonical); Decl *private = NULL; Decl *ambiguous = NULL; - Decl *found = sema_resolve_method_in_module(unit->module, type, method_name, &private, &ambiguous, METHOD_SEARCH_CURRENT); + Decl *found = sema_find_extension_method_in_list(unit->local_method_extensions, type, method_name); + if (!found) found = sema_resolve_method_in_module(unit->module, type, method_name, &private, &ambiguous, METHOD_SEARCH_CURRENT); if (ambiguous) { *ambiguous_ref = ambiguous; @@ -725,7 +732,7 @@ Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *me } if (!found) { - found = sema_find_extension_method_in_module(global_context.method_extensions, type, method_name); + found = sema_find_extension_method_in_list(global_context.method_extensions, type, method_name); private = NULL; } if (private) *private_ref = private; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 3caa0ada6..77cff493d 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -198,7 +198,7 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) break; } htable_set(&unit->module->symbols, (void *)decl->name, decl); - if (!decl->is_private) global_context_add_generic_decl(decl); + if (decl->visibility == VISIBLE_PUBLIC) global_context_add_generic_decl(decl); } } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 20baacc01..98811babf 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -301,6 +301,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_EXTNAME] = KW_DEF("@extname"); attribute_list[ATTRIBUTE_INLINE] = KW_DEF("@inline"); attribute_list[ATTRIBUTE_LITTLEENDIAN] = KW_DEF("@littleendian"); + attribute_list[ATTRIBUTE_LOCAL] = KW_DEF("@local"); attribute_list[ATTRIBUTE_MAYDISCARD] = KW_DEF("@maydiscard"); attribute_list[ATTRIBUTE_NAKED] = KW_DEF("@naked"); attribute_list[ATTRIBUTE_NODISCARD] = KW_DEF("@nodiscard"); diff --git a/src/version.h b/src/version.h index 6aa9b26df..5ad01c934 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.62" \ No newline at end of file +#define COMPILER_VERSION "0.4.63" \ No newline at end of file diff --git a/test/test_suite/methods/extending_with_visibility.c3 b/test/test_suite/methods/extending_with_visibility.c3 new file mode 100644 index 000000000..64119422e --- /dev/null +++ b/test/test_suite/methods/extending_with_visibility.c3 @@ -0,0 +1,35 @@ +module test; +import abc; + +struct Foo +{ + int x; +} + +fn int Foo.get1(Foo* f) @private => f.x; +fn int Foo.get2(Foo* f) => f.x; +fn int Foo.get3(Foo* f) @local => f.x; + +fn int Bar.get1(Bar* f) @private => f.x; +fn int Bar.get2(Bar* f) => f.x; +fn int Bar.get3(Bar* f) @local => f.x; + +fn int main() +{ + Foo x = { 1 }; + x.get1(); + x.get2(); + x.get3(); + Bar y = { 1 }; + y.get1(); + y.get2(); + y.get3(); + return 1; +} + +module abc; + +struct Bar +{ + int x; +} diff --git a/test/test_suite/methods/extending_with_visibility_fail.c3 b/test/test_suite/methods/extending_with_visibility_fail.c3 new file mode 100644 index 000000000..548ad1beb --- /dev/null +++ b/test/test_suite/methods/extending_with_visibility_fail.c3 @@ -0,0 +1,37 @@ +module test; +import abc; + +struct Foo +{ + int x; +} + +fn int Foo.get1(Foo* f) @private => f.x; +fn int Foo.get2(Foo* f) => f.x; +fn int Foo.get3(Foo* f) @local => f.x; + +fn int Bar.get1(Bar* f) @private => f.x; +fn int Bar.get2(Bar* f) => f.x; +fn int Bar.get3(Bar* f) @local => f.x; + +module abc; +import test; + +struct Bar +{ + int x; +} + +Foo x = { 1 }; +Bar y = { 1 }; + +fn int test() +{ + x.get1(); // #error: method + x.get2(); + x.get3(); // #error: method + y.get1(); // #error: method + y.get2(); + y.get3(); // #error: method + return 1; +} diff --git a/test/test_suite/methods/extending_with_visibility_fail_private.c3 b/test/test_suite/methods/extending_with_visibility_fail_private.c3 new file mode 100644 index 000000000..3e8bb5ffe --- /dev/null +++ b/test/test_suite/methods/extending_with_visibility_fail_private.c3 @@ -0,0 +1,37 @@ +module test; +import abc; + +struct Foo +{ + int x; +} + +fn int Foo.get1(Foo* f) @private => f.x; +fn int Foo.get2(Foo* f) => f.x; +fn int Foo.get3(Foo* f) @local => f.x; + +fn int Bar.get1(Bar* f) @private => f.x; +fn int Bar.get2(Bar* f) => f.x; +fn int Bar.get3(Bar* f) @local => f.x; + +module abc; +import private test; + +struct Bar +{ + int x; +} + +Foo x = { 1 }; +Bar y = { 1 }; + +fn int test() +{ + x.get1(); + x.get2(); + x.get3(); // #error: method + y.get1(); + y.get2(); + y.get3(); // #error: method + return 1; +}