diff --git a/src/check_expr.c b/src/check_expr.c index a631bf272..d3338f9c8 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -250,7 +250,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n return; } target_type = default_type(operand->type); - if (!is_type_any(type)) { + if (type != NULL && !is_type_any(type)) { GB_ASSERT_MSG(is_type_typed(target_type), "%s", type_to_string(type)); } add_type_info_type(c, type); diff --git a/src/check_stmt.c b/src/check_stmt.c index 78d646302..549383472 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -915,6 +915,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { token.string = str_lit("true"); x.expr = ast_ident(c->curr_ast_file, token); } + if (is_type_vector(x.type)) { + gbString str = type_to_string(x.type); + error_node(x.expr, "Invalid match expression type: %s", str); + gb_string_free(str); + break; + } + // NOTE(bill): Check for multiple defaults AstNode *first_default = NULL; @@ -957,65 +964,119 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { for_array(j, cc->list) { AstNode *expr = cc->list.e[j]; - Operand y = {0}; - check_expr(c, &y, expr); - if (x.mode == Addressing_Invalid || - y.mode == Addressing_Invalid) { - continue; - } + if (expr->kind == AstNode_IntervalExpr) { + ast_node(ie, IntervalExpr, expr); + Operand lhs = {0}; + Operand rhs = {0}; + check_expr(c, &lhs, ie->left); + if (x.mode == Addressing_Invalid) { + continue; + } + if (lhs.mode == Addressing_Invalid) { + continue; + } + check_expr(c, &rhs, ie->right); + if (rhs.mode == Addressing_Invalid) { + continue; + } - convert_to_typed(c, &y, x.type, 0); - if (y.mode == Addressing_Invalid) { - continue; - } - - // NOTE(bill): the ordering here matters - Operand z = y; - check_comparison(c, &z, &x, Token_CmpEq); - if (z.mode == Addressing_Invalid) { - continue; - } - if (y.mode != Addressing_Constant) { - continue; - } + if (!is_type_ordered(x.type)) { + gbString str = type_to_string(x.type); + error_node(x.expr, "Unordered type `%s`, is invalid for an interval expression", str); + gb_string_free(str); + continue; + } - if (y.value.kind != ExactValue_Invalid) { - HashKey key = hash_exact_value(y.value); - TypeAndToken *found = map_type_and_token_get(&seen, key); - if (found != NULL) { - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); - isize count = map_type_and_token_multi_count(&seen, key); - TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count); + TokenKind op = {0}; - map_type_and_token_multi_get_all(&seen, key, taps); - bool continue_outer = false; + Operand a = lhs; + Operand b = rhs; + check_comparison(c, &a, &x, Token_LtEq); + if (a.mode == Addressing_Invalid) { + continue; + } + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_GtEq; break; + case Token_HalfClosed: op = Token_Gt; break; + default: error(ie->op, "Invalid interval operator"); continue; + } - for (isize i = 0; i < count; i++) { - TypeAndToken tap = taps[i]; - if (are_types_identical(y.type, tap.type)) { - TokenPos pos = tap.token.pos; - gbString expr_str = expr_to_string(y.expr); - error_node(y.expr, - "Duplicate case `%s`\n" - "\tprevious case at %.*s(%td:%td)", - expr_str, - LIT(pos.file), pos.line, pos.column); - gb_string_free(expr_str); - continue_outer = true; - break; + check_comparison(c, &b, &x, op); + if (b.mode == Addressing_Invalid) { + continue; + } + + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_HalfClosed: op = Token_Lt; break; + default: error(ie->op, "Invalid interval operator"); continue; + } + + Operand a1 = lhs; + Operand b1 = rhs; + check_comparison(c, &a1, &b1, op); + } else { + Operand y = {0}; + check_expr(c, &y, expr); + if (x.mode == Addressing_Invalid || + y.mode == Addressing_Invalid) { + continue; + } + + convert_to_typed(c, &y, x.type, 0); + if (y.mode == Addressing_Invalid) { + continue; + } + + // NOTE(bill): the ordering here matters + Operand z = y; + check_comparison(c, &z, &x, Token_CmpEq); + if (z.mode == Addressing_Invalid) { + continue; + } + if (y.mode != Addressing_Constant) { + continue; + } + + + if (y.value.kind != ExactValue_Invalid) { + HashKey key = hash_exact_value(y.value); + TypeAndToken *found = map_type_and_token_get(&seen, key); + if (found != NULL) { + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + isize count = map_type_and_token_multi_count(&seen, key); + TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count); + + map_type_and_token_multi_get_all(&seen, key, taps); + bool continue_outer = false; + + for (isize i = 0; i < count; i++) { + TypeAndToken tap = taps[i]; + if (are_types_identical(y.type, tap.type)) { + TokenPos pos = tap.token.pos; + gbString expr_str = expr_to_string(y.expr); + error_node(y.expr, + "Duplicate case `%s`\n" + "\tprevious case at %.*s(%td:%td)", + expr_str, + LIT(pos.file), pos.line, pos.column); + gb_string_free(expr_str); + continue_outer = true; + break; + } + } + + gb_temp_arena_memory_end(tmp); + + if (continue_outer) { + continue; } } - - gb_temp_arena_memory_end(tmp); - - if (continue_outer) { - continue; - } + TypeAndToken tap = {y.type, ast_node_token(y.expr)}; + map_type_and_token_multi_insert(&seen, key, tap); } - TypeAndToken tap = {y.type, ast_node_token(y.expr)}; - map_type_and_token_multi_insert(&seen, key, tap); } } diff --git a/src/ir.c b/src/ir.c index aaddaf84a..4e1e942b4 100644 --- a/src/ir.c +++ b/src/ir.c @@ -6191,8 +6191,24 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { for_array(j, cc->list) { AstNode *expr = cc->list.e[j]; next_cond = ir_new_block(proc, clause, "match.case.next"); - - irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, ir_build_expr(proc, expr)); + irValue *cond = v_false; + if (expr->kind == AstNode_IntervalExpr) { + ast_node(ie, IntervalExpr, expr); + TokenKind op = {0}; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_HalfClosed: op = Token_Lt; break; + default: GB_PANIC("Invalid interval operator"); break; + } + irValue *lhs = ir_build_expr(proc, ie->left); + irValue *rhs = ir_build_expr(proc, ie->right); + // TODO(bill): do short circuit here + irValue *cond_lhs = ir_emit_comp(proc, Token_LtEq, lhs, tag); + irValue *cond_rhs = ir_emit_comp(proc, op, tag, rhs); + cond = ir_emit_arith(proc, Token_And, cond_lhs, cond_rhs, t_bool); + } else { + cond = ir_emit_comp(proc, Token_CmpEq, tag, ir_build_expr(proc, expr)); + } ir_emit_if(proc, cond, body, next_cond); ir_start_block(proc, next_cond); } diff --git a/src/parser.c b/src/parser.c index d9b20fca6..251477b8d 100644 --- a/src/parser.c +++ b/src/parser.c @@ -3088,7 +3088,25 @@ AstNode *parse_case_clause(AstFile *f) { Token token = f->curr_token; AstNodeArray list = make_ast_node_array(f); if (allow_token(f, Token_case)) { - list = parse_rhs_expr_list(f); + list = make_ast_node_array(f); + for (;;) { + AstNode *e = parse_expr(f, false); + if (f->curr_token.kind == Token_Ellipsis || + f->curr_token.kind == Token_HalfClosed) { + Token op = f->curr_token; + next_token(f); + AstNode *lhs = e; + AstNode *rhs = parse_expr(f, false); + e = ast_interval_expr(f, op, lhs, rhs); + } + + array_add(&list, e); + if (f->curr_token.kind != Token_Comma || + f->curr_token.kind == Token_EOF) { + break; + } + next_token(f); + } } else { expect_token(f, Token_default); }