Skip to content

Commit

Permalink
Allow user-defined attributes to have typed parameters. Folding a con…
Browse files Browse the repository at this point in the history
…stant array of structs at compile time would cause an assert.
  • Loading branch information
lerno committed Sep 19, 2024
1 parent f2cfa61 commit ff36380
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 8 deletions.
2 changes: 2 additions & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
23 changes: 19 additions & 4 deletions src/compiler/sema_decls.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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++)
{
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/sema_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
63 changes: 63 additions & 0 deletions test/test_suite/compile_time/comptime_array_folding.c3t
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ff36380

Please sign in to comment.