mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-22 15:37:51 +00:00
Bug fix: comparisons with shifts
This commit is contained in:
+102
-226
@@ -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;
|
||||
|
||||
|
||||
+7
-8
@@ -3,7 +3,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count);
|
||||
|
||||
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
|
||||
flags &= ~Stmt_FallthroughAllowed;
|
||||
@@ -602,9 +602,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
|
||||
Type *type = x.type;
|
||||
Type *bt = base_type(base_enum_type(type));
|
||||
|
||||
if (!is_type_integer(bt) && !is_type_float(bt) && !is_type_pointer(bt)) {
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
goto skip_expr;
|
||||
}
|
||||
@@ -784,21 +782,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
for_array(j, cc->list) {
|
||||
AstNode *expr = cc->list.e[j];
|
||||
Operand y = {0};
|
||||
Operand z = {0};
|
||||
Token eq = {Token_CmpEq};
|
||||
|
||||
check_expr(c, &y, expr);
|
||||
if (x.mode == Addressing_Invalid ||
|
||||
y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &y, x.type, 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
z = y;
|
||||
check_comparison(c, &z, &x, eq);
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(c, &z, &x, Token_CmpEq);
|
||||
if (z.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
@@ -806,6 +804,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (y.value.kind != ExactValue_Invalid) {
|
||||
HashKey key = hash_exact_value(y.value);
|
||||
TypeAndToken *found = map_type_and_token_get(&seen, key);
|
||||
|
||||
+261
-147
@@ -206,11 +206,7 @@ gb_global ImplicitValueInfo implicit_value_infos[ImplicitValue_Count] = {0};
|
||||
|
||||
|
||||
|
||||
typedef struct CheckerContext {
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
u32 stmt_state_flags;
|
||||
} CheckerContext;
|
||||
|
||||
|
||||
#define MAP_TYPE TypeAndValue
|
||||
#define MAP_PROC map_tav_
|
||||
@@ -242,6 +238,11 @@ typedef struct DelayedDecl {
|
||||
AstNode *decl;
|
||||
} DelayedDecl;
|
||||
|
||||
typedef struct CheckerContext {
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
u32 stmt_state_flags;
|
||||
} CheckerContext;
|
||||
|
||||
// NOTE(bill): Symbol tables
|
||||
typedef struct CheckerInfo {
|
||||
@@ -284,6 +285,15 @@ typedef struct Checker {
|
||||
} Checker;
|
||||
|
||||
|
||||
typedef struct DelayedEntity {
|
||||
AstNode * ident;
|
||||
Entity * entity;
|
||||
DeclInfo * decl;
|
||||
} DelayedEntity;
|
||||
|
||||
typedef Array(DelayedEntity) DelayedEntities;
|
||||
|
||||
|
||||
|
||||
|
||||
void init_declaration_info(DeclInfo *d, Scope *scope) {
|
||||
@@ -388,6 +398,35 @@ void check_close_scope(Checker *c) {
|
||||
c->context.scope = c->context.scope->parent;
|
||||
}
|
||||
|
||||
|
||||
Entity *current_scope_lookup_entity(Scope *s, String name) {
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(&s->elements, key);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
for_array(i, s->shared) {
|
||||
Scope *shared = s->shared.e[i];
|
||||
Entity **found = map_entity_get(&shared->elements, key);
|
||||
if (found) {
|
||||
Entity *e = *found;
|
||||
if (e->kind == Entity_Variable &&
|
||||
!e->scope->is_file &&
|
||||
!e->scope->is_global) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->scope != shared) {
|
||||
// Do not return imported entities even #include ones
|
||||
continue;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) {
|
||||
bool gone_thru_proc = false;
|
||||
bool gone_thru_file = false;
|
||||
@@ -456,21 +495,6 @@ Entity *scope_lookup_entity(Scope *s, String name) {
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *current_scope_lookup_entity(Scope *s, String name) {
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(&s->elements, key);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
for_array(i, s->shared) {
|
||||
Entity **found = map_entity_get(&s->shared.e[i]->elements, key);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Entity *scope_insert_entity(Scope *s, Entity *entity) {
|
||||
@@ -769,7 +793,8 @@ void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
|
||||
if (identifier->kind != AstNode_Ident) {
|
||||
return;
|
||||
}
|
||||
map_entity_set(&c->info.uses, hash_pointer(identifier), entity);
|
||||
HashKey key = hash_pointer(identifier);
|
||||
map_entity_set(&c->info.uses, key, entity);
|
||||
add_declaration_dependency(c, entity); // TODO(bill): Should this be here?
|
||||
}
|
||||
|
||||
@@ -942,7 +967,8 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
|
||||
TokenPos zero_pos = {0};
|
||||
global_error_collector.prev = zero_pos;
|
||||
c->curr_ast_file = file;
|
||||
c->context.decl = file->decl_info;
|
||||
c->context.decl = file->decl_info;
|
||||
c->context.scope = file->scope;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,12 +1125,20 @@ void init_preload(Checker *c) {
|
||||
c->done_preload = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d);
|
||||
void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scopes, DelayedEntities *delayed_entities);
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapScope *file_scopes, DelayedEntities *delayed_entities);
|
||||
|
||||
#include "check_expr.c"
|
||||
#include "check_decl.c"
|
||||
#include "check_stmt.c"
|
||||
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
||||
isize lhs = d->names.count;
|
||||
isize rhs = d->values.count;
|
||||
@@ -1135,6 +1169,202 @@ bool check_arity_match(Checker *c, AstNodeValueDecl *d) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, MapScope *file_scopes, DelayedEntities *delayed_entities) {
|
||||
// NOTE(bill): File scope and local scope are different kinds of scopes
|
||||
GB_ASSERT(file_scopes == NULL || delayed_entities == NULL);
|
||||
|
||||
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_collect_entities(c, ws->body->BlockStmt.stmts, file_scopes, delayed_entities);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt:
|
||||
check_collect_entities(c, ws->else_stmt->BlockStmt.stmts, file_scopes, delayed_entities);
|
||||
break;
|
||||
case AstNode_WhenStmt:
|
||||
check_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, file_scopes, delayed_entities);
|
||||
break;
|
||||
default:
|
||||
error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): If file_scopes == NULL, this will act like a local scope
|
||||
void check_collect_entities(Checker *c, AstNodeArray nodes, MapScope *file_scopes, DelayedEntities *delayed_entities) {
|
||||
// NOTE(bill): File scope and local scope are different kinds of scopes
|
||||
GB_ASSERT(file_scopes == NULL || delayed_entities == NULL);
|
||||
if (file_scopes != NULL) {
|
||||
GB_ASSERT(c->context.scope->is_file);
|
||||
}
|
||||
if (delayed_entities != NULL) {
|
||||
GB_ASSERT(!c->context.scope->is_file);
|
||||
}
|
||||
|
||||
for_array(decl_index, nodes) {
|
||||
AstNode *decl = nodes.e[decl_index];
|
||||
if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (decl->kind) {
|
||||
case_ast_node(bd, BadDecl, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhenStmt, decl);
|
||||
if (c->context.scope->is_file) {
|
||||
error_node(decl, "`when` statements are not allowed at file scope");
|
||||
} else {
|
||||
// Will be handled later
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
if (vd->is_var) {
|
||||
if (!c->context.scope->is_file) {
|
||||
// NOTE(bill): local scope -> handle later and in order
|
||||
break;
|
||||
}
|
||||
// NOTE(bill): You need to store the entity information here unline a constant declaration
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (vd->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), c->context.scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values.e[0];
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < vd->values.count) {
|
||||
value = vd->values.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;
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident, NULL);
|
||||
e->identifier = name;
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
} 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);
|
||||
// TODO(bill): What if vd->type != NULL??? How to handle this case?
|
||||
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;
|
||||
d->type_expr = vd->type;
|
||||
} 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);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
if (!c->context.scope->is_file) {
|
||||
if (id->is_import) {
|
||||
error_node(decl, "#import declarations are only allowed in the file scope");
|
||||
} else {
|
||||
error_node(decl, "#include declarations are only allowed in the file scope");
|
||||
}
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
DelayedDecl di = {c->context.scope, decl};
|
||||
array_add(&c->delayed_imports, di);
|
||||
case_end;
|
||||
case_ast_node(fl, ForeignLibrary, decl);
|
||||
if (!c->context.scope->is_file) {
|
||||
error_node(decl, "#foreign_library declarations are only allowed in the file scope");
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
|
||||
DelayedDecl di = {c->context.scope, decl};
|
||||
array_add(&c->delayed_foreign_libraries, di);
|
||||
case_end;
|
||||
default:
|
||||
if (c->context.scope->is_file) {
|
||||
error_node(decl, "Only declarations are allowed at file scope");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c->context.scope->is_file) {
|
||||
// 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_collect_entities_from_when_stmt(c, ws, file_scopes, delayed_entities);
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void check_all_global_entities(Checker *c) {
|
||||
@@ -1171,126 +1401,6 @@ void check_all_global_entities(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) {
|
||||
for_array(decl_index, nodes) {
|
||||
AstNode *decl = nodes.e[decl_index];
|
||||
if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (decl->kind) {
|
||||
case_ast_node(bd, BadDecl, decl);
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, ValueDecl, decl);
|
||||
if (vd->is_var) {
|
||||
// NOTE(bill): You need to store the entity information here unline a constant declaration
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclInfo *di = NULL;
|
||||
if (vd->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), parent_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values.e[0];
|
||||
}
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
AstNode *value = NULL;
|
||||
if (i < vd->values.count) {
|
||||
value = vd->values.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;
|
||||
}
|
||||
Entity *e = make_entity_variable(c->allocator, parent_scope, name->Ident, NULL);
|
||||
e->identifier = name;
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
d->var_decl_tags = vd->tags;
|
||||
}
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
} 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, parent_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);
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(id, ImportDecl, decl);
|
||||
if (!parent_scope->is_file) {
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
DelayedDecl di = {parent_scope, decl};
|
||||
array_add(&c->delayed_imports, di);
|
||||
case_end;
|
||||
case_ast_node(fl, ForeignLibrary, decl);
|
||||
if (!parent_scope->is_file) {
|
||||
// NOTE(bill): _Should_ be caught by the parser
|
||||
// TODO(bill): Better error handling if it isn't
|
||||
continue;
|
||||
}
|
||||
|
||||
DelayedDecl di = {parent_scope, decl};
|
||||
array_add(&c->delayed_foreign_libraries, di);
|
||||
case_end;
|
||||
default:
|
||||
if (parent_scope->is_file) {
|
||||
error_node(decl, "Only declarations are allowed at file scope");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_import_entities(Checker *c, MapScope *file_scopes) {
|
||||
for_array(i, c->delayed_imports) {
|
||||
@@ -1463,8 +1573,10 @@ void check_parsed_files(Checker *c) {
|
||||
// Collect Entities
|
||||
for_array(i, c->parser->files) {
|
||||
AstFile *f = &c->parser->files.e[i];
|
||||
CheckerContext prev_context = c->context;
|
||||
add_curr_ast_file(c, f);
|
||||
check_global_collect_entities_from_file(c, f->scope, f->decls, &file_scopes);
|
||||
check_collect_entities(c, f->decls, &file_scopes, NULL);
|
||||
c->context = prev_context;
|
||||
}
|
||||
|
||||
check_import_entities(c, &file_scopes);
|
||||
@@ -1472,7 +1584,7 @@ void check_parsed_files(Checker *c) {
|
||||
|
||||
check_all_global_entities(c);
|
||||
init_preload(c); // NOTE(bill): This could be setup previously through the use of `type_info(_of_val)`
|
||||
// NOTE(bill): Nothing is the global scope _should_ depend on this implicit value as implicit
|
||||
// NOTE(bill): Nothing in the global scope _should_ depend on this implicit value as implicit
|
||||
// values are only useful within procedures
|
||||
add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
|
||||
|
||||
@@ -1497,12 +1609,12 @@ void check_parsed_files(Checker *c) {
|
||||
// NOTE(bill): Nested procedures bodies will be added to this "queue"
|
||||
for_array(i, c->procs) {
|
||||
ProcedureInfo *pi = &c->procs.e[i];
|
||||
CheckerContext prev_context = c->context;
|
||||
add_curr_ast_file(c, pi->file);
|
||||
|
||||
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
||||
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
|
||||
|
||||
CheckerContext prev_context = c->context;
|
||||
|
||||
if (bounds_check) {
|
||||
c->context.stmt_state_flags |= StmtStateFlag_bounds_check;
|
||||
@@ -1554,8 +1666,10 @@ void check_parsed_files(Checker *c) {
|
||||
for_array(i, c->info.definitions.entries) {
|
||||
Entity *e = c->info.definitions.entries.e[i].value;
|
||||
if (e->kind == Entity_TypeName) {
|
||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||
i64 align = type_align_of(c->sizes, c->allocator, e->type);
|
||||
if (e->type != NULL) {
|
||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||
i64 align = type_align_of(c->sizes, c->allocator, e->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1516,6 +1516,18 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
||||
|
||||
|
||||
switch (op) {
|
||||
case Token_Shl:
|
||||
case Token_Shr:
|
||||
left = ir_emit_conv(proc, left, type);
|
||||
if (!is_type_unsigned(ir_type(right))) {
|
||||
Type *t = t_u64;
|
||||
if (proc->module->sizes.word_size == 32) {
|
||||
t = t_u32;
|
||||
}
|
||||
right = ir_emit_conv(proc, right, t);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token_AndNot: {
|
||||
// NOTE(bill): x &~ y == x & (~y) == x & (y ~ -1)
|
||||
// NOTE(bill): "not" `x` == `x` "xor" `-1`
|
||||
@@ -1534,8 +1546,6 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
|
||||
case Token_And:
|
||||
case Token_Or:
|
||||
case Token_Xor:
|
||||
case Token_Shl:
|
||||
case Token_Shr:
|
||||
left = ir_emit_conv(proc, left, type);
|
||||
right = ir_emit_conv(proc, right, type);
|
||||
break;
|
||||
@@ -1949,15 +1959,15 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
||||
dst->kind == Type_Basic);
|
||||
i64 sz = type_size_of(proc->module->sizes, proc->module->allocator, src);
|
||||
i64 dz = type_size_of(proc->module->sizes, proc->module->allocator, dst);
|
||||
irConvKind kind = irConv_trunc;
|
||||
if (sz == dz) {
|
||||
// NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment
|
||||
return value;
|
||||
}
|
||||
|
||||
irConvKind kind = irConv_trunc;
|
||||
if (dz >= sz) {
|
||||
// NOTE(bill): Copy the value just for type correctness
|
||||
kind = irConv_bitcast;
|
||||
} else if (dz > sz) {
|
||||
kind = irConv_zext;
|
||||
}
|
||||
|
||||
return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst));
|
||||
}
|
||||
|
||||
@@ -2560,7 +2570,8 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
|
||||
expr = unparen_expr(expr);
|
||||
switch (expr->kind) {
|
||||
case_ast_node(bl, BasicLit, expr);
|
||||
GB_PANIC("Non-constant basic literal");
|
||||
TokenPos pos = bl->pos;
|
||||
GB_PANIC("Non-constant basic literal %.*s(%td:%td) - %.*s", LIT(pos.file), pos.line, pos.column, LIT(token_strings[bl->kind]));
|
||||
case_end;
|
||||
|
||||
case_ast_node(i, Ident, expr);
|
||||
@@ -2596,7 +2607,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv
|
||||
|
||||
case_ast_node(re, RunExpr, expr);
|
||||
// TODO(bill): Run Expression
|
||||
return ir_build_single_expr(proc, re->expr, tv);
|
||||
return ir_build_expr(proc, re->expr);
|
||||
case_end;
|
||||
|
||||
case_ast_node(de, DerefExpr, expr);
|
||||
@@ -5834,9 +5845,10 @@ void ir_gen_tree(irGen *s) {
|
||||
case Type_Proc: {
|
||||
tag = ir_emit_conv(proc, ti_ptr, t_type_info_procedure_ptr);
|
||||
|
||||
irValue *params = ir_emit_struct_ep(proc, tag, 0);
|
||||
irValue *results = ir_emit_struct_ep(proc, tag, 1);
|
||||
irValue *variadic = ir_emit_struct_ep(proc, tag, 2);
|
||||
irValue *params = ir_emit_struct_ep(proc, tag, 0);
|
||||
irValue *results = ir_emit_struct_ep(proc, tag, 1);
|
||||
irValue *variadic = ir_emit_struct_ep(proc, tag, 2);
|
||||
irValue *convention = ir_emit_struct_ep(proc, tag, 3);
|
||||
|
||||
if (t->Proc.params) {
|
||||
ir_emit_store(proc, params, ir_get_type_info_ptr(proc, type_info_data, t->Proc.params));
|
||||
@@ -5845,6 +5857,7 @@ void ir_gen_tree(irGen *s) {
|
||||
ir_emit_store(proc, results, ir_get_type_info_ptr(proc, type_info_data, t->Proc.results));
|
||||
}
|
||||
ir_emit_store(proc, variadic, ir_make_const_bool(a, t->Proc.variadic));
|
||||
ir_emit_store(proc, convention, ir_make_const_int(a, t->Proc.calling_convention));
|
||||
|
||||
// TODO(bill): Type_Info for procedures
|
||||
} break;
|
||||
|
||||
+41
-29
@@ -62,36 +62,41 @@ typedef struct Parser {
|
||||
} Parser;
|
||||
|
||||
typedef enum ProcTag {
|
||||
ProcTag_bounds_check = GB_BIT(0),
|
||||
ProcTag_no_bounds_check = GB_BIT(1),
|
||||
ProcTag_bounds_check = 1<<0,
|
||||
ProcTag_no_bounds_check = 1<<1,
|
||||
|
||||
ProcTag_foreign = GB_BIT(10),
|
||||
ProcTag_export = GB_BIT(11),
|
||||
ProcTag_link_name = GB_BIT(12),
|
||||
ProcTag_inline = GB_BIT(13),
|
||||
ProcTag_no_inline = GB_BIT(14),
|
||||
ProcTag_dll_import = GB_BIT(15),
|
||||
// ProcTag_dll_export = GB_BIT(16),
|
||||
ProcTag_foreign = 1<<10,
|
||||
ProcTag_export = 1<<11,
|
||||
ProcTag_link_name = 1<<12,
|
||||
ProcTag_inline = 1<<13,
|
||||
ProcTag_no_inline = 1<<14,
|
||||
ProcTag_dll_import = 1<<15,
|
||||
// ProcTag_dll_export = 1<<16,
|
||||
} ProcTag;
|
||||
|
||||
typedef enum ProcCallingConvention {
|
||||
ProcCC_Odin = 0,
|
||||
ProcCC_C,
|
||||
ProcCC_Std,
|
||||
ProcCC_Fast,
|
||||
ProcCC_C = 1,
|
||||
ProcCC_Std = 2,
|
||||
ProcCC_Fast = 3,
|
||||
|
||||
ProcCC_Invalid,
|
||||
} ProcCallingConvention;
|
||||
|
||||
typedef enum VarDeclTag {
|
||||
VarDeclTag_thread_local = GB_BIT(0),
|
||||
VarDeclTag_thread_local = 1<<0,
|
||||
} VarDeclTag;
|
||||
|
||||
typedef enum StmtStateFlag {
|
||||
StmtStateFlag_bounds_check = GB_BIT(0),
|
||||
StmtStateFlag_no_bounds_check = GB_BIT(1),
|
||||
StmtStateFlag_bounds_check = 1<<0,
|
||||
StmtStateFlag_no_bounds_check = 1<<1,
|
||||
} StmtStateFlag;
|
||||
|
||||
typedef enum FieldFlag {
|
||||
FieldFlag_using = 1<<0,
|
||||
FieldFlag_ellipsis = 1<<1,
|
||||
} FieldListTag;
|
||||
|
||||
AstNodeArray make_ast_node_array(AstFile *f) {
|
||||
AstNodeArray a;
|
||||
// array_init(&a, gb_arena_allocator(&f->arena));
|
||||
@@ -228,7 +233,10 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { Token token; AstNode *node; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { \
|
||||
Token token; \
|
||||
AstNode *node; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
AstNode *operand; \
|
||||
@@ -283,7 +291,7 @@ AST_NODE_KIND(_DeclEnd, "", i32) \
|
||||
AST_NODE_KIND(Field, "field", struct { \
|
||||
AstNodeArray names; \
|
||||
AstNode * type; \
|
||||
bool is_using; \
|
||||
u32 flags; \
|
||||
}) \
|
||||
AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||
AST_NODE_KIND(HelperType, "type", struct { \
|
||||
@@ -955,11 +963,11 @@ AstNode *make_bad_decl(AstFile *f, Token begin, Token end) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, bool is_using) {
|
||||
AstNode *make_field(AstFile *f, AstNodeArray names, AstNode *type, u32 flags) {
|
||||
AstNode *result = make_node(f, AstNode_Field);
|
||||
result->Field.names = names;
|
||||
result->Field.type = type;
|
||||
result->Field.is_using = is_using;
|
||||
result->Field.flags = flags;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2217,7 +2225,7 @@ AstNode *parse_proc_type(AstFile *f, String *foreign_name_, String *link_name_)
|
||||
return make_proc_type(f, proc_token, params, results, tags, cc);
|
||||
}
|
||||
|
||||
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using, bool ellipsis_ok,
|
||||
AstNodeArray parse_field_list(AstFile *f, isize *name_count_, u32 flags,
|
||||
TokenKind separator, TokenKind follow) {
|
||||
AstNodeArray params = make_ast_node_array(f);
|
||||
isize name_count = 0;
|
||||
@@ -2240,7 +2248,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
is_using = false;
|
||||
}
|
||||
|
||||
if (!allow_using && is_using) {
|
||||
if ((flags&FieldFlag_using) == 0 && is_using) {
|
||||
syntax_error(f->curr_token, "`using` is not allowed within this parameter list");
|
||||
is_using = false;
|
||||
}
|
||||
@@ -2250,7 +2258,7 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
expect_token_after(f, Token_Colon, "parameter list");
|
||||
|
||||
AstNode *type = NULL;
|
||||
if (ellipsis_ok && f->curr_token.kind == Token_Ellipsis) {
|
||||
if ((flags&FieldFlag_ellipsis) != 0 && f->curr_token.kind == Token_Ellipsis) {
|
||||
Token ellipsis = f->curr_token;
|
||||
next_token(f);
|
||||
type = parse_type_attempt(f);
|
||||
@@ -2273,7 +2281,11 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
syntax_error(f->curr_token, "Expected a type for this parameter declaration");
|
||||
}
|
||||
|
||||
AstNode *param = make_field(f, names, type, is_using);
|
||||
u32 flags = 0;
|
||||
if (is_using) {
|
||||
flags |= FieldFlag_using;
|
||||
}
|
||||
AstNode *param = make_field(f, names, type, flags);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (separator == Token_Semicolon) {
|
||||
@@ -2291,8 +2303,8 @@ AstNodeArray parse_field_list(AstFile *f, isize *name_count_, bool allow_using,
|
||||
}
|
||||
|
||||
|
||||
AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, bool allow_using, String context) {
|
||||
return parse_field_list(f, field_count_, allow_using, false, Token_Semicolon, Token_CloseBrace);
|
||||
AstNodeArray parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
|
||||
return parse_field_list(f, field_count_, flags, Token_Semicolon, Token_CloseBrace);
|
||||
}
|
||||
|
||||
AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
@@ -2385,7 +2397,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
|
||||
Token open = expect_token_after(f, Token_OpenBrace, "struct");
|
||||
isize decl_count = 0;
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, true, str_lit("struct"));
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("struct"));
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered);
|
||||
@@ -2395,7 +2407,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
Token token = expect_token(f, Token_union);
|
||||
Token open = expect_token_after(f, Token_OpenBrace, "union");
|
||||
isize decl_count = 0;
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, false, str_lit("union"));
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, 0, str_lit("union"));
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_union_type(f, token, decls, decl_count);
|
||||
@@ -2405,7 +2417,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
Token token = expect_token(f, Token_raw_union);
|
||||
Token open = expect_token_after(f, Token_OpenBrace, "raw_union");
|
||||
isize decl_count = 0;
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, true, str_lit("raw_union"));
|
||||
AstNodeArray decls = parse_record_fields(f, &decl_count, FieldFlag_using, str_lit("raw_union"));
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_raw_union_type(f, token, decls, decl_count);
|
||||
@@ -2479,7 +2491,7 @@ void parse_proc_signature(AstFile *f,
|
||||
AstNodeArray *params,
|
||||
AstNodeArray *results) {
|
||||
expect_token(f, Token_OpenParen);
|
||||
*params = parse_field_list(f, NULL, true, true, Token_Comma, Token_CloseParen);
|
||||
*params = parse_field_list(f, NULL, FieldFlag_using|FieldFlag_ellipsis, Token_Comma, Token_CloseParen);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
*results = parse_results(f);
|
||||
}
|
||||
|
||||
+48
-25
@@ -74,8 +74,11 @@ typedef struct TypeRecord {
|
||||
|
||||
// All record types
|
||||
// Theses are arrays
|
||||
Entity **fields; // Entity_Variable (otherwise Entity_TypeName if union)
|
||||
i32 field_count; // == offset_count is struct
|
||||
// Entity_Variable - struct/raw_union
|
||||
// Entity_TypeName - union
|
||||
// Entity_Constant - enum
|
||||
Entity **fields;
|
||||
i32 field_count; // == struct_offsets count
|
||||
AstNode *node;
|
||||
|
||||
i64 * struct_offsets;
|
||||
@@ -84,10 +87,10 @@ typedef struct TypeRecord {
|
||||
bool struct_is_ordered;
|
||||
Entity **fields_in_src_order; // Entity_Variable
|
||||
|
||||
Type * enum_base_type;
|
||||
Entity *enum_count;
|
||||
Entity *enum_min_value;
|
||||
Entity *enum_max_value;
|
||||
Type * enum_base_type;
|
||||
Entity * enum_count;
|
||||
Entity * enum_min_value;
|
||||
Entity * enum_max_value;
|
||||
} TypeRecord;
|
||||
|
||||
#define TYPE_KINDS \
|
||||
@@ -481,28 +484,28 @@ bool is_type_named(Type *t) {
|
||||
return t->kind == Type_Named;
|
||||
}
|
||||
bool is_type_boolean(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Boolean) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_integer(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Integer) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_unsigned(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Unsigned) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_numeric(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Numeric) != 0;
|
||||
}
|
||||
@@ -536,7 +539,7 @@ bool is_type_untyped(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_ordered(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
||||
}
|
||||
@@ -553,21 +556,21 @@ bool is_type_constant_type(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_float(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Float) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_f32(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_f32;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_f64(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_f64;
|
||||
}
|
||||
@@ -677,8 +680,13 @@ bool is_type_indexable(Type *t) {
|
||||
bool type_has_nil(Type *t) {
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return is_type_rawptr(t);
|
||||
case Type_Basic: {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_rawptr:
|
||||
case Basic_any:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case Type_Slice:
|
||||
case Type_Proc:
|
||||
case Type_Pointer:
|
||||
@@ -693,24 +701,22 @@ bool is_type_comparable(Type *t) {
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return t->kind != Basic_UntypedNil;
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_UntypedNil:
|
||||
case Basic_any:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case Type_Pointer:
|
||||
return true;
|
||||
case Type_Record: {
|
||||
if (false && is_type_struct(t)) {
|
||||
// TODO(bill): Should I even allow this?
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
if (!is_type_comparable(t->Record.fields[i]->type))
|
||||
return false;
|
||||
}
|
||||
} else if (is_type_enum(t)) {
|
||||
if (is_type_enum(t)) {
|
||||
return is_type_comparable(base_enum_type(t));
|
||||
}
|
||||
return false;
|
||||
} break;
|
||||
case Type_Array:
|
||||
return false;
|
||||
// return is_type_comparable(t->Array.elem);
|
||||
case Type_Vector:
|
||||
return is_type_comparable(t->Vector.elem);
|
||||
case Type_Proc:
|
||||
@@ -1189,6 +1195,9 @@ i64 align_formula(i64 size, i64 align) {
|
||||
}
|
||||
|
||||
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
if (t == NULL) {
|
||||
return 0;
|
||||
}
|
||||
i64 size;
|
||||
TypePath path = {0};
|
||||
type_path_init(&path);
|
||||
@@ -1198,6 +1207,9 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
}
|
||||
|
||||
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
if (t == NULL) {
|
||||
return 1;
|
||||
}
|
||||
i64 align;
|
||||
TypePath path = {0};
|
||||
type_path_init(&path);
|
||||
@@ -1214,6 +1226,17 @@ i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, Type
|
||||
t = base_type(t);
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Basic: {
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
switch (t->kind) {
|
||||
case Basic_string: return s.word_size;
|
||||
case Basic_any: return s.word_size;
|
||||
|
||||
case Basic_int: case Basic_uint: case Basic_rawptr:
|
||||
return s.word_size;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Type_Array: {
|
||||
Type *elem = t->Array.elem;
|
||||
type_path_push(path, elem);
|
||||
|
||||
Reference in New Issue
Block a user