mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-10 05:11:37 -07:00
Add constant literal expressions
This commit is contained in:
@@ -1815,6 +1815,60 @@ soa_struct_layout :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
constant_literal_expressions :: proc() {
|
||||
fmt.println("\n#constant literal expressions");
|
||||
|
||||
Bar :: struct {x, y: f32};
|
||||
Foo :: struct {a, b: int, using c: Bar};
|
||||
|
||||
FOO_CONST :: Foo{b = 2, a = 1, c = {3, 4}};
|
||||
|
||||
|
||||
fmt.println(FOO_CONST.a);
|
||||
fmt.println(FOO_CONST.b);
|
||||
fmt.println(FOO_CONST.c);
|
||||
fmt.println(FOO_CONST.c.x);
|
||||
fmt.println(FOO_CONST.c.y);
|
||||
fmt.println(FOO_CONST.x); // using works as expected
|
||||
fmt.println(FOO_CONST.y);
|
||||
|
||||
fmt.println("-------");
|
||||
|
||||
ARRAY_CONST :: [3]int{1 = 4, 2 = 9, 0 = 1};
|
||||
|
||||
fmt.println(ARRAY_CONST[0]);
|
||||
fmt.println(ARRAY_CONST[1]);
|
||||
fmt.println(ARRAY_CONST[2]);
|
||||
|
||||
fmt.println("-------");
|
||||
|
||||
FOO_ARRAY_DEFAULTS :: [3]Foo{{}, {}, {}};
|
||||
fmt.println(FOO_ARRAY_DEFAULTS[2].x);
|
||||
|
||||
|
||||
|
||||
fmt.println("-------");
|
||||
|
||||
Baz :: enum{A=5, B, C, D=9};
|
||||
ENUM_ARRAY_CONST :: [Baz]int{.A .. .C = 1, .D = 16};
|
||||
|
||||
fmt.println(ENUM_ARRAY_CONST[.A]);
|
||||
fmt.println(ENUM_ARRAY_CONST[.B]);
|
||||
fmt.println(ENUM_ARRAY_CONST[.C]);
|
||||
fmt.println(ENUM_ARRAY_CONST[.D]);
|
||||
|
||||
fmt.println("-------");
|
||||
|
||||
STRING_CONST :: "Hellope!";
|
||||
|
||||
fmt.println(STRING_CONST[0]);
|
||||
fmt.println(STRING_CONST[2]);
|
||||
fmt.println(STRING_CONST[3]);
|
||||
|
||||
fmt.println(STRING_CONST[0:5]);
|
||||
fmt.println(STRING_CONST[3:][:4]);
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
@@ -1844,5 +1898,6 @@ main :: proc() {
|
||||
range_statements_with_multiple_return_values();
|
||||
threading_example();
|
||||
soa_struct_layout();
|
||||
constant_literal_expressions();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
if (!is_type_constant_type(operand->type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
error(operand->expr, "Invalid constant type: '%s'", type_str);
|
||||
|
||||
+212
-40
@@ -3108,6 +3108,8 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value) *value = exact_value_to_i64(exact_value_sub(operand.value, lo));
|
||||
|
||||
return true;
|
||||
|
||||
} else { // NOTE(bill): Do array bound checking
|
||||
@@ -3144,6 +3146,140 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
|
||||
return true;
|
||||
}
|
||||
|
||||
ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 index, bool *success_, bool *finish_) {
|
||||
if (value.kind == ExactValue_String) {
|
||||
GB_ASSERT(0 <= index && index < value.value_string.len);
|
||||
u8 val = value.value_string[index];
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
return exact_value_u64(val);
|
||||
}
|
||||
if (value.kind != ExactValue_Compound) {
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
Ast *node = value.value_compound;
|
||||
switch (node->kind) {
|
||||
case_ast_node(cl, CompoundLit, node);
|
||||
if (cl->elems.count == 0) {
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
return empty_exact_value;
|
||||
}
|
||||
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
if (is_type_struct(node->tav.type)) {
|
||||
for_array(i, cl->elems) {
|
||||
Ast *elem = cl->elems[i];
|
||||
if (elem->kind != Ast_FieldValue) {
|
||||
continue;
|
||||
}
|
||||
ast_node(fv, FieldValue, elem);
|
||||
String name = fv->field->Ident.token.string;
|
||||
Selection sub_sel = lookup_field(node->tav.type, name, false);
|
||||
defer (array_free(&sub_sel.index));
|
||||
if (sub_sel.index[0] == index) {
|
||||
value = fv->value->tav.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (is_type_array(node->tav.type) || is_type_enumerated_array(node->tav.type)) {
|
||||
for_array(i, cl->elems) {
|
||||
Ast *elem = cl->elems[i];
|
||||
if (elem->kind != Ast_FieldValue) {
|
||||
continue;
|
||||
}
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (is_ast_range(fv->field)) {
|
||||
ast_node(ie, BinaryExpr, fv->field);
|
||||
TypeAndValue lo_tav = ie->left->tav;
|
||||
TypeAndValue hi_tav = ie->right->tav;
|
||||
GB_ASSERT(lo_tav.mode == Addressing_Constant);
|
||||
GB_ASSERT(hi_tav.mode == Addressing_Constant);
|
||||
|
||||
TokenKind op = ie->op.kind;
|
||||
i64 lo = exact_value_to_i64(lo_tav.value);
|
||||
i64 hi = exact_value_to_i64(hi_tav.value);
|
||||
|
||||
i64 corrected_index = index;
|
||||
|
||||
if (is_type_enumerated_array(node->tav.type)) {
|
||||
Type *bt = base_type(node->tav.type);
|
||||
GB_ASSERT(bt->kind == Type_EnumeratedArray);
|
||||
corrected_index = index + exact_value_to_i64(bt->EnumeratedArray.min_value);
|
||||
}
|
||||
if (op == Token_Ellipsis) {
|
||||
if (lo <= corrected_index && corrected_index <= hi) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = false;
|
||||
return tav.value;
|
||||
}
|
||||
} else {
|
||||
if (lo <= corrected_index && corrected_index < hi) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = false;
|
||||
return tav.value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TypeAndValue index_tav = fv->field->tav;
|
||||
GB_ASSERT(index_tav.mode == Addressing_Constant);
|
||||
ExactValue index_value = index_tav.value;
|
||||
if (is_type_enumerated_array(node->tav.type)) {
|
||||
Type *bt = base_type(node->tav.type);
|
||||
GB_ASSERT(bt->kind == Type_EnumeratedArray);
|
||||
index_value = exact_value_sub(index_value, bt->EnumeratedArray.min_value);
|
||||
}
|
||||
|
||||
i64 field_index = exact_value_to_i64(index_value);
|
||||
if (index == field_index) {
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
value = tav.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i32 count = (i32)cl->elems.count;
|
||||
if (count < index) {
|
||||
if (success_) *success_ = false;
|
||||
if (finish_) *finish_ = true;
|
||||
return empty_exact_value;
|
||||
}
|
||||
TypeAndValue tav = cl->elems[index]->tav;
|
||||
if (tav.mode == Addressing_Constant) {
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = false;
|
||||
return tav.value;
|
||||
} else {
|
||||
GB_ASSERT(is_type_untyped_nil(tav.type));
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = false;
|
||||
return tav.value;
|
||||
}
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
default:
|
||||
// TODO(bill): Should this be a general fallback?
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
return empty_exact_value;
|
||||
}
|
||||
|
||||
if (finish_) *finish_ = false;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selection sel, bool *success_) {
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
@@ -3169,39 +3305,11 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
|
||||
i32 index = sel.index[0];
|
||||
sel = sub_selection(sel, 1);
|
||||
|
||||
Ast *node = value.value_compound;
|
||||
switch (node->kind) {
|
||||
case_ast_node(cl, CompoundLit, node);
|
||||
if (cl->elems.count == 0) {
|
||||
if (success_) *success_ = true;
|
||||
return empty_exact_value;
|
||||
}
|
||||
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
GB_PANIC("TODO");
|
||||
} else {
|
||||
i32 count = (i32)cl->elems.count;
|
||||
if (count < index) {
|
||||
if (success_) *success_ = false;
|
||||
return empty_exact_value;
|
||||
}
|
||||
TypeAndValue tav = cl->elems[index]->tav;
|
||||
if (tav.mode == Addressing_Constant) {
|
||||
value = tav.value;
|
||||
} else {
|
||||
GB_ASSERT(is_type_untyped_nil(tav.type));
|
||||
value = tav.value;
|
||||
}
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
default:
|
||||
if (success_) *success_ = true;
|
||||
return empty_exact_value;
|
||||
bool finish = false;
|
||||
value = get_constant_field_single(c, value, index, success_, &finish);
|
||||
if (finish) {
|
||||
return value;
|
||||
}
|
||||
|
||||
depth += 1;
|
||||
}
|
||||
|
||||
if (success_) *success_ = true;
|
||||
@@ -3366,6 +3474,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
||||
operand->expr = node;
|
||||
operand->value = field_value;
|
||||
operand->type = entity->type;
|
||||
add_entity_use(c, selector, entity);
|
||||
add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -3389,6 +3499,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
||||
operand->expr = node;
|
||||
operand->value = field_value;
|
||||
operand->type = entity->type;
|
||||
add_entity_use(c, selector, entity);
|
||||
add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -7002,6 +7114,13 @@ bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count,
|
||||
}
|
||||
o->type = t_u8;
|
||||
return true;
|
||||
} else if (t->Basic.kind == Basic_UntypedString) {
|
||||
if (o->mode == Addressing_Constant) {
|
||||
*max_count = o->value.value_string.len;
|
||||
o->type = t_u8;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -7733,6 +7852,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
error(elem, "Expected a constant integer as an array field");
|
||||
continue;
|
||||
}
|
||||
// add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
|
||||
|
||||
i64 index = exact_value_to_i64(op_index.value);
|
||||
|
||||
@@ -7913,13 +8033,13 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
|
||||
|
||||
// NOTE(bill): These are sanity checks for invalid enum values
|
||||
if (max_type_count >= 0 && (lo < total_lo || lo >= total_hi)) {
|
||||
if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
|
||||
gbString lo_str = expr_to_string(x.expr);
|
||||
error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
|
||||
gb_string_free(lo_str);
|
||||
continue;
|
||||
}
|
||||
if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
|
||||
if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
|
||||
gbString hi_str = expr_to_string(y.expr);
|
||||
error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
|
||||
gb_string_free(hi_str);
|
||||
@@ -7946,7 +8066,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
|
||||
i64 index = exact_value_to_i64(op_index.value);
|
||||
|
||||
if (max_type_count >= 0 && (index < total_lo || index >= total_hi)) {
|
||||
if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
|
||||
gbString idx_str = expr_to_string(op_index.expr);
|
||||
error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
|
||||
gb_string_free(idx_str);
|
||||
@@ -8506,7 +8626,15 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
|
||||
|
||||
if (is_const) {
|
||||
valid = false;
|
||||
if (is_type_array(t)) {
|
||||
// Okay
|
||||
} else if (is_type_enumerated_array(t)) {
|
||||
// Okay
|
||||
} else if (is_type_string(t)) {
|
||||
// Okay
|
||||
} else {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
@@ -8515,7 +8643,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
defer (gb_string_free(str));
|
||||
defer (gb_string_free(type_str));
|
||||
if (is_const) {
|
||||
error(o->expr, "Cannot index a constant '%s'", str);
|
||||
error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
|
||||
} else {
|
||||
error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
|
||||
}
|
||||
@@ -8542,6 +8670,20 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
|
||||
i64 index = 0;
|
||||
bool ok = check_index_value(c, false, ie->index, max_count, &index, index_type_hint);
|
||||
if (is_const) {
|
||||
if (index < 0) {
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot index a constant '%s'", str);
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
} else if (ok) {
|
||||
ExactValue value = type_and_value_of_expr(ie->expr).value;
|
||||
o->mode = Addressing_Constant;
|
||||
o->value = get_constant_field_single(c, value, cast(i32)index, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
node->viral_state_flags |= ie->index->viral_state_flags;
|
||||
case_end;
|
||||
@@ -8563,7 +8705,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
Type *t = base_type(type_deref(o->type));
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind == Basic_string) {
|
||||
if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
|
||||
valid = true;
|
||||
if (o->mode == Addressing_Constant) {
|
||||
max_count = o->value.value_string.len;
|
||||
@@ -8651,6 +8793,36 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_string(t) && max_count >= 0) {
|
||||
bool all_constant = true;
|
||||
for (isize i = 0; i < gb_count_of(nodes); i++) {
|
||||
if (nodes[i] != nullptr) {
|
||||
TypeAndValue tav = type_and_value_of_expr(nodes[i]);
|
||||
if (tav.mode != Addressing_Constant) {
|
||||
all_constant = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!all_constant) {
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot slice '%s' with non-constant indices", str);
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
|
||||
String s = {};
|
||||
if (o->value.kind == ExactValue_String) {
|
||||
s = o->value.value_string;
|
||||
}
|
||||
|
||||
o->mode = Addressing_Constant;
|
||||
o->type = t;
|
||||
o->value = exact_value_string(substring(s, indices[0], indices[1]));
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
|
||||
@@ -8729,9 +8901,9 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
|
||||
|
||||
if (type != nullptr && is_type_untyped(type)) {
|
||||
add_untyped(&c->checker->info, node, false, o->mode, type, value);
|
||||
} else {
|
||||
add_type_and_value(&c->checker->info, node, o->mode, type, value);
|
||||
}
|
||||
add_type_and_value(&c->checker->info, node, o->mode, type, value);
|
||||
|
||||
return kind;
|
||||
}
|
||||
|
||||
|
||||
@@ -6978,6 +6978,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
||||
return ir_addr_load(proc, ir_build_addr(proc, expr));
|
||||
}
|
||||
|
||||
if (tv.mode == Addressing_Constant) {
|
||||
GB_ASSERT(tv.value.kind == ExactValue_Invalid);
|
||||
// NOTE(bill): Zero value constant
|
||||
return ir_add_module_constant(proc->module, tv.type, tv.value);
|
||||
}
|
||||
|
||||
|
||||
switch (expr->kind) {
|
||||
case_ast_node(bl, BasicLit, expr);
|
||||
TokenPos pos = bl->token.pos;
|
||||
|
||||
+9
-1
@@ -339,7 +339,15 @@ Selection selection_combine(Selection const &lhs, Selection const &rhs) {
|
||||
Selection sub_selection(Selection const &sel, isize offset) {
|
||||
Selection res = {};
|
||||
res.index.data = sel.index.data + offset;
|
||||
res.index.count = sel.index.count - offset;
|
||||
res.index.count = gb_max(sel.index.count - offset, 0);
|
||||
res.index.capacity = res.index.count;
|
||||
return res;
|
||||
}
|
||||
|
||||
Selection sub_selection_with_length(Selection const &sel, isize offset, isize len) {
|
||||
Selection res = {};
|
||||
res.index.data = sel.index.data + offset;
|
||||
res.index.count = gb_max(len, gb_max(sel.index.count - offset, 0));
|
||||
res.index.capacity = res.index.count;
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user