diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 8b7379e76..727a76da2 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2264,6 +2264,7 @@ static inline Type *type_flatten(Type *type) return type; } } + static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_TYPEID; } static inline bool type_kind_is_signed(TypeKind kind) { return kind >= TYPE_I8 && kind < TYPE_U8; } static inline bool type_kind_is_unsigned(TypeKind kind) { return kind >= TYPE_U8 && kind < TYPE_IXX; } diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 856f6126f..9965569f9 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -468,6 +468,7 @@ typedef enum TOKEN_CT_ELSE, // $else TOKEN_CT_ENDIF, // $endif TOKEN_CT_ENDSWITCH, // $endswitch + TOKEN_CT_EXTNAMEOF, // $extnameof TOKEN_CT_IF, // $if TOKEN_CT_NAMEOF, // $nameof TOKEN_CT_OFFSETOF, // $offsetof diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 240dacab4..d40b8017c 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1417,6 +1417,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_SIZEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, + [TOKEN_CT_EXTNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_NAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_QNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 827143acb..0ccf1c4bd 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1054,6 +1054,7 @@ Ast *parse_stmt(Context *context) case TOKEN_LBRAPIPE: case TOKEN_CT_OFFSETOF: case TOKEN_CT_ALIGNOF: + case TOKEN_CT_EXTNAMEOF: case TOKEN_CT_SIZEOF: case TOKEN_CT_QNAMEOF: case TOKEN_CT_NAMEOF: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 30067fa2c..1883a1f2a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -5903,14 +5903,49 @@ static inline bool sema_expr_analyse_ct_nameof(Context *context, Type *to, Expr Expr *first_expr = elements[0]; if (!sema_analyse_expr_value(context, NULL, first_expr)) return false; - bool qualified = expr->ct_call_expr.token_type == TOKEN_CT_QNAMEOF; + TokenType name_type = expr->ct_call_expr.token_type; - if (first_expr->expr_kind == EXPR_TYPEINFO) + Decl *decl = NULL; + Type *type = NULL; + if (first_expr->expr_kind == EXPR_CONST && first_expr->const_expr.kind == TYPE_STRLIT) + { + ExprFlatElement *path_elements = NULL; + if (!sema_analyse_identifier_path_string(context, first_expr, &decl, &type, &path_elements)) return false; + if (path_elements) + { + SEMA_ERROR(first_expr, "You cannot take the name of sub elements by string name."); + return false; + } + } + else if (first_expr->expr_kind == EXPR_TYPEINFO) + { + type = first_expr->type_expr->type->canonical; + } + else + { + if (first_expr->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(first_expr, "Expected an identifier or a string."); + return false; + } + decl = first_expr->identifier_expr.decl; + } + if (type) { - Type *type = first_expr->type_expr->type->canonical; if (!sema_resolve_type(context, type)) return false; + // TODO type_is_builtin is wrong also this does not cover virtual. - if (!qualified || type_is_builtin(type->type_kind)) + if (name_type == TOKEN_CT_EXTNAMEOF) + { + if (!type_is_user_defined(type)) + { + SEMA_ERROR(first_expr, "Only user defined types have an external name."); + return false; + } + expr_rewrite_to_string(expr, type->decl->extname ?: type->decl->external_name); + return true; + } + if (name_type == TOKEN_CT_NAMEOF || type_is_builtin(type->type_kind)) { expr_rewrite_to_string(expr, type->name); return true; @@ -5922,14 +5957,22 @@ static inline bool sema_expr_analyse_ct_nameof(Context *context, Type *to, Expr expr_rewrite_to_string(expr, scratch_buffer_interned()); return true; } - if (first_expr->expr_kind != EXPR_IDENTIFIER) - { - SEMA_ERROR(first_expr, "Expected an identifier."); - return false; - } - Decl *decl = first_expr->identifier_expr.decl; - if (!decl->module || !qualified || decl_is_local(decl)) + assert(decl); + + if (!sema_analyse_decl(context, decl)) return false; + + if (name_type == TOKEN_CT_EXTNAMEOF) + { + if (!decl->external_name) + { + SEMA_ERROR(first_expr, "'%s' does not have an external name.", decl->name); + return false; + } + expr_rewrite_to_string(expr, decl->extname ?: decl->external_name); + return true; + } + if (!decl->module || name_type == TOKEN_CT_NAMEOF || decl_is_local(decl)) { expr_rewrite_to_string(expr, decl->name); return true; @@ -6069,8 +6112,8 @@ static inline bool sema_expr_analyse_ct_call(Context *context, Type *to, Expr *e case TOKEN_CT_OFFSETOF: return sema_expr_analyse_ct_offsetof(context, to, expr); case TOKEN_CT_QNAMEOF: - return sema_expr_analyse_ct_nameof(context, to, expr); case TOKEN_CT_NAMEOF: + case TOKEN_CT_EXTNAMEOF: return sema_expr_analyse_ct_nameof(context, to, expr); default: UNREACHABLE diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index e3b1bea11..2cb5df578 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -345,6 +345,8 @@ const char *token_type_to_string(TokenType type) return "$endif"; case TOKEN_CT_ENDSWITCH: return "$endswitch"; + case TOKEN_CT_EXTNAMEOF: + return "$extnameof"; case TOKEN_CT_IF: return "$if"; case TOKEN_CT_NAMEOF: diff --git a/test/test_suite/compile_time_introspection/nameof.c3t b/test/test_suite/compile_time_introspection/nameof.c3t new file mode 100644 index 000000000..defc32050 --- /dev/null +++ b/test/test_suite/compile_time_introspection/nameof.c3t @@ -0,0 +1,68 @@ +// #target: x64-darwin +module mymodule; + +extern func void printf(char *, ...); + +struct Foo { } + +int b; + +func void main() +{ + printf("%s\n", $qnameof(Foo)); + printf("%s\n", $qnameof("Foo")); + printf("%s\n", $nameof(Foo)); + printf("%s\n", $nameof("mymodule::Foo")); + printf("%s\n", $extnameof(Foo)); + printf("%s\n", $extnameof("Foo")); + + printf("%s\n", $qnameof(b)); + printf("%s\n", $qnameof("b")); + printf("%s\n", $nameof(b)); + printf("%s\n", $nameof("mymodule::b")); + printf("%s\n", $extnameof(b)); + printf("%s\n", $extnameof("b")); + + int a; + + printf("%s\n", $qnameof(a)); + printf("%s\n", $qnameof("a")); + printf("%s\n", $nameof(a)); + printf("%s\n", $nameof("a")); + +} + +// #expect: mymodule.ll + +@.str = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.1 = private constant [14 x i8] c"mymodule::Foo\00", align 1 +@.str.2 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.3 = private constant [14 x i8] c"mymodule::Foo\00", align 1 +@.str.4 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.5 = private constant [4 x i8] c"Foo\00", align 1 +@.str.6 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.7 = private constant [4 x i8] c"Foo\00", align 1 +@.str.8 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.9 = private constant [13 x i8] c"mymodule.Foo\00", align 1 +@.str.10 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.11 = private constant [13 x i8] c"mymodule.Foo\00", align 1 +@.str.12 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.13 = private constant [12 x i8] c"mymodule::b\00", align 1 +@.str.14 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.15 = private constant [12 x i8] c"mymodule::b\00", align 1 +@.str.16 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.17 = private constant [2 x i8] c"b\00", align 1 +@.str.18 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.19 = private constant [2 x i8] c"b\00", align 1 +@.str.20 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.21 = private constant [11 x i8] c"mymodule.b\00", align 1 +@.str.22 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.23 = private constant [11 x i8] c"mymodule.b\00", align 1 +@.str.24 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.25 = private constant [2 x i8] c"a\00", align 1 +@.str.26 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.27 = private constant [2 x i8] c"a\00", align 1 +@.str.28 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.29 = private constant [2 x i8] c"a\00", align 1 +@.str.30 = private constant [4 x i8] c"%s\0A\00", align 1 +@.str.31 = private constant [2 x i8] c"a\00", align 1 \ No newline at end of file diff --git a/test/test_suite/compile_time_introspection/nameof_err.c3 b/test/test_suite/compile_time_introspection/nameof_err.c3 new file mode 100644 index 000000000..530137138 --- /dev/null +++ b/test/test_suite/compile_time_introspection/nameof_err.c3 @@ -0,0 +1,15 @@ +func void main() +{ + int a; + $extnameof(a); // #error: 'a' does not have an external name. +} +func void main2() +{ + int a; + $extnameof("a"); // #error: 'a' does not have an external name. +} + +func void main3() +{ + $extnameof("int"); // #error: Only user defined types have an external name. +}