diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 9247057ef..4e06a1cb1 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -528,9 +528,6 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin @body(); } - - - import libc; diff --git a/releasenotes.md b/releasenotes.md index 3a323cb4c..8a30c37b1 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,7 +24,8 @@ - Add `@const` attribute for macros, for better error messages with constant macros. - Add `wincrt` setting to libraries. - Add `+++` `&&&` `|||` as replacement for `$concat`, `$and` and `$or`. -- Add `methodsof` to type info for struct, union and bitstruct +- Add `methodsof` to type info for struct, union and bitstruct. +- Added `@tag` `tagof` and `has_tagof` to user defined types and members. ### Fixes @@ -50,6 +51,7 @@ - Improved output when pointer is out of range. - Better error when casting to a distinct fails. - With single module, name the .o file after what `-o` provides. #1306 +- Bitstruct members can now have attributes. ### Stdlib changes diff --git a/src/build/build.h b/src/build/build.h index e32a13e9e..b5b7763b7 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -394,6 +394,7 @@ typedef struct BuildOptions_ const char *custom_linker_path; uint32_t symtab_size; unsigned version; + bool silence_deprecation; CompilerBackend backend; CompilerCommand command; ProjectSubcommand subcommand; @@ -542,6 +543,7 @@ typedef struct bool print_linking; bool no_entry; bool kernel_build; + bool silence_deprecation; int build_threads; TrustLevel trust_level; OptimizationSetting optsetting; diff --git a/src/build/build_options.c b/src/build/build_options.c index e3c0471d5..ed34d72ab 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -703,6 +703,11 @@ static void parse_option(BuildOptions *options) if (maxmem < 128) PRINTF("Expected a valid positive integer >= 128."); return; } + if (match_longopt("silence-deprecation")) + { + options->silence_deprecation = true; + return; + } if (match_longopt("symtab")) { if (at_end() || next_is_opt()) error_exit("error: --symtab needs a valid integer."); diff --git a/src/build/builder.c b/src/build/builder.c index d827ef279..b1fe7d235 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -324,6 +324,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * if (options->arch_os_target_override != ARCH_OS_TARGET_DEFAULT) target->arch_os_target = options->arch_os_target_override; if (options->reloc_model != RELOC_DEFAULT) target->reloc_model = options->reloc_model; if (options->symtab_size) target->symtab_size = options->symtab_size; + if (options->silence_deprecation) target->silence_deprecation = options->silence_deprecation; target->print_linking = options->print_linking; for (int i = 0; i < options->linker_arg_count; i++) diff --git a/src/compiler/ast.c b/src/compiler/ast.c index ac8cf52a6..50df5c2e5 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -300,19 +300,11 @@ void decl_append_links_to_global(Decl *decl) FOREACH(const char *, link, unit->links) global_context_add_link(link); unit->links = NULL; // Don't register twice } - if (decl->has_link) + if (decl->attrs_resolved && decl->attrs_resolved->links) { - FOREACH(Attr *, attr, decl->attributes) + FOREACH(const char *, link, decl->attrs_resolved->links) { - if (attr->attr_kind != ATTRIBUTE_LINK) continue; - if (!attr->exprs) continue; - unsigned args = vec_size(attr->exprs); - for (unsigned i = 0; i < args; i++) - { - Expr *string = attr->exprs[i]; - if (!string) continue; - global_context_add_link(string->const_expr.bytes.ptr); - } + global_context_add_link(link); } } } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 7b622fcfb..1aaf66ae1 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -56,7 +56,6 @@ void compiler_init(const char *std_lib_dir) global_context.module_list = NULL; global_context.generic_module_list = NULL; global_context.method_extensions = NULL; - global_context.section_list = NULL; vmem_init(&ast_arena, 512); ast_calloc(); vmem_init(&expr_arena, 512); @@ -1163,26 +1162,6 @@ void global_context_add_link(const char *link) vec_add(global_context.links, link); } -SectionId global_context_register_section(const char *section) -{ - scratch_buffer_clear(); - scratch_buffer_append("SECTION#"); - scratch_buffer_append(section); - TokenType type = TOKEN_INVALID_TOKEN; - const char *result = scratch_buffer_interned(); - FOREACH_IDX(i, const char *, candidate, global_context.section_list) - { - if (result == candidate) return i + 1; - } - unsigned len = vec_size(global_context.section_list); - if (len >= MAX_SECTIONS) - { - error_exit("Too many sections in source, max %d allowed.", MAX_SECTIONS); - } - vec_add(global_context.section_list, result); - return len + 1; -} - void global_context_clear_errors(void) { global_context.in_panic_mode = false; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 5e07a4320..3026c09d9 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -24,10 +24,7 @@ typedef uint32_t AlignSize; typedef int32_t ScopeId; typedef uint32_t ArraySize; typedef uint64_t BitSize; -typedef uint16_t SectionId; -#define MAX_SECTIONS 0xFFFE -#define SECTION_PREFIX_LEN 8 #define MAX_FIXUPS 0xFFFFF #define MAX_HASH_SIZE (512 * 1024 * 1024) #define INVALID_SPAN ((SourceSpan){ .row = 0 }) @@ -411,6 +408,7 @@ struct TypeInfo_ }; + typedef struct { Path *path; @@ -421,6 +419,15 @@ typedef struct Expr **exprs; } Attr; +typedef struct +{ + Attr **tags; + const char *deprecated; + const char **links; + const char *section; + SourceSpan overload; +} ResolvedAttrData; + typedef struct { Path *path; @@ -462,6 +469,8 @@ typedef struct typedef struct VarDecl_ { + TypeInfoId type_info; + uint16_t va_index; VarDeclKind kind : 8; bool shadow : 1; bool vararg : 1; @@ -476,7 +485,6 @@ typedef struct VarDecl_ bool no_init : 1; bool no_alias : 1; bool bit_is_expr : 1; - TypeInfoId type_info; union { Expr *init_expr; @@ -619,7 +627,6 @@ typedef struct }; } TypedefDecl; - typedef enum { DEFINE_IDENT_ALIAS, @@ -672,6 +679,7 @@ typedef struct Decl_ DeclKind decl_kind : 7; ResolveStatus resolve_status : 3; Visibility visibility : 3; + bool has_tag : 1; bool is_packed : 1; bool is_extern : 1; bool is_substruct : 1; @@ -690,12 +698,11 @@ typedef struct Decl_ bool is_export : 1; bool is_live : 1; bool no_strip : 1; - bool is_deprecated : 1; bool is_cond : 1; - bool has_link : 1; bool is_if : 1; bool attr_nopadding : 1; bool attr_compact : 1; + bool resolved_attributes : 1; OperatorOverload operator : 4; union { @@ -705,15 +712,14 @@ typedef struct Decl_ void *tb_symbol; }; AlignSize alignment; - union - { - SectionId section_id; - uint16_t va_index; - }; AlignSize offset; AlignSize padding; struct CompilationUnit_ *unit; - Attr **attributes; + union + { + Attr **attributes; + ResolvedAttrData *attrs_resolved; + }; Type *type; union { @@ -753,6 +759,7 @@ typedef struct Decl_ }; } Decl; +// static_assert(sizeof(void*) != 8 || sizeof(Decl) == 136, "Decl has unexpected size."); typedef struct @@ -911,6 +918,11 @@ typedef struct Expr *value; } ExprDesignator; +typedef struct +{ + Decl *type; + TypeProperty property; +} ExprTagOf; typedef struct { union @@ -1228,6 +1240,7 @@ struct Expr_ ExprSwizzle swizzle_expr; ExprTernary ternary_expr; // 16 BuiltinDefine benchmark_hook_expr; + ExprTagOf tag_of_expr; BuiltinDefine test_hook_expr; Expr** try_unwrap_chain_expr; // 8 ExprTryUnwrap try_unwrap_expr; // 24 @@ -1807,7 +1820,6 @@ typedef struct Decl *decl_stack[MAX_GLOBAL_DECL_STACK]; Decl **decl_stack_bottom; Decl **decl_stack_top; - const char **section_list; } GlobalContext; @@ -2209,10 +2221,8 @@ void global_context_clear_errors(void); void global_context_add_type(Type *type); void global_context_add_decl(Decl *type_decl); void global_context_add_generic_decl(Decl *decl); -SectionId global_context_register_section(const char *section); -void global_context_add_link(const char *link); -INLINE const char *section_from_id(SectionId id); +void global_context_add_link(const char *link); Module *compiler_find_or_create_module(Path *module_name, const char **parameters); Module *global_context_find_module(const char *name); @@ -3453,6 +3463,7 @@ INLINE void expr_set_span(Expr *expr, SourceSpan loc) case EXPR_VASPLAT: case EXPR_MACRO_BODY: case EXPR_DEFAULT_ARG: + case EXPR_TAGOF: break; } } @@ -3849,11 +3860,6 @@ INLINE bool expr_is_const_member(Expr *expr) return expr->expr_kind == EXPR_CONST && expr->const_expr.const_kind == CONST_MEMBER; } -INLINE const char *section_from_id(SectionId id) -{ - return id ? global_context.section_list[id - 1] + SECTION_PREFIX_LEN : NULL; -} - INLINE bool check_module_name(Path *path) { if (!str_is_valid_module_name(path->module)) diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 9b347baea..c3071bc6c 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -288,6 +288,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) Expr *expr = expr_copy(source_expr); switch (source_expr->expr_kind) { + case EXPR_TAGOF: case EXPR_ANYSWITCH: UNREACHABLE case EXPR_OTHER_CONTEXT: @@ -762,21 +763,40 @@ Decl **copy_decl_list_single(Decl **decl_list) return result; } +INLINE Attr *copy_attribute(CopyStruct *c, Attr *attr) +{ + if (!attr) return NULL; + Attr *copy = MALLOCS(Attr); + *copy = *attr; + MACRO_COPY_EXPR_LIST(copy->exprs); + return copy; +} + static Attr **copy_attributes(CopyStruct *c, Attr** attr_list) { if (!attr_list) return attr_list; Attr** list = NULL; FOREACH(Attr *, attribute, attr_list) { - - Attr *copy = MALLOCS(Attr); - *copy = *attribute; - MACRO_COPY_EXPR_LIST(copy->exprs); - vec_add(list, copy); + vec_add(list, copy_attribute(c, attribute)); } return list; } + +static ResolvedAttrData *copy_attrs_resolved(CopyStruct *c, ResolvedAttrData *data) +{ + if (!data) return NULL; + ResolvedAttrData *copy = MALLOCS(ResolvedAttrData); + *copy = (ResolvedAttrData) { + .tags = copy_attributes(c, data->tags), + .deprecated = data->deprecated, + .links = data->links, + .section = data->section, + }; + return copy; +} + Attr **copy_attributes_single(Attr** attr_list) { copy_begin(); @@ -888,7 +908,14 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) if (c->single_static && decl_is_resolved_static_var(decl)) return decl; Decl *copy = decl_copy(decl); copy_reg_ref(c, decl, copy); - copy->attributes = copy_attributes(c, copy->attributes); + if (decl->resolved_attributes) + { + copy->attrs_resolved = copy_attrs_resolved(c, copy->attrs_resolved); + } + else + { + copy->attributes = copy_attributes(c, copy->attributes); + } switch (decl->decl_kind) { case DECL_POISONED: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 25eb116b1..a9a1ad45a 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -184,12 +184,12 @@ typedef enum case DECL_POISONED -#define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \ +#define NON_RUNTIME_EXPR EXPR_POISONED: \ case EXPR_CT_DEFINED: case EXPR_CT_AND_OR:\ case EXPR_CT_CASTABLE: case EXPR_CT_IS_CONST: \ case EXPR_CT_ARG: case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \ - case EXPR_ANYSWITCH: case EXPR_STRINGIFY: \ + case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_TAGOF: \ case EXPR_CT_EVAL: case EXPR_CT_CONCAT: case EXPR_CT_APPEND typedef enum @@ -245,6 +245,7 @@ typedef enum EXPR_COMPOUND_LITERAL, EXPR_COND, EXPR_CONST, + EXPR_TAGOF, EXPR_CT_AND_OR, EXPR_CT_ARG, EXPR_CT_APPEND, @@ -298,8 +299,11 @@ typedef enum EXPR_TYPEINFO, EXPR_UNARY, EXPR_VASPLAT, + EXPR_LAST = EXPR_VASPLAT } ExprKind; +static_assert(EXPR_LAST < 128, "Too many expression types"); + typedef enum { ASM_ARG_REG, @@ -846,6 +850,7 @@ typedef enum ATTRIBUTE_REFLECT, ATTRIBUTE_SAFEMACRO, ATTRIBUTE_SECTION, + ATTRIBUTE_TAG, ATTRIBUTE_TEST, ATTRIBUTE_UNUSED, ATTRIBUTE_USED, @@ -1046,6 +1051,8 @@ typedef enum TYPE_PROPERTY_QNAMEOF, TYPE_PROPERTY_RETURNS, TYPE_PROPERTY_SIZEOF, + TYPE_PROPERTY_TAGOF, + TYPE_PROPERTY_HAS_TAGOF, TYPE_PROPERTY_VALUES, TYPE_PROPERTY_NONE, NUMBER_OF_TYPE_PROPERTIES = TYPE_PROPERTY_NONE diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 60739d60c..4a6ddced3 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -127,6 +127,7 @@ bool expr_may_addr(Expr *expr) case EXPR_MACRO_BODY: case EXPR_DEFAULT_ARG: case EXPR_LAST_FAULT: + case EXPR_DESIGNATOR: return false; } UNREACHABLE @@ -173,6 +174,8 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) return false; case EXPR_BITASSIGN: return false; + case EXPR_TAGOF: + return true; case EXPR_BUILTIN_ACCESS: switch (expr->builtin_access_expr.kind) { @@ -679,6 +682,7 @@ bool expr_is_pure(Expr *expr) return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset); case EXPR_COMPILER_CONST: case EXPR_CONST: + case EXPR_TAGOF: case EXPR_CT_AND_OR: case EXPR_CT_CONCAT: case EXPR_CT_APPEND: diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 62ebfd999..5381891d9 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -540,9 +540,9 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) LLVMSetUnnamedAddress(decl->backend_ref, decl_is_local(decl) ? LLVMGlobalUnnamedAddr : LLVMLocalUnnamedAddr); } - if (decl->section_id) + if (decl->attrs_resolved && decl->attrs_resolved->section) { - LLVMSetSection(global_ref, section_from_id(decl->section_id)); + LLVMSetSection(global_ref, decl->attrs_resolved->section); } llvm_set_global_tls(decl); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 73bfdf7f5..bb67d183b 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -7062,6 +7062,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_EMBED: case EXPR_MACRO_BODY: case EXPR_OTHER_CONTEXT: + case EXPR_DESIGNATOR: UNREACHABLE case EXPR_DEFAULT_ARG: llvm_emit_default_arg(c, value, expr); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index d737cf21c..a2d912b37 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -620,9 +620,9 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) decl_append_links_to_global(decl); LLVMValueRef function = llvm_get_ref(c, decl); decl->backend_ref = function; - if (decl->section_id) + if (decl->attrs_resolved && decl->attrs_resolved->section) { - LLVMSetSection(function, section_from_id(decl->section_id)); + LLVMSetSection(function, decl->attrs_resolved->section); } if (llvm_use_debug(c)) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 914139caf..9f09504fc 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1627,7 +1627,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) PRINT_ERROR_HERE("Expected a field name at this position."); return false; } - if (tok_is(c, TOKEN_EOS)) + if (tok_is(c, TOKEN_EOS) || tok_is(c, TOKEN_AT_IDENT)) { if (!is_consecutive) { @@ -1637,6 +1637,9 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) } is_consecutive = true; } + bool is_cond = false; + if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond)) return false; + member_decl->is_cond = is_cond; CONSUME_OR_RET(TOKEN_EOS, false); unsigned index = vec_size(decl->bitstruct.members); member_decl->var.start_bit = index; @@ -1655,6 +1658,9 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl) { member_decl->var.end = NULL; } + bool is_cond = false; + if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond)) return false; + member_decl->is_cond = is_cond; CONSUME_EOS_OR_RET(false); if (is_consecutive) { diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 672e03b93..2ffaca3cd 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -42,8 +42,8 @@ static inline bool sema_analyse_bitstruct_member(SemaContext *context, Decl *par static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, Decl **params, Decl **extra_params, bool *pure_ref); static const char *attribute_domain_to_string(AttributeDomain domain); -static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, AttributeDomain domain, bool *erase_decl); -static bool sema_analyse_attributes_inner(SemaContext *context, Decl *decl, Attr **attrs, AttributeDomain domain, +static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_data, Decl *decl, Attr *attr, AttributeDomain domain, bool *erase_decl); +static bool sema_analyse_attributes_inner(SemaContext *context, ResolvedAttrData *attr_data, Decl *decl, Attr **attrs, AttributeDomain domain, Decl *top, bool *erase_decl); static bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **attrs, AttributeDomain domain, bool *erase_decl); @@ -927,11 +927,21 @@ static bool sema_analyse_interface(SemaContext *context, Decl *decl, bool *erase if (!decl_ok(method)) return false; continue; } - - if (method->decl_kind != DECL_FUNC) RETURN_SEMA_ERROR(method, "Only functions are allowed here."); + if (method->resolve_status == RESOLVE_RUNNING) + { + SEMA_ERROR(method, "Recursive definition of method, this is not allowed."); + return decl_poison(method); + } + method->resolve_status = RESOLVE_RUNNING; + if (method->decl_kind != DECL_FUNC) + { + SEMA_ERROR(method, "Only functions are allowed here."); + return decl_poison(method); + } if (method->func_decl.type_parent) { - RETURN_SEMA_ERROR(type_infoptr(method->func_decl.type_parent), "Interfaces should not be declared as methods."); + SEMA_ERROR(type_infoptr(method->func_decl.type_parent), "Interfaces should not be declared as methods."); + return decl_poison(method); } method->func_decl.attr_interface_method = true; bool erase = false; @@ -974,10 +984,10 @@ static bool sema_analyse_interface(SemaContext *context, Decl *decl, bool *erase { SEMA_ERROR(method, "Duplicate definition of method '%s'.", name); SEMA_NOTE(functions[j], "The previous definition was here."); - decl_poison(method); - return false; + return decl_poison(method); } } + method->resolve_status = RESOLVE_DONE; } return true; } @@ -1758,24 +1768,21 @@ bool sema_decl_if_cond(SemaContext *context, Decl *decl) return false; } -INLINE Attr* method_find_overload_attribute(Decl *method) +INLINE SourceSpan method_find_overload_span(Decl *method) { - FOREACH(Attr *, attr, method->attributes) - { - if (attr->attr_kind == ATTRIBUTE_OPERATOR) return attr; - } - UNREACHABLE + assert(method->resolved_attributes && method->attrs_resolved); + return method->attrs_resolved->overload; } -static inline bool -unit_add_base_extension_method(SemaContext *context, CompilationUnit *unit, Type *parent_type, Decl *method) +static inline bool unit_add_base_extension_method(SemaContext *context, CompilationUnit *unit, Type *parent_type, Decl *method) { // We don't support operator overloading on base types, because // there seems little use for it frankly. if (method->operator) { - RETURN_SEMA_ERROR(method_find_overload_attribute(method), - "Only user-defined types support operator oveloading."); + sema_error_at(context, method_find_overload_span(method), + "Only user-defined types support operator oveloading."); + return false; } // Add it to the right list of extensions. @@ -1835,8 +1842,8 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type Decl *other = sema_find_operator(context, parent_type, operator); if (other) { - Attr *attr = method_find_overload_attribute(method); - SEMA_ERROR(attr->exprs[0], "This operator is already defined for '%s'.", parent_type->name); + SourceSpan span = method_find_overload_span(method); + sema_error_at(context, span, "This operator is already defined for '%s'.", parent_type->name); SEMA_NOTE(other, "The previous definition was here."); return false; } @@ -2340,7 +2347,7 @@ static bool update_call_abi_from_string(SemaContext *context, Decl *decl, Expr * /** * Analyse almost all attributes. */ -static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, AttributeDomain domain, bool *erase_decl) +static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_data, Decl *decl, Attr *attr, AttributeDomain domain, bool *erase_decl) { AttributeType type = attr->attr_kind; assert(type >= 0 && type < NUMBER_OF_ATTRIBUTES); @@ -2384,6 +2391,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_REFLECT] = ATTR_FUNC | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES, [ATTRIBUTE_SAFEMACRO] = ATTR_MACRO, [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_GLOBAL, + [ATTRIBUTE_TAG] = ATTR_BITSTRUCT_MEMBER | ATTR_MEMBER | USER_DEFINED_TYPES | CALLABLE_TYPE, [ATTRIBUTE_TEST] = ATTR_FUNC, [ATTRIBUTE_UNUSED] = (AttributeDomain)~(ATTR_CALL), [ATTRIBUTE_USED] = (AttributeDomain)~(ATTR_CALL), @@ -2400,7 +2408,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, // No attribute has more than one argument right now. unsigned args = vec_size(attr->exprs); - if (args > 1 && type != ATTRIBUTE_LINK) + if (args > 1 && type != ATTRIBUTE_LINK && type != ATTRIBUTE_TAG) { SEMA_ERROR(attr->exprs[1], "Too many arguments for the attribute."); return false; @@ -2416,7 +2424,12 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, // These are pseudo-attributes and are processed separately. UNREACHABLE; case ATTRIBUTE_DEPRECATED: + if (attr_data->deprecated) + { + RETURN_SEMA_ERROR(attr, "There can't be more than a single '@deprecated' tag."); + } // We expect an optional string. + attr_data->deprecated = ""; if (expr) { if (!sema_analyse_expr(context, expr)) return false; @@ -2424,8 +2437,8 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, { RETURN_SEMA_ERROR(expr, "Expected a constant string value as argument."); } + attr_data->deprecated = expr->const_expr.bytes.ptr; } - decl->is_deprecated = true; return true; case ATTRIBUTE_OPTIONAL: decl->func_decl.attr_optional = true; @@ -2447,6 +2460,29 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, case ATTRIBUTE_BENCHMARK: decl->func_decl.attr_benchmark = true; break; + case ATTRIBUTE_TAG: + { + decl->has_tag = true; + if (args != 2) RETURN_SEMA_ERROR(attr, "'@tag' requires two arguments."); + Expr *string = attr->exprs[0]; + Expr *val = attr->exprs[1]; + if (!sema_analyse_expr(context, string)) return false; + if (!sema_cast_const(string) || !expr_is_const_string(string)) RETURN_SEMA_ERROR(string, "Expected a constant string here, usage is: '@tag(name, value)'."); + if (!sema_analyse_expr(context, val)) return false; + if (!sema_cast_const(val) || !expr_is_const(val)) RETURN_SEMA_ERROR(val, "Expected a constant value here, usage is: '@tag(name, value)'."); + const char *name = string->const_expr.bytes.ptr; + FOREACH_IDX(i, Attr *, tag, attr_data->tags) + { + // Overwrite if already found + if (str_eq(tag->exprs[0]->const_expr.bytes.ptr, name)) + { + attr_data->tags[i] = attr; + return true; + } + } + vec_add(attr_data->tags, attr); + return true; + } case ATTRIBUTE_TEST: decl->func_decl.attr_test = true; break; @@ -2454,6 +2490,10 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, { assert(decl->decl_kind == DECL_FUNC || decl->decl_kind == DECL_MACRO); if (!expr) goto FAILED_OP_TYPE; + if (decl->operator) + { + RETURN_SEMA_ERROR(attr, "This method already has overload, it can't match multiple ones."); + } switch (expr->expr_kind) { case EXPR_IDENTIFIER: @@ -2467,10 +2507,10 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, default: goto FAILED_OP_TYPE; } + attr_data->overload = attr->span; if (!decl->func_decl.type_parent) { - SEMA_ERROR(expr, "@operator(...) can only be used with methods."); - return false; + RETURN_SEMA_ERROR(expr, "@operator(...) can only be used with methods."); } return true; FAILED_OP_TYPE: @@ -2548,21 +2588,19 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, Expr *cond = args > 1 ? attr->exprs[0] : NULL; if (cond && !sema_analyse_expr(context, cond)) return false; int start = 0; - decl->has_link = true; + bool has_link = true; if (cond && expr_is_const_bool(cond)) { start = 1; - decl->has_link = cond->const_expr.b; + has_link = cond->const_expr.b; } for (unsigned i = start; i < args; i++) { Expr *string = attr->exprs[i]; if (!sema_analyse_expr(context, string)) return false; if (!expr_is_const_string(string)) RETURN_SEMA_ERROR(string, "Expected a constant string here, usage is: '@link(cond1, link1, link2, ...)'."); + if (has_link) vec_add(attr_data->links, string->const_expr.bytes.ptr); } - // Erase if not applicable. - if (start == 1) attr->exprs[0] = NULL; - if (!decl->has_link) attr->exprs = NULL; return true; case ATTRIBUTE_INIT: decl->func_decl.attr_init = true; @@ -2601,7 +2639,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, { case ATTRIBUTE_SECTION: if (!sema_check_section(context, attr)) return false; - decl->section_id = global_context_register_section(expr->const_expr.bytes.ptr); + attr_data->section = expr->const_expr.bytes.ptr; break; case ATTRIBUTE_EXTERN: decl->has_extname = true; @@ -2711,7 +2749,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, } -static inline bool sema_analyse_custom_attribute(SemaContext *context, Decl *decl, Attr *attr, AttributeDomain domain, +static inline bool sema_analyse_custom_attribute(SemaContext *context, ResolvedAttrData *attr_data_ref, Decl *decl, Attr *attr, AttributeDomain domain, Decl *top, bool *erase_decl) { // Custom attributes. @@ -2729,6 +2767,7 @@ static inline bool sema_analyse_custom_attribute(SemaContext *context, Decl *dec // Handle the case where the current function is the declaration itself. if (context->call_env.kind == CALL_ENV_ATTR && context->call_env.attr_declaration == attr_decl) { + decl_poison(attr_decl); RETURN_SEMA_ERROR(attr_decl, "Recursive declaration of attribute '%s' – it contains itself.", attr_decl->name); } @@ -2777,7 +2816,7 @@ static inline bool sema_analyse_custom_attribute(SemaContext *context, Decl *dec } // Now we've added everything to the evaluation context, so we can (recursively) // apply it to the contained attributes, which in turn may be derived attributes. - if (!sema_analyse_attributes_inner(&eval_context, decl, attributes, domain, top ? top : attr_decl, erase_decl)) goto ERR; + if (!sema_analyse_attributes_inner(&eval_context, attr_data_ref, decl, attributes, domain, top ? top : attr_decl, erase_decl)) goto ERR; // Then destroy the eval context. sema_context_destroy(&eval_context); // Stop evaluating on erase. @@ -2788,8 +2827,8 @@ ERR: } // TODO consider doing this evaluation early, it should be possible. -static bool sema_analyse_attributes_inner(SemaContext *context, Decl *decl, Attr **attrs, AttributeDomain domain, - Decl *top, bool *erase_decl) +static bool sema_analyse_attributes_inner(SemaContext *context, ResolvedAttrData *attr_data_ref, Decl *decl, Attr **attrs, AttributeDomain domain, + Decl *top, bool *erase_decl) { // Detect cycles of the type @Foo = @BarCyclic, @BarCyclic = @BarCyclic if (context->macro_call_depth > 1024) @@ -2803,12 +2842,12 @@ static bool sema_analyse_attributes_inner(SemaContext *context, Decl *decl, Attr { if (attr->is_custom) { - if (!sema_analyse_custom_attribute(context, decl, attr, domain, top, erase_decl)) return false; + if (!sema_analyse_custom_attribute(context, attr_data_ref, decl, attr, domain, top, erase_decl)) return false; } else { // The simple case, we have a built in attribute: - if (!sema_analyse_attribute(context, decl, attr, domain, erase_decl)) return false; + if (!sema_analyse_attribute(context, attr_data_ref, decl, attr, domain, erase_decl)) return false; } if (*erase_decl) return true; } @@ -2818,7 +2857,21 @@ static bool sema_analyse_attributes_inner(SemaContext *context, Decl *decl, Attr static bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **attrs, AttributeDomain domain, bool *erase_decl) { - return sema_analyse_attributes_inner(context, decl, attrs, domain, NULL, erase_decl); + ResolvedAttrData data = { .tags = NULL, .overload = INVALID_SPAN }; + if (!sema_analyse_attributes_inner(context, &data, decl, attrs, domain, NULL, erase_decl)) return false; + if (*erase_decl) return true; + decl->resolved_attributes = true; + if (data.tags || data.deprecated || data.links || data.section || data.overload.row) + { + ResolvedAttrData *copy = MALLOCS(ResolvedAttrData); + *copy = data; + decl->attrs_resolved = copy; + } + else + { + decl->attrs_resolved = NULL; + } + return true; } static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, @@ -3756,11 +3809,12 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) : RESOLVE_TYPE_DEFAULT)) return decl_poison(decl); Type *type = decl->type = type_info->type; - if (!sema_analyse_decl_type(context, decl->type, type_info->span)) return decl_poison(decl); + if (!sema_analyse_decl_type(context, type, type_info->span)) return decl_poison(decl); type = type_no_optional(type); if (type_is_user_defined(type) && type->decl) { + if (!sema_analyse_decl(context, type->decl)) return false; sema_display_deprecated_warning_on_use(context, type->decl, type_info->span); } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 913386345..1e95fc25a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -588,6 +588,7 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_SLICE_ASSIGN: case EXPR_SLICE_COPY: case EXPR_STRINGIFY: + case EXPR_TAGOF: case EXPR_TERNARY: case EXPR_TRY_UNWRAP: case EXPR_TRY_UNWRAP_CHAIN: @@ -618,6 +619,7 @@ static bool expr_may_ref(Expr *expr) case EXPR_CT_IDENT: case EXPR_EMBED: case EXPR_DEFAULT_ARG: + case EXPR_TAGOF: return false; case EXPR_OTHER_CONTEXT: return expr_may_ref(expr->expr_other_context.inner); @@ -2450,6 +2452,7 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl type_flatten(exprptr(expr->call_expr.function)->type), optional, no_match_ref); } + if (!sema_analyse_decl(context, decl)) return false; switch (decl->decl_kind) { case DECL_MACRO: @@ -2476,11 +2479,52 @@ bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl } } +static inline bool sema_expr_analyse_tagof(SemaContext *context, Expr *expr) +{ + expr->call_expr.arguments = sema_expand_vasplat_exprs(context, expr->call_expr.arguments); + Expr **args = expr->call_expr.arguments; + unsigned arg_count = vec_size(args); + Expr *tag = exprptr(expr->call_expr.function); + Decl *decl = tag->tag_of_expr.type; + bool is_has = tag->tag_of_expr.property == TYPE_PROPERTY_HAS_TAGOF; + const char *name = is_has ? "has_tagof" : "tagof"; + if (arg_count != 1) RETURN_SEMA_ERROR(expr, "Expected a single string argument to '%s'.", name); + Expr *key = args[0]; + if (!sema_analyse_expr(context, key)) return false; + if (!sema_cast_const(key) || !expr_is_const_string(key)) + { + RETURN_SEMA_ERROR(key, "The tag name should be a string constant."); + } + assert(decl->resolved_attributes); + ResolvedAttrData *attrs = decl->attrs_resolved; + if (!attrs || !attrs->tags) goto NOT_FOUND; + const char *tagname = key->const_expr.bytes.ptr; + Expr *value = NULL; + FOREACH(Attr *, attr, attrs->tags) + { + if (str_eq(attr->exprs[0]->const_expr.bytes.ptr, tagname)) value = attr->exprs[1]; + } + if (!value) goto NOT_FOUND; + if (is_has) + { + expr_rewrite_const_bool(expr, type_bool, true); + return true; + } + expr_replace(expr, expr_copy(value)); + return true; +NOT_FOUND: + if (is_has) + { + expr_rewrite_const_bool(expr, type_bool, false); + return true; + } + RETURN_SEMA_ERROR(expr, "The tag '%s' is not defined, always check with '.has_tagof'."); +} + static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref) { if (no_match_ref) *no_match_ref = true; Expr *func_expr = exprptr(expr->call_expr.function); - if (!sema_analyse_expr_lvalue_fold_const(context, func_expr)) return false; if (func_expr->expr_kind == EXPR_MACRO_BODY_EXPANSION) { @@ -2491,6 +2535,8 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool Expr *struct_var = NULL; switch (func_expr->expr_kind) { + case EXPR_TAGOF: + return sema_expr_analyse_tagof(context, expr); case EXPR_BUILTIN: return sema_expr_analyse_builtin_call(context, expr); case EXPR_IDENTIFIER: @@ -3391,6 +3437,11 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e TypeProperty type_property = type_property_by_name(name); switch (type_property) { + case TYPE_PROPERTY_TAGOF: + case TYPE_PROPERTY_HAS_TAGOF: + expr->expr_kind = EXPR_TAGOF; + expr->tag_of_expr = (ExprTagOf) { .type = decl, .property = type_property }; + return true; case TYPE_PROPERTY_NONE: break; case TYPE_PROPERTY_QNAMEOF: @@ -3869,6 +3920,8 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp case TYPE_PROPERTY_MEMBERSOF: case TYPE_PROPERTY_METHODSOF: case TYPE_PROPERTY_EXTNAMEOF: + case TYPE_PROPERTY_TAGOF: + case TYPE_PROPERTY_HAS_TAGOF: case TYPE_PROPERTY_NAMEOF: case TYPE_PROPERTY_QNAMEOF: case TYPE_PROPERTY_ASSOCIATED: @@ -4117,6 +4170,8 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper case TYPE_PROPERTY_PARAMS: case TYPE_PROPERTY_RETURNS: return type_is_func_ptr(type); + case TYPE_PROPERTY_TAGOF: + case TYPE_PROPERTY_HAS_TAGOF: case TYPE_PROPERTY_EXTNAMEOF: return !type_is_builtin(type->type_kind); } @@ -4222,6 +4277,11 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, if (!sema_resolve_type_decl(context, type)) return false; sema_expr_rewrite_to_type_nameof(expr, type, TOKEN_CT_EXTNAMEOF); return true; + case TYPE_PROPERTY_TAGOF: + case TYPE_PROPERTY_HAS_TAGOF: + expr->expr_kind = EXPR_TAGOF; + expr->tag_of_expr = (ExprTagOf) { .type = type->decl, .property = property }; + return true; case TYPE_PROPERTY_NONE: return false; } @@ -8876,6 +8936,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_POST_UNARY: case EXPR_TYPEID: case EXPR_TYPEID_INFO: + case EXPR_TAGOF: if (!sema_analyse_expr(active_context, main_expr)) return false; break; } @@ -8901,7 +8962,7 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty return true; case TOKEN_CT_VAARG: { - unsigned index; + unsigned index = 0; // A normal argument, this means we only evaluate it once. ASSIGN_EXPR_OR_RET(Expr *arg_expr, sema_expr_analyse_ct_arg_index(context, exprptr(expr->ct_arg_expr.arg), &index, true), false); @@ -8913,7 +8974,7 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty FOREACH(Decl *, val, context->macro_params) { if (!val) continue; - if (val->va_index == index && val->var.kind == VARDECL_PARAM) + if (val->var.va_index == index && val->var.kind == VARDECL_PARAM) { decl = val; break; @@ -8929,7 +8990,7 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty } decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM, arg_expr->span); decl->var.init_expr = arg_expr; - decl->va_index = (uint16_t)index; + decl->var.va_index = (uint16_t)index; vec_add(context->macro_params, decl); } // Replace with the identifier. @@ -8976,7 +9037,7 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty FOREACH(Decl *, val, context->macro_params) { if (!val) continue; - if (val->var.kind == VARDECL_PARAM_REF && val->va_index == index) + if (val->var.kind == VARDECL_PARAM_REF && val->var.va_index == index) { decl = val; break; @@ -8990,7 +9051,7 @@ static inline bool sema_expr_analyse_ct_arg(SemaContext *context, Type *infer_ty expr_insert_addr(arg_expr); decl = decl_new_generated_var(arg_expr->type, VARDECL_PARAM_REF, arg_expr->span); decl->var.init_expr = arg_expr; - decl->va_index = (uint16_t)index; + decl->var.va_index = (uint16_t)index; vec_add(context->macro_params, decl); } // Replace with the identifier. @@ -9493,6 +9554,7 @@ static inline bool sema_expr_analyse_builtin(SemaContext *context, Expr *expr, b return true; } + static inline bool sema_expr_analyse_compound_literal(SemaContext *context, Expr *expr) { TypeInfo *type_info = expr->expr_compound_literal.type_info; @@ -9532,6 +9594,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_MACRO_BODY: case EXPR_DEFAULT_ARG: UNREACHABLE + case EXPR_TAGOF: + RETURN_SEMA_ERROR(expr, "Expected '()' after this."); case EXPR_OTHER_CONTEXT: context = expr->expr_other_context.context; expr_replace(expr, expr->expr_other_context.inner); @@ -9837,19 +9901,18 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) return false; } break; + case EXPR_TAGOF: + RETURN_SEMA_ERROR(expr, "A tag name must be given."); case EXPR_BUILTIN: - SEMA_ERROR(expr, "A builtin must be followed by ()."); - return false; + RETURN_SEMA_ERROR(expr, "A builtin must be followed by ()."); case EXPR_ACCESS: if (expr->access_expr.ref->decl_kind == DECL_FUNC) { - SEMA_ERROR(expr, "A function name must be followed by '(' or preceded by '&'."); - return false; + RETURN_SEMA_ERROR(expr, "A function name must be followed by '(' or preceded by '&'."); } if (expr->access_expr.ref->decl_kind == DECL_MACRO) { - SEMA_ERROR(expr, "A macro name must be followed by '('."); - return false; + RETURN_SEMA_ERROR(expr, "A macro name must be followed by '('."); } // We may have kept FOO.x.y as a reference, fold it now if y is not an aggregate. sema_expr_flatten_const_ident(expr->access_expr.parent); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 7660b5cfc..0f8d0b457 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -131,15 +131,17 @@ INLINE Attr* attr_find_kind(Attr **attrs, AttributeType attr_type) INLINE void sema_display_deprecated_warning_on_use(SemaContext *context, Decl *decl, SourceSpan span) { - if (!decl->is_deprecated) return; + assert(decl->resolve_status == RESOLVE_DONE); + if (!decl->resolved_attributes || !decl->attrs_resolved || !decl->attrs_resolved->deprecated) return; + const char *msg = decl->attrs_resolved->deprecated; + // Prevent multiple reports - decl->is_deprecated = false; - Attr *attr = attr_find_kind(decl->attributes, ATTRIBUTE_DEPRECATED); - assert(attr); - if (attr->exprs) + decl->attrs_resolved->deprecated = NULL; + + if (active_target.silence_deprecation) return; + if (msg[0]) { - const char *comment_string = attr->exprs[0]->const_expr.bytes.ptr; - sema_warning_at(span, "'%s' is deprecated: %s.", decl->name, comment_string); + sema_warning_at(span, "'%s' is deprecated: %s.", decl->name, msg); return; } sema_warning_at(span, "'%s' is deprecated.", decl->name); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 1e45a681d..279c3c41e 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -248,33 +248,18 @@ RETRY: sema_trace_type_liveness(expr->type); switch (expr->expr_kind) { + case NON_RUNTIME_EXPR: case EXPR_SUBSCRIPT_ASSIGN: case EXPR_OPERATOR_CHARS: case EXPR_VASPLAT: - case EXPR_POISONED: - case EXPR_COMPILER_CONST: - case EXPR_CT_ARG: - case EXPR_CT_CALL: - case EXPR_CT_DEFINED: - case EXPR_CT_IS_CONST: - case EXPR_CT_EVAL: - case EXPR_CT_IDENT: - case EXPR_ANYSWITCH: case EXPR_GENERIC_IDENT: case EXPR_EMBED: - case EXPR_CT_CASTABLE: - case EXPR_CT_AND_OR: - case EXPR_CT_CONCAT: - case EXPR_CT_APPEND: case EXPR_MACRO_BODY: case EXPR_OTHER_CONTEXT: UNREACHABLE case EXPR_DESIGNATOR: sema_trace_expr_liveness(expr->designator_expr.value); return; - case EXPR_HASH_IDENT: - case EXPR_STRINGIFY: - case EXPR_TYPEINFO: case EXPR_BUILTIN: return; case EXPR_ACCESS: @@ -578,6 +563,8 @@ RETRY: case DECL_VAR: switch (decl->var.kind) { + case VARDECL_PARAM_CT_TYPE: + case VARDECL_LOCAL_CT_TYPE: case VARDECL_REWRAPPED: case VARDECL_UNWRAPPED: break; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 65f9a96a3..f7c3a02cb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -631,6 +631,7 @@ static inline bool sema_expr_valid_try_expression(Expr *expr) case EXPR_POST_UNARY: case EXPR_TERNARY: case EXPR_LAST_FAULT: + case EXPR_TAGOF: return false; case EXPR_BITACCESS: case EXPR_BUILTIN: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index f0f97f534..4bedc2ef9 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -176,6 +176,8 @@ void symtab_init(uint32_t capacity) type_property_list[TYPE_PROPERTY_QNAMEOF] = KW_DEF("qnameof"); type_property_list[TYPE_PROPERTY_RETURNS] = KW_DEF("returns"); type_property_list[TYPE_PROPERTY_SIZEOF] = KW_DEF("sizeof"); + type_property_list[TYPE_PROPERTY_TAGOF] = KW_DEF("tagof"); + type_property_list[TYPE_PROPERTY_HAS_TAGOF] = KW_DEF("has_tagof"); type_property_list[TYPE_PROPERTY_VALUES] = KW_DEF("values"); builtin_list[BUILTIN_ABS] = KW_DEF("abs"); @@ -347,6 +349,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_SAFEMACRO] = KW_DEF("@safemacro"); attribute_list[ATTRIBUTE_SECTION] = KW_DEF("@section"); attribute_list[ATTRIBUTE_TEST] = KW_DEF("@test"); + attribute_list[ATTRIBUTE_TAG] = KW_DEF("@tag"); attribute_list[ATTRIBUTE_UNUSED] = KW_DEF("@unused"); attribute_list[ATTRIBUTE_USED] = KW_DEF("@used"); attribute_list[ATTRIBUTE_WASM] = KW_DEF("@wasm"); diff --git a/test/test_suite/compile_time_introspection/tag.c3t b/test/test_suite/compile_time_introspection/tag.c3t new file mode 100644 index 000000000..7a0c592a1 --- /dev/null +++ b/test/test_suite/compile_time_introspection/tag.c3t @@ -0,0 +1,34 @@ +// #target: macos-x64 +module test; +struct Foo @tag("foo", 3) +{ + int a @tag("foo", "hello"); +} +bitstruct Bar : int +{ + int a: 1..4 @tag("foo", "a"); +} +fn void main() +{ + $if Foo.has_tagof("foo"): + int a = Foo.tagof("foo"); + $endif; + String b = Foo.a.tagof("foo"); + String c = Bar.a.tagof("foo"); +} + +/* #expect: test.ll + + +@.str = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@.str.1 = private unnamed_addr constant [2 x i8] c"a\00", align 1 + +entry: + %a = alloca i32, align 4 + %b = alloca %"char[]", align 8 + %c = alloca %"char[]", align 8 + store i32 3, ptr %a, align 4 + store %"char[]" { ptr @.str, i64 5 }, ptr %b, align 8 + store %"char[]" { ptr @.str.1, i64 1 }, ptr %c, align 8 + ret void +}