From 45820d45e598980e259a8cff8dc02a7b0ec372f9 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 1 Jul 2023 22:47:43 +0200 Subject: [PATCH] Allow using inferred type on method first parameter. --- releasenotes.md | 1 + src/compiler/sema_decls.c | 47 +++++++++-- src/compiler/sema_internal.h | 2 +- src/version.h | 2 +- .../methods/methods_with_inferred_type.c3t | 83 +++++++++++++++++++ 5 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 test/test_suite/methods/methods_with_inferred_type.c3t diff --git a/releasenotes.md b/releasenotes.md index aa794fc43..f8d240ea4 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,7 @@ ## 0.5.0 Change List ### Changes / improvements +- Allow inferred type on method first argument. - Fix to void expression blocks - Temporary objects may now invoke methods using ref parameters. - Delete object files after successful linking. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6e054110b..22beee25f 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -8,7 +8,7 @@ static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, bool is_func, bool *erase_decl); static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *erase_decl); static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *erase_decl); -static inline bool sema_analyse_signature(SemaContext *context, Signature *sig); +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent); static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl); static inline bool sema_check_param_uniqueness_and_type(Decl **decls, Decl *current, unsigned current_index, unsigned count); @@ -693,7 +693,7 @@ static bool sema_analyse_bitstruct(SemaContext *context, Decl *decl, bool *erase } -static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfoId type_parent) { Variadic variadic_type = sig->variadic; Decl **params = sig->params; @@ -740,6 +740,33 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) return false; } + // Fill in the type if the first parameter is lacking a type. + if (type_parent && params && params[0] && !params[0]->var.type_info) + { + TypeInfo *method_parent = type_infoptr(type_parent); + if (!sema_resolve_type_info(context, method_parent)) return false; + Decl *param = params[0]; + Type *inferred_type = NULL; + switch (param->var.kind) + { + case VARDECL_PARAM_REF: + if (!is_macro) + { + inferred_type = type_get_ptr(method_parent->type); + param->var.kind = VARDECL_PARAM; + break; + } + FALLTHROUGH; + case VARDECL_PARAM: + inferred_type = method_parent->type; + break; + default: + goto CHECK_PARAMS; + } + param->var.type_info = type_info_new_base(inferred_type, param->span); + } + + CHECK_PARAMS: // Check parameters for (unsigned i = 0; i < param_count; i++) @@ -782,8 +809,9 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) SEMA_ERROR(param, "Only regular parameters are allowed for functions."); return decl_poison(param); } - if (!is_macro_at_name) + if (!is_macro_at_name && (!type_parent || i != 0 || var_kind != VARDECL_PARAM_REF)) { + SEMA_ERROR(param, "Ref and expression parameters are not allowed in function-like macros. Prefix the macro name with '@'."); return decl_poison(param); } @@ -878,18 +906,18 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig) } -Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallABI abi, Signature *signature, bool is_real_function) +Type *sema_analyse_function_signature(SemaContext *context, Decl *func_decl, CallABI abi, Signature *signature, bool is_real_function) { // Get param count and variadic type Decl **params = signature->params; - if (!sema_analyse_signature(context, signature)) return NULL; + if (!sema_analyse_signature(context, signature, func_decl->func_decl.type_parent)) return NULL; Variadic variadic_type = signature->variadic; // Remove the last empty value. if (variadic_type == VARIADIC_RAW) { - assert(!params[signature->vararg_index] && "The last parameter must have been a raw variadic."); + assert(params && !params[signature->vararg_index] && "The last parameter must have been a raw variadic."); assert(signature->vararg_index == vec_size(params) - 1); vec_pop(params); } @@ -907,10 +935,10 @@ Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallAB if (!all_ok) return NULL; Type *raw_type = type_get_func(signature, abi); - Type *type = type_new(TYPE_FUNC, parent->name); + Type *type = type_new(TYPE_FUNC, func_decl->name); type->canonical = type; type->function.signature = signature; - type->function.module = parent->unit->module; + type->function.module = func_decl->unit->module; type->function.prototype = raw_type->function.prototype; return type; } @@ -2580,7 +2608,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er if (!sema_analyse_func_macro(context, decl, false, erase_decl)) return false; if (*erase_decl) return true; - if (!sema_analyse_signature(context, &decl->func_decl.signature)) return decl_poison(decl); + if (!sema_analyse_signature(context, &decl->func_decl.signature, decl->func_decl.type_parent)) return decl_poison(decl); if (!decl->func_decl.signature.is_at_macro && decl->func_decl.body_param) { @@ -2594,6 +2622,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er unsigned body_param_count = vec_size(body_parameters); for (unsigned i = 0; i < body_param_count; i++) { + assert(body_parameters); Decl *param = body_parameters[i]; param->resolve_status = RESOLVE_RUNNING; assert(param->decl_kind == DECL_VAR); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 7a70364ef..da7079ce8 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -80,7 +80,7 @@ bool sema_analyse_asm(SemaContext *context, AsmInlineBlock *block, Ast *asm_stmt bool sema_bit_assignment_check(Expr *right, Decl *member); int sema_check_comp_time_bool(SemaContext *context, Expr *expr); bool sema_expr_check_assign(SemaContext *c, Expr *expr); -Type *sema_analyse_function_signature(SemaContext *context, Decl *parent, CallABI abi, Signature *signature, bool is_real_function); +Type *sema_analyse_function_signature(SemaContext *context, Decl *func_decl, CallABI abi, Signature *signature, bool is_real_function); bool cast_widen_top_down(SemaContext *context, Expr *expr, Type *type); bool cast_promote_vararg(Expr *arg); Type *cast_numeric_arithmetic_promotion(Type *type); diff --git a/src/version.h b/src/version.h index 6faa8af73..d07e5011d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.546" \ No newline at end of file +#define COMPILER_VERSION "0.4.547" \ No newline at end of file diff --git a/test/test_suite/methods/methods_with_inferred_type.c3t b/test/test_suite/methods/methods_with_inferred_type.c3t new file mode 100644 index 000000000..f38c5647f --- /dev/null +++ b/test/test_suite/methods/methods_with_inferred_type.c3t @@ -0,0 +1,83 @@ +// #target: macos-x64 +module test; + +def Foo = distinct int; + +struct Abc +{ + int a; + int b; +} + +fn void Foo.add(&f) +{ + *f = *f + 1; +} + +macro void Foo.add_macro(&f) +{ + f++; +} + +fn int Abc.add(&a) +{ + return a.a + a.b; +} + +macro int Abc.add_macro(&a) +{ + return a.a + a.b; +} + +fn void test() +{ + Abc x = { 3, 4 }; + int a = x.add(); + int b = x.add_macro(); + Foo f = 100; + f.add(); + f.add_macro(); +} + +/* #expect: test.ll + +define void @test.Foo.add(ptr %0) #0 { +entry: + %1 = load i32, ptr %0, align 4 + %add = add i32 %1, 1 + store i32 %add, ptr %0, align 4 + ret void +} + +define i32 @test.Abc.add(ptr %0) #0 { +entry: + %1 = getelementptr inbounds %Abc, ptr %0, i32 0, i32 0 + %2 = load i32, ptr %1, align 4 + %3 = getelementptr inbounds %Abc, ptr %0, i32 0, i32 1 + %4 = load i32, ptr %3, align 4 + %add = add i32 %2, %4 + ret i32 %add +} + +define void @test.test() #0 { +entry: + %x = alloca %Abc, align 4 + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %f = alloca i32, align 4 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %x, ptr align 4 @.__const, i32 8, i1 false) + %0 = call i32 @test.Abc.add(ptr %x) + store i32 %0, ptr %a, align 4 + %1 = getelementptr inbounds %Abc, ptr %x, i32 0, i32 0 + %2 = load i32, ptr %1, align 4 + %3 = getelementptr inbounds %Abc, ptr %x, i32 0, i32 1 + %4 = load i32, ptr %3, align 4 + %add = add i32 %2, %4 + store i32 %add, ptr %b, align 4 + store i32 100, ptr %f, align 4 + call void @test.Foo.add(ptr %f) + %5 = load i32, ptr %f, align 4 + %add1 = add i32 %5, 1 + store i32 %add1, ptr %f, align 4 + ret void +}