mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-20 00:52:33 +00:00
Add constant literal expressions
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user