mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-21 05:45:19 +00:00
bit_set constants
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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<<val);
|
||||
bits |= bit;
|
||||
}
|
||||
o->value = exact_value_u64(bits);
|
||||
} else {
|
||||
o->value = exact_value_compound(node);
|
||||
}
|
||||
} else {
|
||||
o->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
54
src/ir.cpp
54
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<irValue *>(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;
|
||||
|
||||
@@ -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<Ast *> 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<Ast *> stmts = parse_stmt_list(f);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user