Add $member.get(value) to replace value.$eval($member.nameof)

This commit is contained in:
Christoffer Lerno
2024-08-20 14:24:12 +02:00
parent 0963ab4cc0
commit fb4a231703
13 changed files with 106 additions and 18 deletions

View File

@@ -35,6 +35,7 @@
- `$exec` may now provide a stdin parameter. - `$exec` may now provide a stdin parameter.
- Introduce `$vaarg[...]` syntax and deprecate the old `$vaarg(...)`. - Introduce `$vaarg[...]` syntax and deprecate the old `$vaarg(...)`.
- Similar change to `$vasplat`: `$vasplat` and `$vasplat[1..]`. - Similar change to `$vasplat`: `$vasplat` and `$vasplat[1..]`.
- Add `$member.get(value)` to replace `value.$eval($member.nameof)`
### Fixes ### Fixes

View File

@@ -1090,7 +1090,8 @@ struct Expr_
Expr *inner_expr; // 8 Expr *inner_expr; // 8
Decl *lambda_expr; // 8 Decl *lambda_expr; // 8
ExprMacroBlock macro_block; // 24 ExprMacroBlock macro_block; // 24
ExprMacroBody macro_body_expr; // 16; ExprMacroBody macro_body_expr; // 16
Decl *member_get_expr; // 8
OperatorOverload overload_expr; // 4 OperatorOverload overload_expr; // 4
ExprPointerOffset pointer_offset_expr; ExprPointerOffset pointer_offset_expr;
ExprGuard rethrow_expr; // 16 ExprGuard rethrow_expr; // 16
@@ -3265,6 +3266,7 @@ INLINE void expr_set_span(Expr *expr, SourceSpan loc)
case EXPR_MACRO_BODY: case EXPR_MACRO_BODY:
case EXPR_DEFAULT_ARG: case EXPR_DEFAULT_ARG:
case EXPR_TAGOF: case EXPR_TAGOF:
case EXPR_MEMBER_GET:
break; break;
} }
} }

View File

@@ -317,6 +317,9 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
MACRO_COPY_DECL(expr->lambda_expr); MACRO_COPY_DECL(expr->lambda_expr);
} }
return expr; return expr;
case EXPR_MEMBER_GET:
fixup_decl(c, &source_expr->member_get_expr);
break;
case EXPR_SWIZZLE: case EXPR_SWIZZLE:
MACRO_COPY_EXPRID(expr->swizzle_expr.parent); MACRO_COPY_EXPRID(expr->swizzle_expr.parent);
return expr; return expr;

View File

@@ -788,6 +788,7 @@ typedef enum
EXPR_MACRO_BLOCK, EXPR_MACRO_BLOCK,
EXPR_MACRO_BODY, EXPR_MACRO_BODY,
EXPR_MACRO_BODY_EXPANSION, EXPR_MACRO_BODY_EXPANSION,
EXPR_MEMBER_GET,
EXPR_NOP, EXPR_NOP,
EXPR_OPERATOR_CHARS, EXPR_OPERATOR_CHARS,
EXPR_OPTIONAL, EXPR_OPTIONAL,
@@ -1363,6 +1364,7 @@ typedef enum
TYPE_PROPERTY_ASSOCIATED, TYPE_PROPERTY_ASSOCIATED,
TYPE_PROPERTY_ELEMENTS, TYPE_PROPERTY_ELEMENTS,
TYPE_PROPERTY_EXTNAMEOF, TYPE_PROPERTY_EXTNAMEOF,
TYPE_PROPERTY_GET,
TYPE_PROPERTY_INF, TYPE_PROPERTY_INF,
TYPE_PROPERTY_IS_EQ, TYPE_PROPERTY_IS_EQ,
TYPE_PROPERTY_IS_ORDERED, TYPE_PROPERTY_IS_ORDERED,

View File

@@ -79,6 +79,7 @@ bool expr_may_addr(Expr *expr)
return expr_may_addr(expr->access_expr.parent); return expr_may_addr(expr->access_expr.parent);
case EXPR_SUBSCRIPT: case EXPR_SUBSCRIPT:
case EXPR_SLICE: case EXPR_SLICE:
case EXPR_MEMBER_GET:
return true; return true;
case EXPR_BENCHMARK_HOOK: case EXPR_BENCHMARK_HOOK:
case EXPR_TEST_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_SLICE_COPY:
case EXPR_MACRO_BLOCK: case EXPR_MACRO_BLOCK:
case EXPR_RETHROW: case EXPR_RETHROW:
case EXPR_MEMBER_GET:
return false; return false;
case EXPR_IDENTIFIER: case EXPR_IDENTIFIER:
{ {
@@ -704,6 +706,7 @@ bool expr_is_pure(Expr *expr)
case EXPR_TYPEID: case EXPR_TYPEID:
case EXPR_TYPEINFO: case EXPR_TYPEINFO:
case EXPR_LAST_FAULT: case EXPR_LAST_FAULT:
case EXPR_MEMBER_GET:
return true; return true;
case EXPR_VASPLAT: case EXPR_VASPLAT:
return true; return true;

View File

@@ -7064,6 +7064,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
case EXPR_MACRO_BODY: case EXPR_MACRO_BODY:
case EXPR_OTHER_CONTEXT: case EXPR_OTHER_CONTEXT:
case EXPR_DESIGNATOR: case EXPR_DESIGNATOR:
case EXPR_MEMBER_GET:
UNREACHABLE UNREACHABLE
case EXPR_DEFAULT_ARG: case EXPR_DEFAULT_ARG:
llvm_emit_default_arg(c, value, expr); llvm_emit_default_arg(c, value, expr);

View File

@@ -593,6 +593,7 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp
case EXPR_GENERIC_IDENT: case EXPR_GENERIC_IDENT:
case EXPR_MACRO_BODY: case EXPR_MACRO_BODY:
case EXPR_LAST_FAULT: case EXPR_LAST_FAULT:
case EXPR_MEMBER_GET:
goto ERR; goto ERR;
} }
UNREACHABLE UNREACHABLE
@@ -618,6 +619,7 @@ static bool expr_may_ref(Expr *expr)
case EXPR_EMBED: case EXPR_EMBED:
case EXPR_DEFAULT_ARG: case EXPR_DEFAULT_ARG:
case EXPR_TAGOF: case EXPR_TAGOF:
case EXPR_MEMBER_GET:
return false; return false;
case EXPR_OTHER_CONTEXT: case EXPR_OTHER_CONTEXT:
return expr_may_ref(expr->expr_other_context.inner); 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); 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) static inline bool sema_expr_analyse_call(SemaContext *context, Expr *expr, bool *no_match_ref)
{ {
if (no_match_ref) *no_match_ref = true; 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); 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); bool optional = func_expr->type && IS_OPTIONAL(func_expr);
Decl *decl; Decl *decl;
Expr *struct_var = NULL; 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->expr_kind = EXPR_TAGOF;
expr->tag_of_expr = (ExprTagOf) { .type = decl, .property = type_property }; expr->tag_of_expr = (ExprTagOf) { .type = decl, .property = type_property };
return true; 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: case TYPE_PROPERTY_NONE:
break; break;
case TYPE_PROPERTY_QNAMEOF: case TYPE_PROPERTY_QNAMEOF:
@@ -3910,25 +3972,26 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp
case TYPE_PROPERTY_NAMES: case TYPE_PROPERTY_NAMES:
return sema_expr_rewrite_typeid_call(expr, typeid, TYPEID_INFO_NAMES, type_get_slice(type_string)); return sema_expr_rewrite_typeid_call(expr, typeid, TYPEID_INFO_NAMES, type_get_slice(type_string));
case TYPE_PROPERTY_ALIGNOF: 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_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_EQ:
case TYPE_PROPERTY_IS_ORDERED: case TYPE_PROPERTY_IS_ORDERED:
case TYPE_PROPERTY_IS_SUBSTRUCT: 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 // Not supported by dynamic typeid
case TYPE_PROPERTY_NONE: case TYPE_PROPERTY_NONE:
return false; return false;
@@ -4096,6 +4159,8 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper
{ {
case TYPE_PROPERTY_NONE: case TYPE_PROPERTY_NONE:
return false; return false;
case TYPE_PROPERTY_GET:
return type == type_member;
case TYPE_PROPERTY_INF: case TYPE_PROPERTY_INF:
case TYPE_PROPERTY_NAN: case TYPE_PROPERTY_NAN:
return type_is_float(type); 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->type = type;
expr->resolve_status = RESOLVE_DONE; expr->resolve_status = RESOLVE_DONE;
return true; return true;
case TYPE_PROPERTY_GET:
UNREACHABLE
case TYPE_PROPERTY_MEMBERSOF: case TYPE_PROPERTY_MEMBERSOF:
{ {
AlignSize align; AlignSize align;
@@ -8943,6 +9010,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr
case EXPR_TYPEID: case EXPR_TYPEID:
case EXPR_TYPEID_INFO: case EXPR_TYPEID_INFO:
case EXPR_TAGOF: case EXPR_TAGOF:
case EXPR_MEMBER_GET:
if (!sema_analyse_expr(active_context, main_expr)) return false; if (!sema_analyse_expr(active_context, main_expr)) return false;
break; break;
} }
@@ -9599,6 +9667,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr)
case EXPR_SWIZZLE: case EXPR_SWIZZLE:
case EXPR_MACRO_BODY: case EXPR_MACRO_BODY:
case EXPR_DEFAULT_ARG: case EXPR_DEFAULT_ARG:
case EXPR_MEMBER_GET:
UNREACHABLE UNREACHABLE
case EXPR_TAGOF: case EXPR_TAGOF:
RETURN_SEMA_ERROR(expr, "Expected '()' after this."); 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; if (!expr_ok(expr)) return false;
switch (expr->expr_kind) 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: case EXPR_MACRO_BODY_EXPANSION:
if (!expr->body_expansion_expr.first_stmt) if (!expr->body_expansion_expr.first_stmt)
{ {

View File

@@ -261,6 +261,7 @@ RETRY:
case EXPR_EMBED: case EXPR_EMBED:
case EXPR_MACRO_BODY: case EXPR_MACRO_BODY:
case EXPR_OTHER_CONTEXT: case EXPR_OTHER_CONTEXT:
case EXPR_MEMBER_GET:
UNREACHABLE UNREACHABLE
case EXPR_DESIGNATOR: case EXPR_DESIGNATOR:
sema_trace_expr_liveness(expr->designator_expr.value); sema_trace_expr_liveness(expr->designator_expr.value);

View File

@@ -632,6 +632,7 @@ static inline bool sema_expr_valid_try_expression(Expr *expr)
case EXPR_TERNARY: case EXPR_TERNARY:
case EXPR_LAST_FAULT: case EXPR_LAST_FAULT:
case EXPR_TAGOF: case EXPR_TAGOF:
case EXPR_MEMBER_GET:
return false; return false;
case EXPR_BITACCESS: case EXPR_BITACCESS:
case EXPR_BUILTIN: case EXPR_BUILTIN:

View File

@@ -160,6 +160,7 @@ void symtab_init(uint32_t capacity)
type_property_list[TYPE_PROPERTY_ASSOCIATED] = KW_DEF("associated"); type_property_list[TYPE_PROPERTY_ASSOCIATED] = KW_DEF("associated");
type_property_list[TYPE_PROPERTY_ELEMENTS] = KW_DEF("elements"); type_property_list[TYPE_PROPERTY_ELEMENTS] = KW_DEF("elements");
type_property_list[TYPE_PROPERTY_EXTNAMEOF] = KW_DEF("extnameof"); 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_INF] = KW_DEF("inf");
type_property_list[TYPE_PROPERTY_INNER] = KW_DEF("inner"); type_property_list[TYPE_PROPERTY_INNER] = KW_DEF("inner");
type_property_list[TYPE_PROPERTY_IS_EQ] = KW_DEF("is_eq"); type_property_list[TYPE_PROPERTY_IS_EQ] = KW_DEF("is_eq");

View File

@@ -25,7 +25,7 @@ macro deep_print(a)
io::print("{"); io::print("{");
$foreach ($i, $m : $typeof(a).membersof) $foreach ($i, $m : $typeof(a).membersof)
if ($i > 0) io::print(", "); if ($i > 0) io::print(", ");
deep_print(a.$eval($m.nameof)); deep_print($m.get(a));
$endforeach $endforeach
io::print("}"); io::print("}");
$default: $default:

View File

@@ -74,7 +74,7 @@ fn void! TextTemplate.init(&self, String template, String tag_start = "{{", Stri
.data = (String*)(data + $m.offsetof), .data = (String*)(data + $m.offsetof),
}; };
$default: $default:
$if $defined(self.data.$eval($m.nameof).as_stream): $if $defined($m.get(self.data).as_stream):
return TextTag{ return TextTag{
.kind = TEMPLATE, .kind = TEMPLATE,
.template = self.data.$eval($m.nameof).as_stream(), .template = self.data.$eval($m.nameof).as_stream(),

View File

@@ -71,6 +71,8 @@ fn void methodsof()
Foo foo = { .i = 4, .b = true }; Foo foo = { .i = 4, .b = true };
assert(Foo.$eval(Foo.methodsof[2])(&foo) == true); // Foo.xyz 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[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 }; Foo foo2 = { .i = 2, .b = false };
assert(foo2.$eval(Foo.methodsof[2])() == false); // Foo.xyz assert(foo2.$eval(Foo.methodsof[2])() == false); // Foo.xyz