diff options
| author | gingerBill <bill@gingerbill.org> | 2023-09-30 15:04:17 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2023-09-30 15:04:17 +0100 |
| commit | 648b83d6ead67a291dc023f93a7262622991be2a (patch) | |
| tree | 722495f871bb992e44e321d894879337dd03c20c /src/check_expr.cpp | |
| parent | 4e97b833126d9773ec23a0a0652beb19aab4aea4 (diff) | |
Add `or_break` and `or_continue` constructs
Diffstat (limited to 'src/check_expr.cpp')
| -rw-r--r-- | src/check_expr.cpp | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 968b6ec1e..1401793a5 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3826,6 +3826,15 @@ gb_internal void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, update_untyped_expr_type(c, ore->expr, type, final); case_end; + case_ast_node(obe, OrBranchExpr, e); + if (old->value.kind != ExactValue_Invalid) { + // See above note in UnaryExpr case + break; + } + + update_untyped_expr_type(c, obe->expr, type, final); + case_end; + case_ast_node(oee, OrElseExpr, e); if (old->value.kind != ExactValue_Invalid) { // See above note in UnaryExpr case @@ -8193,6 +8202,104 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no return Expr_Expr; } +gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { + ast_node(be, OrBranchExpr, node); + + String name = be->token.string; + Operand x = {}; + check_multi_expr_with_type_hint(c, &x, be->expr, type_hint); + if (x.mode == Addressing_Invalid) { + o->mode = Addressing_Value; + o->type = t_invalid; + o->expr = node; + return Expr_Expr; + } + + Type *left_type = nullptr; + Type *right_type = nullptr; + check_or_return_split_types(c, &x, name, &left_type, &right_type); + add_type_and_value(c, be->expr, x.mode, x.type, x.value); + + if (right_type == nullptr) { + check_or_else_expr_no_value_error(c, name, x, type_hint); + } else { + if (is_type_boolean(right_type) || type_has_nil(right_type)) { + // okay + } else { + gbString s = type_to_string(right_type); + error(node, "'%.*s' requires a boolean or nil-able type, got %s", s); + gb_string_free(s); + } + } + + o->expr = node; + o->type = left_type; + if (left_type != nullptr) { + o->mode = Addressing_Value; + } else { + o->mode = Addressing_NoValue; + } + + if (c->curr_proc_sig == nullptr) { + error(node, "'%.*s' can only be used within a procedure", LIT(name)); + } + + Ast *label = be->label; + + switch (be->token.kind) { + case Token_or_break: + if ((c->stmt_flags & Stmt_BreakAllowed) == 0 && label == nullptr) { + error(be->token, "'%.*s' only allowed in non-inline loops or 'switch' statements", LIT(name)); + } + break; + case Token_or_continue: + if ((c->stmt_flags & Stmt_ContinueAllowed) == 0 && label == nullptr) { + error(be->token, "'%.*s' only allowed in non-inline loops", LIT(name)); + } + break; + } + + if (label != nullptr) { + if (label->kind != Ast_Ident) { + error(label, "A branch statement's label name must be an identifier"); + return Expr_Expr; + } + Ast *ident = label; + String name = ident->Ident.token.string; + Operand o = {}; + Entity *e = check_ident(c, &o, ident, nullptr, nullptr, false); + if (e == nullptr) { + error(ident, "Undeclared label name: %.*s", LIT(name)); + return Expr_Expr; + } + add_entity_use(c, ident, e); + if (e->kind != Entity_Label) { + error(ident, "'%.*s' is not a label", LIT(name)); + return Expr_Expr; + } + Ast *parent = e->Label.parent; + GB_ASSERT(parent != nullptr); + switch (parent->kind) { + case Ast_BlockStmt: + case Ast_IfStmt: + case Ast_SwitchStmt: + if (be->token.kind != Token_or_break) { + error(label, "Label '%.*s' can only be used with 'or_break'", LIT(e->token.string)); + } + break; + case Ast_RangeStmt: + case Ast_ForStmt: + if ((be->token.kind != Token_or_break) && (be->token.kind != Token_or_continue)) { + error(label, "Label '%.*s' can only be used with 'or_break' and 'or_continue'", LIT(e->token.string)); + } + break; + + } + } + + return Expr_Expr; +} + gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<Ast *> const &elems, Operand *o, Type *type, bool &is_constant) { Type *bt = base_type(type); @@ -9947,6 +10054,10 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast return check_or_return_expr(c, o, node, type_hint); case_end; + case_ast_node(re, OrBranchExpr, node); + return check_or_branch_expr(c, o, node, type_hint); + case_end; + case_ast_node(cl, CompoundLit, node); kind = check_compound_literal(c, o, node, type_hint); case_end; @@ -10513,6 +10624,16 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan str = gb_string_appendc(str, " or_return"); case_end; + case_ast_node(oe, OrBranchExpr, node); + str = write_expr_to_string(str, oe->expr, shorthand); + str = gb_string_append_rune(str, ' '); + str = string_append_token(str, oe->token); + if (oe->label) { + str = gb_string_append_rune(str, ' '); + str = write_expr_to_string(str, oe->label, shorthand); + } + case_end; + case_ast_node(pe, ParenExpr, node); str = gb_string_append_rune(str, '('); str = write_expr_to_string(str, pe->expr, shorthand); |