From fb4a2317036de68debe9be8bb2cda105a1d91bcd Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 20 Aug 2024 14:24:12 +0200 Subject: [PATCH] Add `$member.get(value)` to replace `value.$eval($member.nameof)` --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 4 +- src/compiler/copying.c | 3 + src/compiler/enums.h | 2 + src/compiler/expr.c | 3 + src/compiler/llvm_codegen_expr.c | 1 + src/compiler/sema_expr.c | 101 +++++++++++++++--- src/compiler/sema_liveness.c | 1 + src/compiler/sema_stmts.c | 1 + src/compiler/symtab.c | 1 + test/test_suite/assert/assert_with_void.c3 | 2 +- .../generic/generic_lambda_complex.c3t | 2 +- test/unit/regression/methodsof.c3 | 2 + 13 files changed, 106 insertions(+), 18 deletions(-) diff --git a/releasenotes.md b/releasenotes.md index 93c2bd576..4e7d5250a 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -35,6 +35,7 @@ - `$exec` may now provide a stdin parameter. - Introduce `$vaarg[...]` syntax and deprecate the old `$vaarg(...)`. - Similar change to `$vasplat`: `$vasplat` and `$vasplat[1..]`. +- Add `$member.get(value)` to replace `value.$eval($member.nameof)` ### Fixes diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7296e11f3..74c1c001a 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1090,7 +1090,8 @@ struct Expr_ Expr *inner_expr; // 8 Decl *lambda_expr; // 8 ExprMacroBlock macro_block; // 24 - ExprMacroBody macro_body_expr; // 16; + ExprMacroBody macro_body_expr; // 16 + Decl *member_get_expr; // 8 OperatorOverload overload_expr; // 4 ExprPointerOffset pointer_offset_expr; ExprGuard rethrow_expr; // 16 @@ -3265,6 +3266,7 @@ INLINE void expr_set_span(Expr *expr, SourceSpan loc) case EXPR_MACRO_BODY: case EXPR_DEFAULT_ARG: case EXPR_TAGOF: + case EXPR_MEMBER_GET: break; } } diff --git a/src/compiler/copying.c b/src/compiler/copying.c index c3071bc6c..43f3dd604 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -317,6 +317,9 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) MACRO_COPY_DECL(expr->lambda_expr); } return expr; + case EXPR_MEMBER_GET: + fixup_decl(c, &source_expr->member_get_expr); + break; case EXPR_SWIZZLE: MACRO_COPY_EXPRID(expr->swizzle_expr.parent); return expr; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index efa8aa35d..8628c7051 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -788,6 +788,7 @@ typedef enum EXPR_MACRO_BLOCK, EXPR_MACRO_BODY, EXPR_MACRO_BODY_EXPANSION, + EXPR_MEMBER_GET, EXPR_NOP, EXPR_OPERATOR_CHARS, EXPR_OPTIONAL, @@ -1363,6 +1364,7 @@ typedef enum TYPE_PROPERTY_ASSOCIATED, TYPE_PROPERTY_ELEMENTS, TYPE_PROPERTY_EXTNAMEOF, + TYPE_PROPERTY_GET, TYPE_PROPERTY_INF, TYPE_PROPERTY_IS_EQ, TYPE_PROPERTY_IS_ORDERED, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 4a6ddced3..08a08ab11 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -79,6 +79,7 @@ bool expr_may_addr(Expr *expr) return expr_may_addr(expr->access_expr.parent); case EXPR_SUBSCRIPT: case EXPR_SLICE: + case EXPR_MEMBER_GET: return true; case EXPR_BENCHMARK_HOOK: case EXPR_TEST_HOOK: @@ -226,6 +227,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_SLICE_COPY: case EXPR_MACRO_BLOCK: case EXPR_RETHROW: + case EXPR_MEMBER_GET: return false; case EXPR_IDENTIFIER: { @@ -704,6 +706,7 @@ bool expr_is_pure(Expr *expr) case EXPR_TYPEID: case EXPR_TYPEINFO: case EXPR_LAST_FAULT: + case EXPR_MEMBER_GET: return true; case EXPR_VASPLAT: return true; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 5629849e0..ba15b62e9 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -7064,6 +7064,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_MACRO_BODY: case EXPR_OTHER_CONTEXT: case EXPR_DESIGNATOR: + case EXPR_MEMBER_GET: UNREACHABLE case EXPR_DEFAULT_ARG: llvm_emit_default_arg(c, value, expr); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c9305fa59..a7830a6ed 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -593,6 +593,7 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_GENERIC_IDENT: case EXPR_MACRO_BODY: case EXPR_LAST_FAULT: + case EXPR_MEMBER_GET: goto ERR; } UNREACHABLE @@ -618,6 +619,7 @@ static bool expr_may_ref(Expr *expr) case EXPR_EMBED: case EXPR_DEFAULT_ARG: case EXPR_TAGOF: + case EXPR_MEMBER_GET: return false; case EXPR_OTHER_CONTEXT: return expr_may_ref(expr->expr_other_context.inner); @@ -2519,6 +2521,57 @@ NOT_FOUND: RETURN_SEMA_ERROR(expr, "The tag '%s' is not defined, always check with '.has_tagof'.", tagname); } +INLINE bool sema_call_may_not_have_attributes(SemaContext *context, Expr *expr) +{ + if (expr->call_expr.attr_force_inline) + { + RETURN_SEMA_ERROR(expr, "'@inline' is not allowed here."); + } + if (expr->call_expr.attr_force_inline) + { + RETURN_SEMA_ERROR(expr, "'@noinline' is not allowed here."); + } + if (expr->call_expr.attr_force_inline) + { + RETURN_SEMA_ERROR(expr, "'@pure' is not allowed here."); + } + return true; +} +static inline bool sema_call_analyse_member_get(SemaContext *context, Expr *expr) +{ + if (vec_size(expr->call_expr.arguments) != 1) + { + RETURN_SEMA_ERROR(expr, "Expected a single argument to '.get'."); + } + if (!sema_call_may_not_have_attributes(context, expr)) return false; + Expr *get = exprptr(expr->call_expr.function); + Expr *inner = expr->call_expr.arguments[0]; + if (!sema_analyse_expr(context, inner)) return false; + Decl *decl = get->member_get_expr; + Type *type = type_flatten(inner->type); + if (type->type_kind != TYPE_STRUCT && type->type_kind != TYPE_UNION) + { + RETURN_SEMA_ERROR(inner, "This value does not match the member."); + } + Decl **members = type->decl->strukt.members; + ArrayIndex index = -1; + FOREACH_IDX(i, Decl *, member, members) + { + if (member == decl) + { + index = i; + break; + } + } + if (index == -1) + { + RETURN_SEMA_ERROR(inner, "This value does not match the member."); + } + expr->expr_kind = EXPR_ACCESS; + expr->access_expr = (ExprAccess) { .parent = inner, .ref = decl }; + expr->type = decl->type; + return true; +} static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref) { if (no_match_ref) *no_match_ref = true; @@ -2528,6 +2581,10 @@ static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool { return sema_call_analyse_body_expansion(context, expr); } + if (func_expr->expr_kind == EXPR_MEMBER_GET) + { + return sema_call_analyse_member_get(context, expr); + } bool optional = func_expr->type && IS_OPTIONAL(func_expr); Decl *decl; Expr *struct_var = NULL; @@ -3442,6 +3499,11 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e expr->expr_kind = EXPR_TAGOF; expr->tag_of_expr = (ExprTagOf) { .type = decl, .property = type_property }; return true; + case TYPE_PROPERTY_GET: + expr->expr_kind = EXPR_MEMBER_GET; + expr->member_get_expr = decl; + expr->type = type_void; + return true; case TYPE_PROPERTY_NONE: break; case TYPE_PROPERTY_QNAMEOF: @@ -3910,25 +3972,26 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp case TYPE_PROPERTY_NAMES: return sema_expr_rewrite_typeid_call(expr, typeid, TYPEID_INFO_NAMES, type_get_slice(type_string)); case TYPE_PROPERTY_ALIGNOF: - case TYPE_PROPERTY_INF: - case TYPE_PROPERTY_MIN: - case TYPE_PROPERTY_MAX: - case TYPE_PROPERTY_NAN: - case TYPE_PROPERTY_ELEMENTS: - case TYPE_PROPERTY_VALUES: - case TYPE_PROPERTY_RETURNS: - case TYPE_PROPERTY_PARAMS: - 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: + case TYPE_PROPERTY_ELEMENTS: + case TYPE_PROPERTY_EXTNAMEOF: + case TYPE_PROPERTY_GET: + case TYPE_PROPERTY_HAS_TAGOF: + case TYPE_PROPERTY_INF: case TYPE_PROPERTY_IS_EQ: case TYPE_PROPERTY_IS_ORDERED: case TYPE_PROPERTY_IS_SUBSTRUCT: + case TYPE_PROPERTY_MAX: + case TYPE_PROPERTY_MEMBERSOF: + case TYPE_PROPERTY_METHODSOF: + case TYPE_PROPERTY_MIN: + case TYPE_PROPERTY_NAMEOF: + case TYPE_PROPERTY_NAN: + case TYPE_PROPERTY_PARAMS: + case TYPE_PROPERTY_QNAMEOF: + case TYPE_PROPERTY_RETURNS: + case TYPE_PROPERTY_TAGOF: + case TYPE_PROPERTY_VALUES: // Not supported by dynamic typeid case TYPE_PROPERTY_NONE: return false; @@ -4096,6 +4159,8 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper { case TYPE_PROPERTY_NONE: return false; + case TYPE_PROPERTY_GET: + return type == type_member; case TYPE_PROPERTY_INF: case TYPE_PROPERTY_NAN: return type_is_float(type); @@ -4237,6 +4302,8 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, expr->type = type; expr->resolve_status = RESOLVE_DONE; return true; + case TYPE_PROPERTY_GET: + UNREACHABLE case TYPE_PROPERTY_MEMBERSOF: { AlignSize align; @@ -8943,6 +9010,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_TYPEID: case EXPR_TYPEID_INFO: case EXPR_TAGOF: + case EXPR_MEMBER_GET: if (!sema_analyse_expr(active_context, main_expr)) return false; break; } @@ -9599,6 +9667,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_SWIZZLE: case EXPR_MACRO_BODY: case EXPR_DEFAULT_ARG: + case EXPR_MEMBER_GET: UNREACHABLE case EXPR_TAGOF: RETURN_SEMA_ERROR(expr, "Expected '()' after this."); @@ -9900,6 +9969,8 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr) if (!expr_ok(expr)) return false; switch (expr->expr_kind) { + case EXPR_MEMBER_GET: + RETURN_SEMA_ERROR(expr, "Expected a parameter to 'get', e.g. '$member.get(value)'."); case EXPR_MACRO_BODY_EXPANSION: if (!expr->body_expansion_expr.first_stmt) { diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index e92bdb391..630840aa5 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -261,6 +261,7 @@ RETRY: case EXPR_EMBED: case EXPR_MACRO_BODY: case EXPR_OTHER_CONTEXT: + case EXPR_MEMBER_GET: UNREACHABLE case EXPR_DESIGNATOR: sema_trace_expr_liveness(expr->designator_expr.value); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index cc08742e9..064b98558 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -632,6 +632,7 @@ static inline bool sema_expr_valid_try_expression(Expr *expr) case EXPR_TERNARY: case EXPR_LAST_FAULT: case EXPR_TAGOF: + case EXPR_MEMBER_GET: return false; case EXPR_BITACCESS: case EXPR_BUILTIN: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 4bedc2ef9..6fc693bd1 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -160,6 +160,7 @@ void symtab_init(uint32_t capacity) type_property_list[TYPE_PROPERTY_ASSOCIATED] = KW_DEF("associated"); type_property_list[TYPE_PROPERTY_ELEMENTS] = KW_DEF("elements"); type_property_list[TYPE_PROPERTY_EXTNAMEOF] = KW_DEF("extnameof"); + type_property_list[TYPE_PROPERTY_GET] = KW_DEF("get"); type_property_list[TYPE_PROPERTY_INF] = KW_DEF("inf"); type_property_list[TYPE_PROPERTY_INNER] = KW_DEF("inner"); type_property_list[TYPE_PROPERTY_IS_EQ] = KW_DEF("is_eq"); diff --git a/test/test_suite/assert/assert_with_void.c3 b/test/test_suite/assert/assert_with_void.c3 index 905ffe81d..671803880 100644 --- a/test/test_suite/assert/assert_with_void.c3 +++ b/test/test_suite/assert/assert_with_void.c3 @@ -25,7 +25,7 @@ macro deep_print(a) io::print("{"); $foreach ($i, $m : $typeof(a).membersof) if ($i > 0) io::print(", "); - deep_print(a.$eval($m.nameof)); + deep_print($m.get(a)); $endforeach io::print("}"); $default: diff --git a/test/test_suite/generic/generic_lambda_complex.c3t b/test/test_suite/generic/generic_lambda_complex.c3t index feade9ee1..0493b91f6 100644 --- a/test/test_suite/generic/generic_lambda_complex.c3t +++ b/test/test_suite/generic/generic_lambda_complex.c3t @@ -74,7 +74,7 @@ fn void! TextTemplate.init(&self, String template, String tag_start = "{{", Stri .data = (String*)(data + $m.offsetof), }; $default: - $if $defined(self.data.$eval($m.nameof).as_stream): + $if $defined($m.get(self.data).as_stream): return TextTag{ .kind = TEMPLATE, .template = self.data.$eval($m.nameof).as_stream(), diff --git a/test/unit/regression/methodsof.c3 b/test/unit/regression/methodsof.c3 index 695584cee..ecf30caa4 100644 --- a/test/unit/regression/methodsof.c3 +++ b/test/unit/regression/methodsof.c3 @@ -71,6 +71,8 @@ fn void methodsof() Foo foo = { .i = 4, .b = true }; assert(Foo.$eval(Foo.methodsof[2])(&foo) == true); // Foo.xyz assert(Foo.$eval(Foo.methodsof[1])(&foo, 2) == 8); // Foo.bar + assert(Foo.$eval(Foo.methodsof[2])(&foo) == true); // Foo.xyz + assert(Foo.$eval(Foo.methodsof[1])(&foo, 2) == 8); // Foo.bar Foo foo2 = { .i = 2, .b = false }; assert(foo2.$eval(Foo.methodsof[2])() == false); // Foo.xyz