Bug fix: comparisons with shifts

This commit is contained in:
Ginger Bill
2017-01-15 12:00:13 +00:00
parent c29d433e38
commit 6fe25badf0
9 changed files with 859 additions and 498 deletions
+102 -226
View File
@@ -28,143 +28,11 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) {
typedef struct DelayedEntity {
AstNode * ident;
Entity * entity;
DeclInfo * decl;
} DelayedEntity;
typedef struct DelayedOtherFields {
Entity **other_fields;
isize other_field_count;
isize other_field_index;
MapEntity *entity_map;
} DelayedOtherFields;
typedef Array(DelayedEntity) DelayedEntities;
void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof);
void check_local_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
error_node(ws->cond, "Non-boolean condition in `when` statement");
}
if (operand.mode != Addressing_Constant) {
error_node(ws->cond, "Non-constant condition in `when` statement");
}
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
error_node(ws->cond, "Invalid body for `when` statement");
} else {
if (operand.value.kind == ExactValue_Bool &&
operand.value.value_bool) {
check_local_collect_entities(c, ws->body->BlockStmt.stmts, delayed_entities, dof);
} else if (ws->else_stmt) {
switch (ws->else_stmt->kind) {
case AstNode_BlockStmt:
check_local_collect_entities(c, ws->else_stmt->BlockStmt.stmts, delayed_entities, dof);
break;
case AstNode_WhenStmt:
check_local_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, delayed_entities, dof);
break;
default:
error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
break;
}
}
}
}
// NOTE(bill): The `dof` is for use within records
void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
for_array(i, nodes) {
AstNode *node = nodes.e[i];
switch (node->kind) {
case_ast_node(ws, WhenStmt, node);
// Will be handled later
case_end;
case_ast_node(vd, ValueDecl, node);
if (vd->is_var) {
// NOTE(bill): Handled later
} else {
for_array(i, vd->names) {
AstNode *name = vd->names.e[i];
if (name->kind != AstNode_Ident) {
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
continue;
}
AstNode *init = NULL;
if (i < vd->values.count) {
init = vd->values.e[i];
}
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
Entity *e = NULL;
AstNode *up_init = unparen_expr(init);
if (init != NULL && is_ast_node_type(up_init)) {
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
d->type_expr = init;
d->init_expr = init;
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
d->proc_lit = init;
} else {
e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, (ExactValue){0});
d->type_expr = vd->type;
d->init_expr = init;
}
GB_ASSERT(e != NULL);
e->identifier = name;
add_entity_and_decl_info(c, name, e, d);
DelayedEntity delay = {name, e, d};
array_add(delayed_entities, delay);
}
check_arity_match(c, vd);
}
case_end;
#if 0
case_ast_node(pd, ProcDecl, node);
if (!ast_node_expect(pd->name, AstNode_Ident)) {
break;
}
Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
e->identifier = pd->name;
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
d->proc_lit = node;
add_entity_and_decl_info(c, pd->name, e, d);
check_entity_decl(c, e, d, NULL, NULL);
case_end;
#endif
}
}
// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
for_array(i, nodes) {
AstNode *node = nodes.e[i];
switch (node->kind) {
case_ast_node(ws, WhenStmt, node);
check_local_collect_entities_from_when_stmt(c, ws, delayed_entities, dof);
case_end;
}
}
}
void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size, DelayedOtherFields *dof) {
void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size) {
GB_ASSERT(!c->context.scope->is_file);
DelayedEntities delayed_entities;
array_init_reserve(&delayed_entities, heap_allocator(), reserve_size);
check_local_collect_entities(c, nodes, &delayed_entities, dof);
check_collect_entities(c, nodes, NULL, &delayed_entities);
for_array(i, delayed_entities) {
DelayedEntity delayed = delayed_entities.e[i];
@@ -462,7 +330,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
Type *type = check_type_extra(c, f->type, NULL);
if (f->is_using) {
if (f->flags&FieldFlag_using) {
if (f->names.count > 1) {
error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type");
}
@@ -476,7 +344,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
Token name_token = name->Ident;
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->is_using, cast(i32)field_index);
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->flags&FieldFlag_using, cast(i32)field_index);
e->identifier = name;
if (str_eq(name_token.string, str_lit("_"))) {
fields[field_index++] = e;
@@ -495,7 +363,7 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
}
if (f->is_using) {
if (f->flags&FieldFlag_using) {
Type *t = base_type(type_deref(type));
if (!is_type_struct(t) && !is_type_raw_union(t) &&
f->names.count >= 1 &&
@@ -843,7 +711,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
for_array(j, p->names) {
AstNode *name = p->names.e[j];
if (ast_node_expect(name, AstNode_Ident)) {
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->is_using);
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->flags&FieldFlag_using);
add_entity(c, scope, name, param);
variables[variable_index++] = param;
}
@@ -966,25 +834,15 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) {
o->type = t_invalid;
return;
}
#if 0
if (e->Variable.param) {
o->mode = Addressing_Value;
} else {
o->mode = Addressing_Variable;
}
#else
o->mode = Addressing_Variable;
if (e->Variable.is_immutable) {
o->mode = Addressing_Value;
}
#endif
break;
case Entity_TypeName: {
o->mode = Addressing_Type;
#if 1
// TODO(bill): Fix cyclical dependancy checker
#endif
// TODO(bill): Fix cyclical dependancy checker
} break;
case Entity_Procedure:
@@ -1028,7 +886,7 @@ i64 check_array_count(Checker *c, AstNode *e) {
}
return 0;
}
Type *type = base_type(base_enum_type(o.type));
Type *type = base_type(o.type);
if (is_type_untyped(type) || is_type_integer(type)) {
if (o.value.kind == ExactValue_Integer) {
i64 count = o.value.value_integer;
@@ -1252,7 +1110,7 @@ end:
bool check_unary_op(Checker *c, Operand *o, Token op) {
// TODO(bill): Handle errors correctly
Type *type = base_type(base_enum_type(base_vector_type(o->type)));
Type *type = base_type(base_vector_type(o->type));
gbString str = NULL;
switch (op.kind) {
case Token_Add:
@@ -1288,7 +1146,7 @@ bool check_unary_op(Checker *c, Operand *o, Token op) {
bool check_binary_op(Checker *c, Operand *o, Token op) {
// TODO(bill): Handle errors correctly
Type *type = base_type(base_enum_type(base_vector_type(o->type)));
Type *type = base_type(base_vector_type(o->type));
switch (op.kind) {
case Token_Sub:
case Token_SubEq:
@@ -1359,6 +1217,7 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
return true;
}
bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value) {
if (in_value.kind == ExactValue_Invalid) {
// NOTE(bill): There's already been an error
@@ -1389,7 +1248,6 @@ bool check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exa
}
i64 imax = (1ll << (s-1ll));
switch (type->Basic.kind) {
case Basic_i8:
case Basic_i16:
@@ -1581,16 +1439,15 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
o->mode = Addressing_Value;
}
void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
void check_comparison(Checker *c, Operand *x, Operand *y, TokenKind op) {
gbString err_str = NULL;
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
if (check_is_assignable_to(c, x, y->type) ||
check_is_assignable_to(c, y, x->type)) {
Type *err_type = x->type;
bool defined = false;
switch (op.kind) {
switch (op) {
case Token_CmpEq:
case Token_NotEq:
defined = is_type_comparable(x->type);
@@ -1615,7 +1472,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
if (!defined) {
gbString type_string = type_to_string(err_type);
err_str = gb_string_make(c->tmp_allocator,
gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(op.string), type_string));
gb_bprintf("operator `%.*s` not defined for type `%s`", LIT(token_strings[op]), type_string));
gb_string_free(type_string);
}
} else {
@@ -1633,7 +1490,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
} else {
if (x->mode == Addressing_Constant &&
y->mode == Addressing_Constant) {
x->value = make_exact_value_bool(compare_exact_values(op.kind, x->value, y->value));
x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value));
} else {
x->mode = Addressing_Value;
@@ -1650,8 +1507,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
if (err_str != NULL) {
gb_string_free(err_str);
};
}
gb_temp_arena_memory_end(tmp);
}
@@ -1665,7 +1521,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
}
bool x_is_untyped = is_type_untyped(x->type);
if (!(is_type_integer(base_enum_type(x->type)) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
gbString err_str = expr_to_string(x->expr);
error_node(node, "Shifted operand `%s` must be an integer", err_str);
gb_string_free(err_str);
@@ -1673,7 +1529,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
return;
}
if (is_type_unsigned(base_enum_type(y->type))) {
if (is_type_unsigned(y->type)) {
} else if (is_type_untyped(y->type)) {
convert_to_typed(c, y, t_untyped_integer, 0);
@@ -1702,7 +1558,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
}
u64 amount = cast(u64)y_val.value_integer;
if (amount > 1074) {
if (amount > 64) {
gbString err_str = expr_to_string(y->expr);
error_node(node, "Shift amount too large: `%s`", err_str);
gb_string_free(err_str);
@@ -1710,7 +1566,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
return;
}
if (!is_type_integer(base_enum_type(x->type))) {
if (!is_type_integer(x->type)) {
// NOTE(bill): It could be an untyped float but still representable
// as an integer
x->type = t_untyped_integer;
@@ -1719,17 +1575,19 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
x->value = exact_value_shift(be->op.kind, x_val, make_exact_value_integer(amount));
if (is_type_typed(x->type)) {
check_is_expressible(c, x, base_type(base_enum_type(x->type)));
check_is_expressible(c, x, base_type(x->type));
}
return;
}
TokenPos pos = ast_node_token(x->expr).pos;
if (x_is_untyped) {
ExprInfo *info = map_expr_info_get(&c->info.untyped, hash_pointer(x->expr));
if (info != NULL) {
info->is_lhs = true;
}
x->mode = Addressing_Value;
// x->value = x_val;
return;
}
}
@@ -1740,6 +1598,14 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
gb_string_free(err_str);
}
if (!is_type_integer(x->type)) {
gbString err_str = expr_to_string(y->expr);
error_node(node, "Shift operand `%s` must be an integer", err_str);
gb_string_free(err_str);
x->mode = Addressing_Invalid;
return;
}
x->mode = Addressing_Value;
}
@@ -2129,10 +1995,10 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
}
if (op.kind == Token_Add || op.kind == Token_Sub) {
if (is_type_pointer(x->type) && is_type_integer(base_enum_type(y->type))) {
if (is_type_pointer(x->type) && is_type_integer(y->type)) {
*x = check_ptr_addition(c, op.kind, x, y, node);
return;
} else if (is_type_integer(base_enum_type(x->type)) && is_type_pointer(y->type)) {
} else if (is_type_integer(x->type) && is_type_pointer(y->type)) {
if (op.kind == Token_Sub) {
gbString lhs = expr_to_string(x->expr);
gbString rhs = expr_to_string(y->expr);
@@ -2159,7 +2025,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
}
if (token_is_comparison(op)) {
check_comparison(c, x, y, op);
check_comparison(c, x, y, op.kind);
return;
}
@@ -2259,48 +2125,58 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
if (found == NULL) {
return;
}
ExprInfo old = *found;
switch (e->kind) {
case_ast_node(ue, UnaryExpr, e);
if (found->value.kind != ExactValue_Invalid) {
if (old.value.kind != ExactValue_Invalid) {
// NOTE(bill): if `e` is constant, the operands will be constant too.
// They don't need to be updated as they will be updated later and
// checked at the end of general checking stage.
break;
}
update_expr_type(c, ue->expr, type, final);
case_end;
case_ast_node(be, BinaryExpr, e);
if (found->value.kind != ExactValue_Invalid) {
if (old.value.kind != ExactValue_Invalid) {
// See above note in UnaryExpr case
break;
}
if (!token_is_comparison(be->op)) {
if (token_is_shift(be->op)) {
update_expr_type(c, be->left, type, final);
} else {
update_expr_type(c, be->left, type, final);
update_expr_type(c, be->right, type, final);
}
if (token_is_comparison(be->op)) {
}
else if (token_is_shift(be->op)) {
update_expr_type(c, be->left, type, final);
} else {
update_expr_type(c, be->left, type, final);
update_expr_type(c, be->right, type, final);
}
case_end;
case_ast_node(pe, ParenExpr, e);
update_expr_type(c, pe->expr, type, final);
case_end;
}
if (!final && is_type_untyped(type)) {
found->type = base_type(type);
map_expr_info_set(&c->info.untyped, key, *found);
} else {
ExprInfo old = *found;
map_expr_info_remove(&c->info.untyped, key);
if (old.is_lhs && !is_type_integer(type)) {
gbString expr_str = expr_to_string(e);
gbString type_str = type_to_string(type);
error_node(e, "Shifted operand %s must be an integer, got %s", expr_str, type_str);
gb_string_free(type_str);
gb_string_free(expr_str);
return;
}
add_type_and_value(&c->info, e, found->mode, type, found->value);
old.type = base_type(type);
map_expr_info_set(&c->info.untyped, key, old);
return;
}
// We need to remove it and then give it a new one
map_expr_info_remove(&c->info.untyped, key);
if (old.is_lhs && !is_type_integer(type)) {
gbString expr_str = expr_to_string(e);
gbString type_str = type_to_string(type);
error_node(e, "Shifted operand %s must be an integer, got %s", expr_str, type_str);
gb_string_free(type_str);
gb_string_free(expr_str);
return;
}
add_type_and_value(&c->info, e, old.mode, type, old.value);
}
void update_expr_value(Checker *c, AstNode *e, ExactValue value) {
@@ -2426,7 +2302,7 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val
return false;
}
if (!is_type_integer(base_enum_type(operand.type))) {
if (!is_type_integer(operand.type)) {
gbString expr_str = expr_to_string(operand.expr);
error_node(operand.expr, "Index `%s` must be an integer", expr_str);
gb_string_free(expr_str);
@@ -2665,7 +2541,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
if (op.mode == Addressing_Invalid) {
return false;
}
if (!is_type_integer(base_enum_type(op.type))) {
if (!is_type_integer(op.type)) {
gbString type_str = type_to_string(op.type);
error_node(call, "Length for `new_slice` must be an integer, got `%s`", type_str);
gb_string_free(type_str);
@@ -4605,7 +4481,6 @@ gbString write_expr_to_string(gbString str, AstNode *node);
gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
for_array(i, params) {
ast_node(p, Field, params.e[i]);
if (i > 0) {
str = gb_string_appendc(str, sep);
}
@@ -4615,6 +4490,18 @@ gbString write_params_to_string(gbString str, AstNodeArray params, char *sep) {
return str;
}
gbString write_record_fields_to_string(gbString str, AstNodeArray params) {
for_array(i, params) {
if (i > 0) {
str = gb_string_appendc(str, " ");
}
str = write_expr_to_string(str, params.e[i]);
str = gb_string_appendc(str, ";");
}
return str;
}
gbString string_append_token(gbString str, Token token) {
if (token.string.len > 0) {
return gb_string_append_length(str, token.string.text, token.string.len);
@@ -4758,12 +4645,12 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
str = write_expr_to_string(str, vt->elem);
case_end;
case_ast_node(p, Field, node);
if (p->is_using) {
case_ast_node(f, Field, node);
if (f->flags&FieldFlag_using) {
str = gb_string_appendc(str, "using ");
}
for_array(i, p->names) {
AstNode *name = p->names.e[i];
for_array(i, f->names) {
AstNode *name = f->names.e[i];
if (i > 0) {
str = gb_string_appendc(str, ", ");
}
@@ -4771,7 +4658,10 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
}
str = gb_string_appendc(str, ": ");
str = write_expr_to_string(str, p->type);
if (f->flags&FieldFlag_ellipsis) {
str = gb_string_appendc(str, "...");
}
str = write_expr_to_string(str, f->type);
case_end;
case_ast_node(ce, CallExpr, node);
@@ -4798,37 +4688,22 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
str = gb_string_appendc(str, "struct ");
if (st->is_packed) str = gb_string_appendc(str, "#packed ");
if (st->is_ordered) str = gb_string_appendc(str, "#ordered ");
for_array(i, st->fields) {
if (i > 0) {
str = gb_string_appendc(str, "; ");
}
str = write_expr_to_string(str, st->fields.e[i]);
}
// str = write_params_to_string(str, st->decl_list, ", ");
str = gb_string_appendc(str, "{");
str = write_record_fields_to_string(str, st->fields);
str = gb_string_appendc(str, "}");
case_end;
case_ast_node(st, RawUnionType, node);
str = gb_string_appendc(str, "raw_union {");
for_array(i, st->fields) {
if (i > 0) {
str = gb_string_appendc(str, "; ");
}
str = write_expr_to_string(str, st->fields.e[i]);
}
// str = write_params_to_string(str, st->decl_list, ", ");
str = gb_string_appendc(str, "raw_union ");
str = gb_string_appendc(str, "{");
str = write_record_fields_to_string(str, st->fields);
str = gb_string_appendc(str, "}");
case_end;
case_ast_node(st, UnionType, node);
str = gb_string_appendc(str, "union {");
for_array(i, st->fields) {
if (i > 0) {
str = gb_string_appendc(str, "; ");
}
str = write_expr_to_string(str, st->fields.e[i]);
}
// str = write_params_to_string(str, st->decl_list, ", ");
str = gb_string_appendc(str, "union ");
str = gb_string_appendc(str, "{");
str = write_record_fields_to_string(str, st->fields);
str = gb_string_appendc(str, "}");
case_end;
@@ -4839,6 +4714,7 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
str = gb_string_appendc(str, " ");
}
str = gb_string_appendc(str, "{");
str = write_params_to_string(str, et->fields, ", ");
str = gb_string_appendc(str, "}");
case_end;