- Make foo.$abc implicitly mean foo.eval("$abc").

This commit is contained in:
Christoffer Lerno
2026-01-22 16:46:07 +01:00
parent b14053df41
commit 0add42b0a0
6 changed files with 62 additions and 12 deletions

View File

@@ -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.

View File

@@ -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: \

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -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)
{

View File

@@ -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
}