Add constant literal expressions

This commit is contained in:
gingerBill
2019-12-27 15:49:52 +00:00
parent 880c7f01a8
commit f99f351e01
5 changed files with 284 additions and 42 deletions

View File

@@ -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;
}