diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 446f06c9b..a6c11b3ae 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -67,6 +67,34 @@ write_rune :: proc(buf: ^String_Buffer, r: rune) { b, n := utf8.encode_rune(r); write_bytes(buf, b[:n]); } +write_encoded_rune :: proc(buf: ^String_Buffer, r: rune) { + write_byte(buf, '\''); + switch r { + case '\a': write_string(buf, "\\a"); + case '\b': write_string(buf, "\\b"); + case '\e': write_string(buf, "\\e"); + case '\f': write_string(buf, "\\f"); + case '\n': write_string(buf, "\\n"); + case '\r': write_string(buf, "\\r"); + case '\t': write_string(buf, "\\t"); + case '\v': write_string(buf, "\\v"); + case: + if r < 32 { + write_string(buf, "\\x"); + b: [2]byte; + s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil); + switch len(s) { + case 0: write_string(buf, "00"); + case 1: write_rune(buf, '0'); + case 2: write_string(buf, s); + } + } else { + write_rune(buf, r); + } + + } + write_byte(buf, '\''); +} write_i64 :: proc(buf: ^String_Buffer, i: i64, base: int) { b: [129]byte; @@ -315,7 +343,18 @@ write_type :: proc(buf: ^String_Buffer, ti: ^runtime.Type_Info) { case runtime.Type_Info_Bit_Set: write_string(buf, "bit_set["); - write_type(buf, info.base); + switch { + case types.is_enum(info.elem): + write_type(buf, info.elem); + case types.is_rune(info.elem): + write_encoded_rune(buf, rune(info.lower)); + write_string(buf, ".."); + write_encoded_rune(buf, rune(info.upper)); + case: + write_i64(buf, info.lower, 10); + write_string(buf, ".."); + write_i64(buf, info.upper, 10); + } write_string(buf, "]"); } } @@ -763,8 +802,7 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") { case: panic("unknown bit_size size"); } - et := runtime.type_info_base(info.base); - e := et.variant.(runtime.Type_Info_Enum); + et := runtime.type_info_base(info.elem); if name != "" { write_string(fi.buf, name); @@ -774,6 +812,7 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") { write_byte(fi.buf, '{'); defer write_byte(fi.buf, '}'); + e, is_enum := et.variant.(runtime.Type_Info_Enum); commas := 0; loop: for i in 0 .. bit_size-1 { if bits & (1< bool { if xo != yo do return false; if xn != yn do return false; } - return true; + + case rt.Type_Info_Bit_Set: + y, ok := b.variant.(rt.Type_Info_Bit_Set); + if !ok do return false; + return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper; } return false; diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 8e95aefc4..3ac7a0d1f 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -713,33 +713,47 @@ deprecated_attribute :: proc() { } bit_set_type :: proc() { - using Day :: enum { - Sunday, - Monday, - Tuesday, - Wednesday, - Thursday, - Friday, - Saturday, + { + using Day :: enum { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + } + + Days :: distinct bit_set[Day]; + WEEKEND :: Days{Sunday, Saturday}; + + d: Days; + d = {Sunday, Monday}; + x := Tuesday; + e := d | WEEKEND; + e |= {Monday}; + 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); + } + X :: Saturday in WEEKEND; // Constant evaluation + fmt.println(X); } + { + x: bit_set['A'..'Z']; + y: bit_set[0..8]; + fmt.println(typeid_of(type_of(x))); // bit_set[A..Z] + fmt.println(typeid_of(type_of(y))); // bit_set[0..8] - Days :: distinct bit_set[Day]; - WEEKEND :: Days{Sunday, Saturday}; + x |= {'F'}; + assert('F' in x); - d: Days; - d = {Sunday, Monday}; - x := Tuesday; - e := d | WEEKEND; - e |= {Monday}; - 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); + y |= {1, 4, 2}; + assert(2 in y); } - X :: Saturday in WEEKEND; // Constant evaluation - fmt.println(X); } main :: proc() { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1bcac33ae..3b7571f74 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2047,14 +2047,23 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as ExactValue v = exact_value_to_integer(y->value); GB_ASSERT(k.kind == ExactValue_Integer); GB_ASSERT(v.kind == ExactValue_Integer); - i64 bit = 1ll<BitSet.lower; + i64 upper = yt->BitSet.upper; - x->mode = Addressing_Constant; - x->type = t_untyped_bool; - x->value = exact_value_bool((bit & bits) != 0); - x->expr = node; - return; + if (lower <= key && key <= upper) { + i64 bit = 1ll<mode = Addressing_Constant; + x->type = t_untyped_bool; + x->value = exact_value_bool((bit & bits) != 0); + x->expr = node; + return; + } else { + error(x->expr, "key '%lld' out of range of bit set, %lld..%lld", key, lower, upper); + x->mode = Addressing_Invalid; + } } } else { @@ -5591,7 +5600,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_bit_set(type)) { // NOTE(bill): Encode as an integer - i64 lower = base_type(type)->BitSet.min; + i64 lower = base_type(type)->BitSet.lower; u64 bits = 0; for_array(index, cl->elems) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2040471b5..907754cbf 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -706,42 +706,27 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { } - TokenKind op = Token_Invalid; - Operand a = lhs; Operand b = rhs; check_comparison(ctx, &a, &x, Token_LtEq); if (a.mode == Addressing_Invalid) { continue; } - switch (ie->op.kind) { - case Token_Ellipsis: op = Token_GtEq; break; - default: error(ie->op, "Invalid interval operator"); continue; - } - check_comparison(ctx, &b, &x, op); + check_comparison(ctx, &b, &x, Token_GtEq); if (b.mode == Addressing_Invalid) { continue; } - switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - default: error(ie->op, "Invalid interval operator"); continue; - } - - - Operand a1 = lhs; Operand b1 = rhs; - check_comparison(ctx, &a1, &b1, op); + check_comparison(ctx, &a1, &b1, Token_LtEq); if (complete) { error(lhs.expr, "#complete switch statement does not allow ranges"); } add_constant_switch_case(ctx, &seen, lhs); - if (op == Token_LtEq) { - add_constant_switch_case(ctx, &seen, rhs); - } + add_constant_switch_case(ctx, &seen, rhs); } else { Operand y = {}; check_expr(ctx, &y, expr); diff --git a/src/check_type.cpp b/src/check_type.cpp index 6b93fbd68..bbae12654 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -680,49 +680,157 @@ void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type, Ast *node) } } +bool is_type_valid_bit_set_range(Type *t) { + if (is_type_integer(t)) { + return true; + } + if (is_type_rune(t)) { + return true; + } + return false; +} -void check_bit_set_type(CheckerContext *ctx, Type *type, Ast *node) { +void check_bit_set_type(CheckerContext *c, Type *type, Ast *node) { ast_node(bs, BitSetType, node); GB_ASSERT(type->kind == Type_BitSet); - Type *bt = check_type_expr(ctx, bs->base, nullptr); - - type->BitSet.base = bt; - if (!is_type_enum(bt)) { - error(bs->base, "Expected an enum type for a bit_set"); - } else { - Type *et = base_type(bt); - GB_ASSERT(et->kind == Type_Enum); - if (!is_type_integer(et->Enum.base_type)) { - error(bs->base, "Enum type for bit_set must be an integer"); + Ast *base = unparen_expr(bs->base); + if (is_ast_range(base)) { + ast_node(be, BinaryExpr, base); + Operand lhs = {}; + Operand rhs = {}; + check_expr(c, &lhs, be->left); + check_expr(c, &rhs, be->right); + if (lhs.mode == Addressing_Invalid) { return; } - i64 min_value = 0; - i64 max_value = 0; - BigInt v64 = {}; big_int_from_i64(&v64, 64); - - for_array(i, et->Enum.fields) { - Entity *e = et->Enum.fields[i]; - if (e->kind != Entity_Constant) { - continue; + if (rhs.mode == Addressing_Invalid) { + return; + } + convert_to_typed(c, &lhs, rhs.type); + if (lhs.mode == Addressing_Invalid) { + return; + } + convert_to_typed(c, &rhs, lhs.type); + if (rhs.mode == Addressing_Invalid) { + return; + } + if (!are_types_identical(lhs.type, rhs.type)) { + if (lhs.type != t_invalid && + rhs.type != t_invalid) { + gbString xt = type_to_string(lhs.type); + gbString yt = type_to_string(rhs.type); + gbString expr_str = expr_to_string(bs->base); + error(bs->base, "Mismatched types in range '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); } - ExactValue value = exact_value_to_integer(e->Constant.value); - GB_ASSERT(value.kind == ExactValue_Integer); - i64 x = big_int_to_i64(&value.value_integer); - min_value = gb_min(min_value, x); - max_value = gb_max(max_value, x); + return; } - GB_ASSERT(min_value <= max_value); - - if (max_value - min_value > 64) { - error(bs->base, "bit_set range is greater than 64 bits"); + if (!is_type_valid_bit_set_range(lhs.type)) { + gbString str = type_to_string(lhs.type); + error(bs->base, "'%s' is invalid for an interval expression, expected an integer or rune", str); + gb_string_free(str); + return; } - type->BitSet.min = min_value; - type->BitSet.max = max_value; + if (lhs.mode != Addressing_Constant || rhs.mode != Addressing_Constant) { + error(bs->base, "Intervals must be constant values"); + return; + } + + ExactValue iv = exact_value_to_integer(lhs.value); + ExactValue jv = exact_value_to_integer(rhs.value); + GB_ASSERT(iv.kind == ExactValue_Integer); + GB_ASSERT(jv.kind == ExactValue_Integer); + + BigInt i = iv.value_integer; + BigInt j = jv.value_integer; + if (big_int_cmp(&i, &j) > 0) { + gbAllocator a = heap_allocator(); + String si = big_int_to_string(a, &i); + String sj = big_int_to_string(a, &j); + error(bs->base, "Lower interval bound larger than upper bound, %.*s .. %.*s", LIT(si), LIT(sj)); + gb_free(a, si.text); + gb_free(a, sj.text); + return; + } + + Type *t = default_type(lhs.type); + + bool ok = true; + ok = check_representable_as_constant(c, iv, t, nullptr); + if (!ok) { + gbAllocator a = heap_allocator(); + String s = big_int_to_string(a, &i); + gbString ts = type_to_string(t); + error(bs->base, "%.*s is not representable by %s", LIT(s), ts); + gb_string_free(ts); + gb_free(a, s.text); + return; + } + ok = check_representable_as_constant(c, iv, t, nullptr); + if (!ok) { + gbAllocator a = heap_allocator(); + String s = big_int_to_string(a, &j); + gbString ts = type_to_string(t); + error(bs->base, "%.*s is not representable by %s", LIT(s), ts); + gb_string_free(ts); + gb_free(a, s.text); + return; + } + i64 lower = big_int_to_i64(&i); + i64 upper = big_int_to_i64(&j); + + if (upper - lower > 64) { + error(bs->base, "bit_set range is greater than 64 bits, %lld bits are required", (upper-lower+1)); + } + type->BitSet.base = t; + type->BitSet.lower = lower; + type->BitSet.upper = upper; + + } else { + Type *bt = check_type_expr(c, bs->base, nullptr); + + type->BitSet.base = bt; + if (!is_type_enum(bt)) { + error(bs->base, "Expected an enum type for a bit_set"); + } else { + Type *et = base_type(bt); + GB_ASSERT(et->kind == Type_Enum); + if (!is_type_integer(et->Enum.base_type)) { + error(bs->base, "Enum type for bit_set must be an integer"); + return; + } + i64 lower = 0; + i64 upper = 0; + BigInt v64 = {}; big_int_from_i64(&v64, 64); + + for_array(i, et->Enum.fields) { + Entity *e = et->Enum.fields[i]; + if (e->kind != Entity_Constant) { + continue; + } + ExactValue value = exact_value_to_integer(e->Constant.value); + GB_ASSERT(value.kind == ExactValue_Integer); + // NOTE(bill): enum types should be able to store i64 values + i64 x = big_int_to_i64(&value.value_integer); + lower = gb_min(lower, x); + upper = gb_max(upper, x); + } + + GB_ASSERT(lower <= upper); + + if (upper - lower > 64) { + error(bs->base, "bit_set range is greater than 64 bits, %lld bits are required", (upper-lower+1)); + } + + type->BitSet.lower = lower; + type->BitSet.upper = upper; + } } - } @@ -1951,9 +2059,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t case_ast_node(bs, BitSetType, e); *type = alloc_type_bit_set(); set_base_type(named_type, *type); - check_open_scope(ctx, e); check_bit_set_type(ctx, *type, e); - check_close_scope(ctx); return true; case_end; diff --git a/src/ir.cpp b/src/ir.cpp index e0fe074ff..67fee8556 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4915,7 +4915,7 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { 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 *lower = ir_value_constant(it, exact_value_i64(rt->BitSet.lower)); irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, it); irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, it); @@ -5883,7 +5883,7 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count > 0 && sz > 0) { ir_emit_store(proc, v, ir_add_module_constant(proc->module, type, exact_value_compound(expr))); - irValue *lower = ir_value_constant(t_int, exact_value_i64(bt->BitSet.min)); + irValue *lower = ir_value_constant(t_int, exact_value_i64(bt->BitSet.lower)); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; GB_ASSERT(elem->kind != Ast_FieldValue); @@ -6484,6 +6484,7 @@ void ir_type_case_body(irProcedure *proc, Ast *label, Ast *clause, irBlock *body ir_emit_jump(proc, done); } + void ir_build_stmt_internal(irProcedure *proc, Ast *node) { switch (node->kind) { case_ast_node(bs, EmptyStmt, node); @@ -8116,9 +8117,15 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info case Type_BitSet: ir_emit_comment(proc, str_lit("Type_Info_Bit_Set")); tag = ir_emit_conv(proc, variant_ptr, t_type_info_bit_set_ptr); + + GB_ASSERT(is_type_typed(t->BitSet.base)); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), ir_get_type_info_ptr(proc, t->BitSet.base)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), ir_const_i64(t->BitSet.lower)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), ir_const_i64(t->BitSet.upper)); break; + + } if (tag != nullptr) { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index eded9d8dc..85efd12ba 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -807,7 +807,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } GB_ASSERT(tav.value.kind == ExactValue_Integer); i64 v = big_int_to_i64(&tav.value.value_integer); - i64 lower = type->BitSet.min; + i64 lower = type->BitSet.lower; bits |= 1ull<allow_range; + f->allow_range = true; + Ast *base = parse_expr(f, false); + f->allow_range = prev_allow_range; + Token close = expect_token(f, Token_CloseBracket); return ast_bit_set_type(f, token, base); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 7e8f3bcab..47bff66ed 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -674,6 +674,7 @@ bool scan_escape(Tokenizer *t) { Rune r = t->curr_rune; if (r == 'a' || r == 'b' || + r == 'e' || r == 'f' || r == 'n' || r == 'r' || diff --git a/src/types.cpp b/src/types.cpp index 1badb1bdf..406b55b82 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -180,8 +180,8 @@ struct TypeStruct { }) \ TYPE_KIND(BitSet, struct { \ Type *base; \ - i64 min; \ - i64 max; \ + i64 lower; \ + i64 upper; \ }) \ @@ -2069,13 +2069,13 @@ i64 type_align_of_internal(Type *t, TypePath *path) { } break; case Type_BitSet: { - i64 bits = t->BitSet.max - t->BitSet.min + 1; + i64 bits = t->BitSet.upper - t->BitSet.lower + 1; if (bits == 0) return 0; if (bits <= 8) return 1; if (bits <= 16) return 2; if (bits <= 32) return 4; if (bits <= 64) return 8; - return 8; + return 8; // NOTE(bill): Could be an invalid range so limit it for now } } @@ -2296,13 +2296,13 @@ i64 type_size_of_internal(Type *t, TypePath *path) { } break; case Type_BitSet: { - i64 bits = t->BitSet.max - t->BitSet.min + 1; + i64 bits = t->BitSet.upper - t->BitSet.lower + 1; if (bits == 0) return 0; if (bits <= 8) return 1; if (bits <= 16) return 2; if (bits <= 32) return 4; if (bits <= 64) return 8; - return 8; + return 8; // NOTE(bill): Could be an invalid range so limit it for now } }