From ff36380ddf56d5905b25d0fb3774e569033302e7 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 19 Sep 2024 22:21:29 +0200 Subject: [PATCH] Allow user-defined attributes to have typed parameters. Folding a constant array of structs at compile time would cause an assert. --- releasenotes.md | 2 + src/compiler/sema_decls.c | 23 +++++-- src/compiler/sema_expr.c | 8 +-- .../compile_time/comptime_array_folding.c3t | 63 +++++++++++++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 test/test_suite/compile_time/comptime_array_folding.c3t diff --git a/releasenotes.md b/releasenotes.md index 0163693d4..c30c04e40 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -12,6 +12,7 @@ - Support environment variable 'C3C_LIB' to find the standard library. - Support environment variable 'C3C_CC' to find the default C compiler. - Support casting bitstructs to bool. +- Allow user-defined attributes to have typed parameters. ### Fixes - Issue where a lambda wasn't correctly registered as external. #1408 @@ -38,6 +39,7 @@ - Regression when passing types as `#expr` arguments. #1461 - Temp allocator overwrites data when doing reset on extra allocated pages. #1462 - User defined attributes could not have more than 1 parameter due to bug. +- Folding a constant array of structs at compile time would cause an assert. ### Stdlib changes - Additional init functions for hashmap. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 9e4146cb4..0cd88c0cd 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2843,9 +2843,24 @@ static inline bool sema_analyse_custom_attribute(SemaContext *context, ResolvedA // First we need to analyse each expression in the current scope for (int j = 0; j < param_count; j++) { - if (!sema_analyse_ct_expr(context, args[j])) goto ERR; - - params[j]->var.init_expr = args[j]; + Decl *param = params[j]; + Expr *expr = args[j]; + if (param->var.type_info) + { + Type *type = typeget(param->var.type_info); + if (!sema_analyse_inferred_expr(context, type, expr)) goto ERR; + if (!cast_implicit(context, expr, type, false)) goto ERR; + if (!sema_cast_const(expr)) + { + SEMA_ERROR(expr, "Expected a compile time value."); + goto ERR; + } + } + else + { + if (!sema_analyse_ct_expr(context, args[j])) goto ERR; + } + params[j]->var.init_expr = expr; params[j]->var.kind = VARDECL_CONST; // Then add them to the evaluation context. // (Yes this is messy) @@ -4250,8 +4265,8 @@ static inline bool sema_analyse_attribute_decl(SemaContext *context, SemaContext { Decl *param = params[i]; if (param->var.kind != VARDECL_PARAM) RETURN_SEMA_ERROR(param, "Expected a simple replacement parameter e.g. 'val' here."); - if (param->var.type_info) RETURN_SEMA_ERROR(param, "Type is not allowed on attribute parameters."); if (param->var.init_expr) RETURN_SEMA_ERROR(param, "Attribute parameters may not have default values."); + if (param->var.type_info && !sema_resolve_type_info(c, type_infoptr(param->var.type_info), RESOLVE_TYPE_DEFAULT)) return false; param->resolve_status = RESOLVE_DONE; for (int j = 0; j < i; j++) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 950fd8ce5..26b8c4f75 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -4218,14 +4218,14 @@ static inline bool sema_expr_fold_to_index(Expr *expr, Expr *parent, SubscriptIn break; case CONST_INIT_ARRAY: case CONST_INIT_ARRAY_FULL: + case CONST_INIT_STRUCT: + case CONST_INIT_UNION: expr->expr_kind = EXPR_CONST; expr->const_expr.const_kind = CONST_INITIALIZER; - expr->const_expr.initializer = init; - expr->type = init->type; + expr->const_expr.initializer = result; + expr->type = result->type; break; case CONST_INIT_ARRAY_VALUE: - case CONST_INIT_STRUCT: - case CONST_INIT_UNION: UNREACHABLE case CONST_INIT_VALUE: expr_replace(expr, result->init_value); diff --git a/test/test_suite/compile_time/comptime_array_folding.c3t b/test/test_suite/compile_time/comptime_array_folding.c3t new file mode 100644 index 000000000..5d695d07a --- /dev/null +++ b/test/test_suite/compile_time/comptime_array_folding.c3t @@ -0,0 +1,63 @@ +// #target: macos-x64 +module test; +import std; + +def @TaggedAttr(value) = { + @tag("foo", ValueHere[*]{ value }) +}; + +const FOO_STR = "foo"; + +union TestUnion { + int a; +} +struct ValueHere { + int value; + String thing; +} + +const ValueHere VALUE_STRUCT = { 32, "lol" }; + +struct ThisOtherStruct @TaggedAttr(VALUE_STRUCT) { + int something; +} + +enum Example : int (String str) @TaggedAttr(VALUE_STRUCT) { + FOO = FOO_STR, +} + +const int[2][1] BAR = { { 1, 2} }; +const int[2] BAZ = BAR[0]; +const int BAZ2 = BAZ[1]; +const TestUnion[1] ABC = { { .a = 112 } }; +const TestUnion BCD = ABC[0]; + +macro int get_tag_value($Type) { + $if $Type.has_tagof("foo"): + return $Type.tagof("foo")[0].value; + $else + return -1; + $endif +} + +const MY_VALUE_1 = get_tag_value(Example); +const MY_VALUE_2 = get_tag_value(ThisOtherStruct); + +fn void main() +{ + io::printn(MY_VALUE_2); + io::printn(BAZ); +} + +/* #expect: test.ll + +@.enum.FOO = internal constant [4 x i8] c"FOO\00", align 1 +@test.FOO_STR = local_unnamed_addr constant %"char[]" { ptr @.str.9, i64 3 }, align 8 +@test.VALUE_STRUCT = local_unnamed_addr constant %ValueHere { i32 32, %"char[]" { ptr @.str.10, i64 3 } }, align 8 +@test.BAR = local_unnamed_addr constant [1 x [2 x i32]] [[2 x i32] [i32 1, i32 2]], align 4 +@test.BAZ = local_unnamed_addr constant [2 x i32] [i32 1, i32 2], align 4 +@test.BAZ2 = local_unnamed_addr constant i32 2, align 4 +@test.ABC = local_unnamed_addr constant [1 x %TestUnion] [%TestUnion { i32 112 }], align 4 +@test.BCD = local_unnamed_addr constant %TestUnion { i32 112 }, align 4 +@test.MY_VALUE_1 = local_unnamed_addr constant i32 -1, align 4 +@test.MY_VALUE_2 = local_unnamed_addr constant i32 32, align 4