From 41db9c43e5fb42acb3a67d4ca696da084ef1b721 Mon Sep 17 00:00:00 2001 From: Christian Buttner Date: Sat, 22 Jun 2024 14:53:44 +0200 Subject: [PATCH] Allow omitting `= true` for designated initializers of bitstruct bools. --- resources/grammar/grammar.y | 1 + src/compiler/expr.c | 1 + src/compiler/parse_expr.c | 10 ++--- src/compiler/sema_expr.c | 4 ++ src/compiler/sema_initializers.c | 15 ++++++- test/test_suite/bitstruct/bitstruct_init.c3 | 44 ++++++++++++++++++++- 6 files changed, 67 insertions(+), 8 deletions(-) diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index 0fe096d54..99df79ff0 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -441,6 +441,7 @@ param_path arg : param_path '=' expr + | param_path | type | param_path '=' type | expr diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 89c48bbd4..48ecbf22f 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -211,6 +211,7 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) return expr_list_is_constant_eval(expr->cond_expr, eval_kind); case EXPR_DESIGNATOR: expr = expr->designator_expr.value; + if (!expr) return true; goto RETRY; case EXPR_EXPR_BLOCK: case EXPR_DECL: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 59f1bca06..05a76c9f0 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -465,7 +465,7 @@ Expr *parse_vasplat(ParseContext *c) /** * param_list ::= ('...' arg | arg (',' arg)*)? * - * parameter ::= (param_path '=')? expr + * parameter ::= ((param_path '=')? expr) | param_path */ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat) { @@ -483,11 +483,9 @@ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool * expr = expr_new(EXPR_DESIGNATOR, start_span); expr->designator_expr.path = path; - // Expect the '=' after. - CONSUME_OR_RET(TOKEN_EQ, false); - - // Now parse the rest - ASSIGN_EXPR_OR_RET(expr->designator_expr.value, parse_expr(c), false); + if (try_consume(c, TOKEN_EQ)) { + ASSIGN_EXPR_OR_RET(expr->designator_expr.value, parse_expr(c), false); + } RANGE_EXTEND_PREV(expr); } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 52b7241eb..65bff9021 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1331,6 +1331,10 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, } // 8g. Set the parameter + if (!arg->designator_expr.value) + { + RETURN_SEMA_ERROR(arg, "Expected a value for this argument."); + } actual_args[index] = arg->designator_expr.value; continue; } diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index 121eb2488..97a475405 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -406,15 +406,24 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type bool optional = false; Type *inner_type = NULL; bool is_inferred = type_is_inferred(flattened); + int bitmember_count_without_value = 0; VECEACH(init_expressions, i) { Expr *expr = init_expressions[i]; Decl *member; Type *result = sema_expr_analyse_designator(context, original, expr, &max_index, &member); if (!result) return false; + bool is_bitmember = member && member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER; Expr *value = expr->designator_expr.value; + if (!value && is_bitmember && member->var.start_bit == member->var.end_bit && type_flatten(result) == type_bool) { + assert(is_bitstruct); + value = expr_new_const_bool(INVALID_SPAN, type_bool, true); + expr->designator_expr.value = value; + bitmember_count_without_value += 1; + } + if (!value) RETURN_SEMA_ERROR(expr, "This initializer needs a value."); if (!sema_analyse_expr_rhs(context, result, value, true, NULL)) return false; - if (member && member->decl_kind == DECL_VAR && member->var.kind == VARDECL_BITMEMBER) + if (is_bitmember) { if (!sema_bit_assignment_check(context, value, member)) return false; } @@ -426,6 +435,9 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type continue; } } + if (bitmember_count_without_value != 0 && bitmember_count_without_value != vec_size(init_expressions)) { + RETURN_SEMA_ERROR(initializer, "Mixing the omission of initializers is not permitted."); + } Type *type; if (!is_structlike && is_inferred) { @@ -522,6 +534,7 @@ static void sema_create_const_initializer_from_designated_init(ConstInitializer Expr *expr = init_expressions[i]; DesignatorElement **path = expr->designator_expr.path; Expr *value = expr->designator_expr.value; + assert(value); sema_update_const_initializer_with_designator(const_init, path, path + vec_size(path), value); } } diff --git a/test/test_suite/bitstruct/bitstruct_init.c3 b/test/test_suite/bitstruct/bitstruct_init.c3 index ebb316d4c..b7ba43915 100644 --- a/test/test_suite/bitstruct/bitstruct_init.c3 +++ b/test/test_suite/bitstruct/bitstruct_init.c3 @@ -59,4 +59,46 @@ fn void test2() fn void test3() { Foo abc = { .x = -4, .z = 0, .y = 8 }; // #error: would be truncated -} \ No newline at end of file +} + +bitstruct Flags1 : int +{ + bool a; + bool b; + bool c; +} + +distinct Bool = bool; + +bitstruct Flags2 : int +{ + bool a : 0; + Bool b : 1; + int c : 2..3; + bool d : 6; +} + +struct Flags2_Struct +{ + bool a; + bool b; + int c; + bool d; +} + +fn void test4() +{ + Flags1 flags1 = {.a, .b, .c}; + flags1 = {.a}; + flags1 = {.a = true, .b = true, .c = false}; + flags1 = {.a, .b = true, .c = true}; // #error: Mixing the omission + Foo foo = { .x = 0, .z }; // #error: needs a value + + Flags2 flags2 = {.b, .d}; + flags2 = {.b, .c, .d}; // #error: needs a value + flags2 = {.a, .c = 1, .d}; // #error: Mixing the omission + + Flags2_Struct flags2s; + flags2s = {.b, .c, .d}; // #error: needs a value + flags2s = {.a, .c = 1, .d}; // #error: needs a value +}