diff --git a/releasenotes.md b/releasenotes.md index e4dca4d8c..5e1ad5456 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,6 +24,7 @@ - Add `@const` attribute for macros, for better error messages with constant macros. - Add `wincrt` setting to libraries. - Add `+++` `&&&` `|||` as replacement for `$concat`, `$and` and `$or`. +- Add `methodsof` to type info for struct, union and bitstruct ### Fixes diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 6f3277367..25eb116b1 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -1034,6 +1034,7 @@ typedef enum TYPE_PROPERTY_LEN, TYPE_PROPERTY_MAX, TYPE_PROPERTY_MEMBERSOF, + TYPE_PROPERTY_METHODSOF, TYPE_PROPERTY_MIN, TYPE_PROPERTY_NAN, TYPE_PROPERTY_INNER, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 781adf637..913386345 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -178,6 +178,7 @@ static inline bool sema_create_const_max(SemaContext *context, Expr *expr, Type static inline bool sema_create_const_params(SemaContext *context, Expr *expr, Type *type); static inline void sema_create_const_membersof(SemaContext *context, Expr *expr, Type *type, AlignSize alignment, AlignSize offset); +static inline void sema_create_const_methodsof(SemaContext *context, Expr *expr, Type *type); static inline int64_t expr_get_index_max(Expr *expr); static inline bool expr_both_any_integer_or_integer_vector(Expr *left, Expr *right); @@ -3405,6 +3406,8 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e case TYPE_PROPERTY_MEMBERSOF: sema_create_const_membersof(context, expr, decl->type->canonical, parent->const_expr.member.align, parent->const_expr.member.offset); return true; + case TYPE_PROPERTY_METHODSOF: + sema_create_const_methodsof(context, expr, decl->type->canonical); case TYPE_PROPERTY_KINDOF: case TYPE_PROPERTY_SIZEOF: return sema_expr_rewrite_to_type_property(context, expr, decl->type->canonical, type_property, decl->type->canonical); @@ -3459,6 +3462,7 @@ MISSING_REF: return false; } + static inline void sema_expr_rewrite_typeid_kind(Expr *expr, Expr *parent) { Module *module = global_context_find_module(kw_std__core__types); @@ -3702,6 +3706,42 @@ static inline void sema_create_const_membersof(SemaContext *context, Expr *expr, expr_rewrite_const_untyped_list(expr, member_exprs); } + +static inline void sema_create_const_methodsof(SemaContext *context, Expr *expr, Type *type) +{ + Decl **methods = type->decl->methods; + unsigned method_count = vec_size(methods); + + if (!method_count) + { + expr_rewrite_const_untyped_list(expr, NULL); + return; + } + + Expr **method_exprs = method_count ? VECNEW(Expr*, method_count) : NULL; + for (unsigned i = 0; i < method_count; i++) + { + Decl *method = methods[i]; + if (method->decl_kind == DECL_FUNC) + { + Decl *decl = methods[i]; + size_t namestr_len = strlen(decl->name); + const char *namestr = str_copy(decl->name, namestr_len); + Expr *expr_element = expr_new(EXPR_CONST, expr->span); + expr_element->resolve_status = RESOLVE_DONE; + expr_element->type = type_string; + expr_element->const_expr = (ExprConst) { + .const_kind = CONST_STRING, + .bytes.ptr = namestr, + .bytes.len = namestr_len, + }; + vec_add(method_exprs, expr_element); + } + } + expr_rewrite_const_untyped_list(expr, method_exprs); +} + + static inline bool sema_create_const_max(SemaContext *context, Expr *expr, Type *type, Type *flat) { if (type_is_integer(flat)) @@ -3827,6 +3867,7 @@ static bool sema_expr_rewrite_to_typeid_property(SemaContext *context, Expr *exp case TYPE_PROPERTY_RETURNS: case TYPE_PROPERTY_PARAMS: case TYPE_PROPERTY_MEMBERSOF: + case TYPE_PROPERTY_METHODSOF: case TYPE_PROPERTY_EXTNAMEOF: case TYPE_PROPERTY_NAMEOF: case TYPE_PROPERTY_QNAMEOF: @@ -4063,6 +4104,16 @@ static bool sema_type_property_is_valid_for_type(Type *original_type, TypeProper default: return false; } + case TYPE_PROPERTY_METHODSOF: + switch (type->type_kind) + { + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_BITSTRUCT: + return true; + default: + return false; + } case TYPE_PROPERTY_PARAMS: case TYPE_PROPERTY_RETURNS: return type_is_func_ptr(type); @@ -4137,6 +4188,11 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr, sema_create_const_membersof(context, expr, flat, align, 0); return true; } + case TYPE_PROPERTY_METHODSOF: + { + sema_create_const_methodsof(context, expr, flat); + return true; + } case TYPE_PROPERTY_PARAMS: return sema_create_const_params(context, expr, flat); case TYPE_PROPERTY_RETURNS: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d07cd4e67..f0f97f534 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -167,6 +167,7 @@ void symtab_init(uint32_t capacity) type_property_list[TYPE_PROPERTY_IS_SUBSTRUCT] = KW_DEF("is_substruct"); type_property_list[TYPE_PROPERTY_KINDOF] = KW_DEF("kindof"); type_property_list[TYPE_PROPERTY_MEMBERSOF] = KW_DEF("membersof"); + type_property_list[TYPE_PROPERTY_METHODSOF] = KW_DEF("methodsof"); type_property_list[TYPE_PROPERTY_NAMEOF] = KW_DEF("nameof"); type_property_list[TYPE_PROPERTY_NAMES] = KW_DEF("names"); type_property_list[TYPE_PROPERTY_NAN] = KW_DEF("nan"); diff --git a/test/unit/regression/methodsof.c3 b/test/unit/regression/methodsof.c3 new file mode 100644 index 000000000..695584cee --- /dev/null +++ b/test/unit/regression/methodsof.c3 @@ -0,0 +1,78 @@ +module methodsof; + +interface IBar +{ + fn void fnA(); + fn void fnB(); +} +struct Bar (IBar) +{ + int a; +} +fn void Bar.fnB(&self) @dynamic {} +fn void Bar.fnA(&self) @dynamic {} + +struct Foo +{ + int i; + bool b; +} + +fn void Foo.foo(&self) {} +fn int Foo.bar(&self, int x) { return x * self.i; } +fn bool Foo.xyz(&self) { return self.b; } + +struct NoMethods +{ + int a; +} + +bitstruct BazBits : char +{ + int a : 0..2; + int b : 4..6; + bool c : 7; +} + +fn void BazBits.fn1(&self) {} +fn void BazBits.fn2(&self) {} + +union AUnion { + int y; + double z; +} + +fn void AUnion.a(&self) {} +fn void AUnion.b(&self) {} + + +module methodsof @test; + +import std::io; + +fn void methodsof() +{ + assert(Foo.methodsof.len == 3); + assert(Bar.methodsof.len == 2); + assert(NoMethods.methodsof.len == 0); + + assert(Foo.methodsof[0] == "foo"); + assert(Foo.methodsof[1] == "bar"); + assert(Foo.methodsof[2] == "xyz"); + assert(Bar.methodsof[0] == "fnB"); + assert(Bar.methodsof[1] == "fnA"); + + assert(BazBits.methodsof[0] == "fn1"); + assert(BazBits.methodsof[1] == "fn2"); + + assert(AUnion.methodsof[0] == "a"); + assert(AUnion.methodsof[1] == "b"); + + 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 + + Foo foo2 = { .i = 2, .b = false }; + assert(foo2.$eval(Foo.methodsof[2])() == false); // Foo.xyz + assert(foo2.$eval(Foo.methodsof[1])(10) == 20); // Foo.bar +}