diff --git a/releasenotes.md b/releasenotes.md index 4c716c1ea..067c15905 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -20,6 +20,7 @@ - Module-based generics using {} is deprecated. - Create optional with `~` instead of `?`. `return io::EOF?;` becomes `return io::EOF~`. - Deprecated use of `?` to create optional. +- Make `foo.$abc` implicitly mean `foo.eval("$abc")`. ### Fixes - Regression with npot vector in struct triggering an assert #2219. diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 4614cacaa..6380ec2a0 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -1756,6 +1756,13 @@ typedef enum PARAM_RW_EXPAND_ELEMENTS, } ParamRewrite; +typedef enum +{ + CT_EVAL_TYPE, + CT_EVAL_IDENTIFIER, + CT_EVAL_IMPLICIT_IDENTIFIER +} CtEvalKind; + // -- Arch helper macros #define ARCH_UNSUPPORTED ARCH_TYPE_AARCH64_32: case ARCH_TYPE_BPFEL: case ARCH_TYPE_BPFEB: case ARCH_TYPE_SPARCEL: \ case ARCH_TYPE_LE64: case ARCH_TYPE_AMDIL: case ARCH_TYPE_AMDIL64: case ARCH_TYPE_HSAIL: case ARCH_TYPE_HSAIL64: \ diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 6286b349c..2c05647a1 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -400,13 +400,22 @@ NO_PATH: return NULL; } -Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bool report_missing) +Expr *sema_ct_eval_expr(SemaContext *context, CtEvalKind eval_kind, Expr *inner, bool report_missing) { if (!sema_analyse_ct_expr(context, inner)) return NULL; if (!expr_is_const_string(inner)) { - SEMA_ERROR(inner, "'%s' expects a constant string as the argument.", is_type_eval ? "$evaltype" : "$eval"); - return poisoned_expr; + switch (eval_kind) + { + case CT_EVAL_TYPE: + RETURN_VAL_SEMA_ERROR(poisoned_expr, inner, "'$evaltype' expects a constant string as the argument."); + case CT_EVAL_IDENTIFIER: + RETURN_VAL_SEMA_ERROR(poisoned_expr, inner, "'$eval' expects a constant string as the argument."); + case CT_EVAL_IMPLICIT_IDENTIFIER: + RETURN_VAL_SEMA_ERROR(poisoned_expr, inner, "A constant string was expected as the argument."); + default: + UNREACHABLE + } } return sema_resolve_string_ident(context, inner, report_missing); } @@ -4847,7 +4856,7 @@ static inline bool sema_expr_analyse_slice(SemaContext *context, Expr *expr) * 3. .@foo -> It is a macro. * 4. .#bar -> It is an identifier to resolve as a member or a function * 5. .$eval(...) -> resolve the eval and retry. - * 6. .$ident -> It is a child to resolve as CT param + * 6. .$ident -> resolve as `$eval($ident)` * 7. .$Type -> It is a child to resolve as CT type param */ Expr *sema_expr_resolve_access_child(SemaContext *context, Expr *child, bool *missing) @@ -4876,8 +4885,17 @@ RETRY: if (child->unresolved_ident_expr.path) break; return child; case EXPR_CT_IDENT: - if (child->resolve_status == RESOLVE_DONE) goto ALREADY_RESOLVED; - return child; + { + Expr *result = sema_ct_eval_expr(context, CT_EVAL_IMPLICIT_IDENTIFIER, child, missing == NULL); + if (!expr_ok(result)) return NULL; + if (!result) + { + if (missing) *missing = true; + return NULL; + } + expr_replace(child, result); + goto RETRY; + } case EXPR_TYPEINFO: if (child->type_expr->kind == TYPE_INFO_CT_IDENTIFIER) return child; break; @@ -4885,7 +4903,7 @@ RETRY: { ASSERT_SPAN(child, child->resolve_status != RESOLVE_DONE); // Only report missing if missing var is NULL - Expr *result = sema_ct_eval_expr(context, false, child->inner_expr, missing == NULL); + Expr *result = sema_ct_eval_expr(context, CT_EVAL_IDENTIFIER, child->inner_expr, missing == NULL); if (!expr_ok(result)) return NULL; if (!result) { @@ -10529,7 +10547,7 @@ RETRY: case TYPE_INFO_EVALTYPE: { Expr *expr = type_info->unresolved_type_expr; - expr = sema_ct_eval_expr(context, true, expr, false); + expr = sema_ct_eval_expr(context, CT_EVAL_TYPE, expr, false); if (!expr_ok(expr)) return poisoned_type; if (!expr) return NULL; if (expr->expr_kind != EXPR_TYPEINFO) @@ -11088,7 +11106,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr } case EXPR_CT_EVAL: { - Expr *eval = sema_ct_eval_expr(active_context, "$eval", main_expr->inner_expr, false); + Expr *eval = sema_ct_eval_expr(active_context, CT_EVAL_IDENTIFIER, main_expr->inner_expr, false); if (!expr_ok(eval)) return false; if (eval) { @@ -11499,7 +11517,7 @@ static inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *ex static inline bool sema_expr_resolve_ct_eval(SemaContext *context, Expr *expr) { - Expr *result = sema_ct_eval_expr(context, false, expr->inner_expr, true); + Expr *result = sema_ct_eval_expr(context, CT_EVAL_IDENTIFIER, expr->inner_expr, true); if (!result) return false; if (result->expr_kind == EXPR_TYPEINFO) { diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 6aa8cb2e7..31a6fa2c4 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -108,7 +108,8 @@ void sema_add_methods_to_decl_stack(SemaContext *context, Decl *decl); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool call_var_optional, bool *no_match_ref); Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref); -Expr *sema_ct_eval_expr(SemaContext *context, bool is_type_eval, Expr *inner, bool report_missing); + +Expr *sema_ct_eval_expr(SemaContext *context, CtEvalKind eval_kind, Expr *inner, bool report_missing); Expr *sema_resolve_string_ident(SemaContext *context, Expr *inner, bool report_missing); bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt); bool sema_expr_analyse_sprintf(SemaContext *context, Expr *expr, Expr *format_string, Expr **args, unsigned num_args); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 01ef5372d..97462e71c 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -300,7 +300,7 @@ INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, Res { SEMA_DEPRECATED(type_info, "$evaltype is deprecated, use $typefrom instead."); Expr *expr = type_info->unresolved_type_expr; - Expr *inner = sema_ct_eval_expr(context, true, expr, true); + Expr *inner = sema_ct_eval_expr(context, CT_EVAL_TYPE, expr, true); if (!inner || !expr_ok(inner)) return type_info_poison(type_info); if (inner->expr_kind != EXPR_TYPEINFO) { diff --git a/test/test_suite/compile_time/implicit_eval_access.c3t b/test/test_suite/compile_time/implicit_eval_access.c3t new file mode 100644 index 000000000..bfeddeee6 --- /dev/null +++ b/test/test_suite/compile_time/implicit_eval_access.c3t @@ -0,0 +1,23 @@ +// #target: macos-x64 +module test; +struct Bar +{ + struct { union { struct { char z1; } } } +} + +fn void main() +{ + var $x = "alignof"; + int a = Bar.z1.offsetof; + int b = Bar.z1.$x; +} + +/* #expect: test.ll + +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 0, ptr %a, align 4 + store i32 1, ptr %b, align 4 + ret void +}