diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 5ae59848d..331a1e20d 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -724,11 +724,19 @@ bit_set_type :: proc() { } Days :: distinct bit_set[Day]; + WEEKEND :: Days{Sunday, Saturday}; + d: Days; - d = Days{Sunday}; + d = Days{Sunday} | Days{Monday}; x := Tuesday; - d |= Days{Saturday, x}; - fmt.println(d); + e := d | WEEKEND; + fmt.println(d, e); + + ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types + fmt.println(ok); + if Saturday in e { + fmt.println("Saturday in", e); + } } main :: proc() { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d12f162ed..3c3c94b0f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -503,6 +503,10 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } } + if (is_type_bit_set(dst) && are_types_identical(dst->BitSet.base_type, operand->type)) { + return 3; + } + #if 0 if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) { return 1; @@ -1345,6 +1349,10 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ // return true; } if (out_value) *out_value = in_value; + } else if (is_type_bit_set(type)) { + if (in_value.kind == ExactValue_Integer) { + return true; + } } @@ -2019,6 +2027,42 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) { break; } + case Token_in: + check_expr(c, x, be->left); + check_expr(c, y, be->right); + if (x->mode == Addressing_Invalid) { + return; + } + if (y->mode == Addressing_Invalid) { + x->mode = Addressing_Invalid; + x->expr = y->expr; + return; + } + + if (is_type_map(y->type)) { + Type *yt = base_type(y->type); + check_assignment(c, x, yt->Map.key, str_lit("map 'in'")); + + add_package_dependency(c, "runtime", "__dynamic_map_get"); + } else if (is_type_bit_set(y->type)) { + Type *yt = base_type(y->type); + check_assignment(c, x, yt->BitSet.base_type, str_lit("bit_set 'in'")); + } else { + gbString t = type_to_string(y->type); + error(x->expr, "expected either a map or bitset for 'in', got %s", t); + gb_string_free(t); + x->expr = node; + x->mode = Addressing_Invalid; + return; + } + if (x->mode != Addressing_Invalid) { + x->mode = Addressing_Value; + x->type = t_untyped_bool; + } + x->expr = node; + + return; + default: check_expr(c, x, be->left); check_expr(c, y, be->right); @@ -2151,7 +2195,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) { ExactValue a = x->value; ExactValue b = y->value; - Type *type = base_type(x->type); + // Type *type = base_type(x->type); + Type *type = x->type; if (is_type_pointer(type)) { GB_ASSERT(op.kind == Token_Sub); i64 bytes = a.value_pointer - b.value_pointer; @@ -5422,6 +5467,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (cl->elems[0]->kind == Ast_FieldValue) { error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed"); + is_constant = false; } else { for_array(index, cl->elems) { Entity *field = nullptr; @@ -5457,7 +5503,31 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_constant) { o->mode = Addressing_Constant; - o->value = exact_value_compound(node); + + if (is_type_bit_set(type)) { + // NOTE(bill): Encode as an integer + + i64 lower = base_type(type)->BitSet.min; + + u64 bits = 0; + for_array(index, cl->elems) { + Entity *field = nullptr; + Ast *elem = cl->elems[index]; + GB_ASSERT(elem->kind != Ast_FieldValue); + TypeAndValue tav = elem->tav; + ExactValue i = exact_value_to_integer(tav.value); + if (i.kind != ExactValue_Integer) { + continue; + } + i64 val = big_int_to_i64(&i.value_integer); + val -= lower; + u64 bit = u64(1ll<value = exact_value_u64(bits); + } else { + o->value = exact_value_compound(node); + } } else { o->mode = Addressing_Value; } diff --git a/src/ir.cpp b/src/ir.cpp index 31c042264..b9a57ca55 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2355,6 +2355,11 @@ irValue *ir_emit_unary_arith(irProcedure *proc, TokenKind op, irValue *x, Type * } + if (op == Token_Not) { + irValue *cmp = ir_emit_comp(proc, Token_CmpEq, x, v_false); + return ir_emit_conv(proc, cmp, type); + } + return ir_emit(proc, ir_instr_unary_op(proc, op, x, type)); } @@ -4880,6 +4885,55 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_CmpOr: return ir_emit_logical_binary_expr(proc, expr); + + case Token_in: { + irValue *right = ir_build_expr(proc, be->right); + Type *rt = base_type(ir_type(right)); + switch (rt->kind) { + case Type_Map: + { + ir_emit_comment(proc, str_lit("map in")); + + irValue *addr = ir_address_from_load_or_generate_local(proc, right); + irValue *h = ir_gen_map_header(proc, addr, rt); + irValue *key = ir_gen_map_key(proc, left, rt->Map.key); + + auto args = array_make(ir_allocator(), 2); + args[0] = h; + args[1] = key; + + irValue *ptr = ir_emit_runtime_call(proc, "__dynamic_map_get", args); + return ir_emit_conv(proc, ir_emit_comp(proc, Token_NotEq, ptr, v_raw_nil), t_bool); + } + break; + case Type_BitSet: + { + ir_emit_comment(proc, str_lit("bit_set in")); + + Type *key_type = rt->BitSet.base_type; + GB_ASSERT(are_types_identical(ir_type(left), key_type)); + + Type *it = bit_set_to_int(rt); + left = ir_emit_conv(proc, left, it); + + irValue *lower = ir_value_constant(it, exact_value_i64(rt->BitSet.min)); + irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, it); + irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, it); + + + irValue *old_value = ir_emit_bitcast(proc, right, it); + irValue *new_value = ir_emit_arith(proc, Token_And, old_value, bit, it); + + return ir_emit_conv(proc, ir_emit_comp(proc, Token_NotEq, new_value, v_zero), t_bool); + } + break; + default: + GB_PANIC("Invalid 'in' type"); + } + break; + + } + default: GB_PANIC("Invalid binary expression"); break; diff --git a/src/parser.cpp b/src/parser.cpp index d264a2619..79e4ba33b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1137,7 +1137,9 @@ Token expect_token_after(AstFile *f, TokenKind kind, char *msg) { Token expect_operator(AstFile *f) { Token prev = f->curr_token; - if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) { + if (prev.kind == Token_in && (f->expr_level >= 0 || f->allow_in_expr)) { + // okay + } else if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) { syntax_error(f->curr_token, "Expected an operator, got '%.*s'", LIT(token_strings[prev.kind])); } else if (!f->allow_range && (prev.kind == Token_Ellipsis)) { @@ -2242,11 +2244,16 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_LtEq: case Token_GtEq: return 5; + case Token_in: + if (f->expr_level >= 0 || f->allow_in_expr) { + return 6; + } + return 0; case Token_Add: case Token_Sub: case Token_Or: case Token_Xor: - return 6; + return 7; case Token_Mul: case Token_Quo: case Token_Mod: @@ -2255,7 +2262,7 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_AndNot: case Token_Shl: case Token_Shr: - return 7; + return 8; } return 0; } @@ -3088,6 +3095,8 @@ Ast *parse_if_stmt(AstFile *f) { isize prev_level = f->expr_level; f->expr_level = -1; + bool prev_allow_in_expr = f->allow_in_expr; + f->allow_in_expr = true; if (allow_token(f, Token_Semicolon)) { cond = parse_expr(f, false); @@ -3102,6 +3111,7 @@ Ast *parse_if_stmt(AstFile *f) { } f->expr_level = prev_level; + f->allow_in_expr = prev_allow_in_expr; if (cond == nullptr) { syntax_error(f->curr_token, "Expected condition for if statement"); @@ -3320,11 +3330,14 @@ Ast *parse_case_clause(AstFile *f, bool is_type) { Array list = {}; expect_token(f, Token_case); bool prev_allow_range = f->allow_range; + bool prev_allow_in_expr = f->allow_in_expr; f->allow_range = !is_type; + f->allow_in_expr = !is_type; if (f->curr_token.kind != Token_Colon) { list = parse_rhs_expr_list(f); } f->allow_range = prev_allow_range; + f->allow_in_expr = prev_allow_in_expr; expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? Array stmts = parse_stmt_list(f); diff --git a/src/parser.hpp b/src/parser.hpp index 83a6ba425..5a2c9e05d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -89,7 +89,8 @@ struct AstFile { // < 0: In Control Clause // NOTE(bill): Used to prevent type literals in control clauses isize expr_level; - bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases + bool allow_range; // NOTE(bill): Ranges are only allowed in certain cases + bool allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases bool in_foreign_block; bool allow_type; isize when_level; diff --git a/src/types.cpp b/src/types.cpp index 73e889922..23f0aa38b 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -757,6 +757,9 @@ bool is_type_constant_type(Type *t) { if (t->kind == Type_Basic) { return (t->Basic.flags & BasicFlag_ConstantType) != 0; } + if (t->kind == Type_BitSet) { + return true; + } return false; } bool is_type_float(Type *t) {