From 01b087238a8ebf1ed452ba4b3b179bea9d6c94f5 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 24 Sep 2024 18:04:39 +0200 Subject: [PATCH] Const initializer refactoring. Improve error on "Foo![]" #1477 --- src/compiler/compiler_internal.h | 1 + src/compiler/parse_expr.c | 10 +-- src/compiler/parse_global.c | 63 +++++++++++++++ src/compiler/sema_decls.c | 2 +- src/compiler/sema_expr.c | 4 +- src/compiler/sema_initializers.c | 77 +++++++++++-------- .../errors/type_optional_declaration_order.c3 | 27 +++++++ 7 files changed, 144 insertions(+), 40 deletions(-) create mode 100644 test/test_suite/errors/type_optional_declaration_order.c3 diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7d4178261..ff85ee225 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -3202,6 +3202,7 @@ bool const_init_local_init_may_be_global(ConstInitializer *init); ConstInitializer *const_init_new_zero(Type *type); ConstInitializer *const_init_new_value(Expr *value); ConstInitializer *const_init_new_array(Type *type, ConstInitializer **elements); +ConstInitializer *const_init_new_struct(Type *type, Expr **elements); ConstInitializer *const_init_new_array_full(Type *type, ConstInitializer **elements); ConstInitializer *const_init_new_zero_array_value(Type *type, ArrayIndex index); void const_init_rewrite_to_value(ConstInitializer *const_init, Expr *value); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 35af062eb..a994c74a1 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -6,6 +6,7 @@ #include "parser_internal.h" typedef Expr *(*ParseFn)(ParseContext *context, Expr *); +static Expr *parse_subscript_expr(ParseContext *c, Expr *left); typedef struct { @@ -110,11 +111,10 @@ static Expr *parse_rethrow_expr(ParseContext *c, Expr *left_side) { assert(expr_ok(left_side)); advance_and_verify(c, TOKEN_BANG); - Expr *expr_ternary = expr_new_expr(EXPR_RETHROW, left_side); - expr_ternary->rethrow_expr.inner = left_side; - RANGE_EXTEND_PREV(expr_ternary); - - return expr_ternary; + Expr *expr = expr_new_expr(EXPR_RETHROW, left_side); + expr->rethrow_expr.inner = left_side; + RANGE_EXTEND_PREV(expr); + return expr; } /** diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 6d4b3b311..811944652 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -652,6 +652,68 @@ TypeInfo *parse_type(ParseContext *c) return parse_type_with_base(c, base); } +typedef enum DiscardedSubscript_ +{ + DISCARD_ERR, + DISCARD_WILDCARD, + DISCARD_SLICE, + DISCARD_EXPR +} DiscardedSubscript; + +static DiscardedSubscript parse_discarded_subscript(ParseContext *c, TokenType end) +{ + if (end == TOKEN_RBRACKET && try_consume(c, end)) return DISCARD_SLICE; + if (try_consume(c, TOKEN_STAR)) + { + CONSUME_OR_RET(end, DISCARD_ERR); + return DISCARD_WILDCARD; + } + ASSIGN_EXPR_OR_RET(Expr *ex, parse_expr(c), DISCARD_ERR); + (void)ex; + CONSUME_OR_RET(end, DISCARD_ERR); + return DISCARD_EXPR; +} + +INLINE bool parse_rethrow_bracket(ParseContext *c, SourceSpan start) +{ + if (try_consume(c, TOKEN_LBRACKET)) + { + switch (parse_discarded_subscript(c, TOKEN_RBRACKET)) + { + case DISCARD_ERR: + return false; + case DISCARD_WILDCARD: + print_error_at(extend_span_with_token(start, c->prev_span), "When declaring an optional array, the '[*]' should appear before the '!', e.g 'Foo[*]!'."); + return false; + case DISCARD_SLICE: + print_error_at(extend_span_with_token(start, c->prev_span), + "When declaring an optional slice the '[]' should appear before the '!', e.g 'Foo[]!'."); + return false; + case DISCARD_EXPR: + print_error_at(extend_span_with_token(start, c->prev_span), "When declaring an optional array, the '[...]' should appear before the '!', e.g 'Foo[4]!'."); + return false; + } + UNREACHABLE + } + if (try_consume(c, TOKEN_LVEC)) + { + switch (parse_discarded_subscript(c, TOKEN_RVEC)) + { + case DISCARD_ERR: + return false; + case DISCARD_WILDCARD: + print_error_at(extend_span_with_token(start, c->span), "When declaring an optional vector, the '[<*>]' should appear before the '!', e.g 'Foo[<*>]!'."); + return false; + case DISCARD_SLICE: + UNREACHABLE + case DISCARD_EXPR: + print_error_at(extend_span_with_token(start, c->span), "When declaring an optional vector, the '[<...>]' should appear before the '!', e.g 'Foo[<4>]!'."); + return false; + } + UNREACHABLE + } + return true; +} /** * optional_type ::= type '!'? * @param c @@ -663,6 +725,7 @@ TypeInfo *parse_optional_type(ParseContext *c) ASSIGN_TYPE_OR_RET(info, parse_type_with_base(c, info), poisoned_type_info); if (try_consume(c, TOKEN_BANG)) { + if (!parse_rethrow_bracket(c, info->span)) return poisoned_type_info; assert(!info->optional); info->optional = true; if (info->resolve_status == RESOLVE_DONE) diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index fb8f6952d..2ba3434da 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3707,7 +3707,7 @@ bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) { if (!sema_analyse_expr(context, init)) goto FAIL; // Check it is constant. - if (!sema_cast_const(init)) + if (!expr_is_runtime_const(init)) { SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); goto FAIL; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 39923d7b1..cbb1dac11 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3909,7 +3909,9 @@ static inline bool sema_create_const_paramsof(SemaContext *context, Expr *expr, Decl *decl = sig->params[i]; Expr *name_expr = expr_new_const_string(span, decl->name ? decl->name : ""); Expr *type_expr = expr_new_const_typeid(span, decl->type->canonical); - Expr *values[] = { name_expr, type_expr }; + Expr **values = NULL; + vec_add(values, name_expr); + vec_add(values, type_expr); Expr *struct_value = sema_create_struct_from_expressions(type_reflected_param->decl, expr->span, values); vec_add(param_exprs, struct_value); } diff --git a/src/compiler/sema_initializers.c b/src/compiler/sema_initializers.c index a95050925..a56f5d5ce 100644 --- a/src/compiler/sema_initializers.c +++ b/src/compiler/sema_initializers.c @@ -103,6 +103,42 @@ ConstInitializer *const_init_new_array_full(Type *type, ConstInitializer **eleme return init; } +ConstInitializer *const_init_new_struct(Type *type, Expr **elements) +{ + ConstInitializer *init = CALLOCS(ConstInitializer); + init->kind = CONST_INIT_STRUCT; + init->type = type_flatten(type); + ConstInitializer **values = NULL; + FOREACH(Expr *, expr, elements) + { + if (expr_is_const_initializer(expr)) + { + vec_add(values, expr->const_expr.initializer); + continue; + } + vec_add(values, const_init_new_value(expr)); + } + init->init_struct = values; + return init; +} + +ConstInitializer *const_init_new_union(Type *type, ArrayIndex index, Expr *value) +{ + ConstInitializer *init = CALLOCS(ConstInitializer); + init->type = type_flatten(type); + init->kind = CONST_INIT_UNION; + init->init_union.index = index; + if (expr_is_const_initializer(value)) + { + init->init_union.element = value->const_expr.initializer; + } + else + { + init->init_union.element = const_init_new_value(value); + } + return init; +} + ConstInitializer *const_init_new_zero_array_value(Type *type, ArrayIndex index) { ConstInitializer *init = CALLOCS(ConstInitializer); @@ -246,35 +282,17 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte { bool is_union = type_flatten(initializer->type)->type_kind == TYPE_UNION; assert(!is_union || vec_size(elements) == 1); - ConstInitializer *const_init = CALLOCS(ConstInitializer); - const_init->kind = is_union ? CONST_INIT_UNION : CONST_INIT_STRUCT; - const_init->type = type_flatten(initializer->type); + ConstInitializer *init; if (is_union) { Expr *expr = elements[0]; - const_init->init_union.index = 0; - if (expr_is_const_initializer(expr)) - { - const_init->init_union.element = expr->const_expr.initializer; - } - else - { - const_init->init_union.element = const_init_new_value(expr); - } - expr_rewrite_const_initializer(initializer, initializer->type, const_init); - return true; + init = const_init_new_union(initializer->type, 0, expr); } - const_init->init_struct = NULL; - FOREACH(Expr *, expr, elements) + else { - if (expr_is_const_initializer(expr)) - { - vec_add(const_init->init_struct, expr->const_expr.initializer); - continue; - } - vec_add(const_init->init_struct, const_init_new_value(expr)); + init = const_init_new_struct(initializer->type, elements); } - expr_rewrite_const_initializer(initializer, initializer->type, const_init); + expr_rewrite_const_initializer(initializer, initializer->type, init); } // 7. Done! @@ -284,15 +302,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte Expr *sema_create_struct_from_expressions(Decl *struct_decl, SourceSpan span, Expr **exprs) { - ConstInitializer *init = CALLOCS(ConstInitializer); - init->kind = CONST_INIT_STRUCT; - init->type = struct_decl->type; - unsigned params = vec_size(struct_decl->strukt.members); - for (unsigned i = 0; i < params; i++) - { - vec_add(init->init_struct, const_init_new_value(exprs[i])); - } - return expr_new_const_initializer(span, struct_decl->type, init); + return expr_new_const_initializer(span, struct_decl->type, + const_init_new_struct(struct_decl->type, exprs)); } /** @@ -899,7 +910,7 @@ static inline void sema_update_const_initializer_with_designator_struct(ConstIni assert(const_init->kind == CONST_INIT_STRUCT); // Find the ConstInitializer to change - ConstInitializer *sub_element = const_init->init_struct[element->index]; + ConstInitializer *sub_element = const_init->init_struct[element->index]; // NOLINT // If this isn't the last element, we recurse. if (!is_last_path_element) diff --git a/test/test_suite/errors/type_optional_declaration_order.c3 b/test/test_suite/errors/type_optional_declaration_order.c3 new file mode 100644 index 000000000..8ea1d2151 --- /dev/null +++ b/test/test_suite/errors/type_optional_declaration_order.c3 @@ -0,0 +1,27 @@ +import std::io; + +fn void! test1() +{ + CallbackResult![] result = 123; // #error: Foo[]! +} + +fn void! test2() +{ + CallbackResult![*] result = 123; // #error: Foo[*]! +} + +fn void! test3() +{ + CallbackResult![4 + 5] result = 123; // #error: Foo[4]! +} + +fn void! test4() +{ + CallbackResult![<*>] result = 123; // #error: Foo[<*>]! +} + +fn void! test5() +{ + CallbackResult![] result = 123; // #error: Foo[<4>]! +} +