aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2024-06-11 12:31:59 +0100
committergingerBill <bill@gingerbill.org>2024-06-11 12:31:59 +0100
commit5802f5221a2acbe03a7a7996549277b189f85a58 (patch)
tree33f28f25fc5c8b8c0c1894721413381f04fed4b3 /src/check_expr.cpp
parente5a816d5dcc3bf322981ebb0cc7f87fc5a0243d5 (diff)
parent1dc90103bd9e4c7783222d34cd16a2237a7dc377 (diff)
Merge branch 'master' into update-tilde
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp221
1 files changed, 165 insertions, 56 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index c143cfce0..01cba881e 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -102,6 +102,7 @@ gb_internal Type * check_init_variable (CheckerContext *c, Entity *
gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0);
gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
+gb_internal Type *make_soa_struct_fixed(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem, i64 count, Type *generic_type);
gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
@@ -124,6 +125,8 @@ gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types
gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finish);
+gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -278,8 +281,20 @@ gb_internal void error_operand_not_expression(Operand *o) {
gb_internal void error_operand_no_value(Operand *o) {
if (o->mode == Addressing_NoValue) {
- gbString err = expr_to_string(o->expr);
Ast *x = unparen_expr(o->expr);
+
+ if (x->kind == Ast_CallExpr) {
+ Ast *p = unparen_expr(x->CallExpr.proc);
+ if (p->kind == Ast_BasicDirective) {
+ String tag = p->BasicDirective.name.string;
+ if (tag == "panic" ||
+ tag == "assert") {
+ return;
+ }
+ }
+ }
+
+ gbString err = expr_to_string(o->expr);
if (x->kind == Ast_CallExpr) {
error(o->expr, "'%s' call does not return a value and cannot be used as a value", err);
} else {
@@ -563,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->defer_use_checked = false;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
+ entity->state.store(EntityState_Resolved);
entity->identifier = ident;
add_entity_and_decl_info(&nctx, ident, entity, d);
@@ -1178,15 +1194,59 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
LIT(context_name));
check_assignment_error_suggestion(c, operand, type);
+ Type *src = base_type(operand->type);
+ Type *dst = base_type(type);
if (context_name == "procedure argument") {
- Type *src = base_type(operand->type);
- Type *dst = base_type(type);
if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) {
gbString a = expr_to_string(operand->expr);
error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a);
gb_string_free(a);
}
}
+ if (src->kind == dst->kind && src->kind == Type_Proc) {
+ Type *x = src;
+ Type *y = dst;
+ bool same_inputs = are_types_identical_internal(x->Proc.params, y->Proc.params, false);
+ bool same_outputs = are_types_identical_internal(x->Proc.results, y->Proc.results, false);
+ if (same_inputs && same_outputs &&
+ x->Proc.calling_convention != y->Proc.calling_convention) {
+ gbString s_expected = type_to_string(y);
+ gbString s_got = type_to_string(x);
+
+ error_line("\tNote: The calling conventions differ between the procedure signature types\n");
+ error_line("\t Expected \"%s\", got \"%s\"\n",
+ proc_calling_convention_strings[y->Proc.calling_convention],
+ proc_calling_convention_strings[x->Proc.calling_convention]);
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else if (same_inputs && !same_outputs) {
+ gbString s_expected = type_to_string(y->Proc.results);
+ gbString s_got = type_to_string(x->Proc.results);
+ error_line("\tNote: The return types differ between the procedure signature types\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else if (!same_inputs && same_outputs) {
+ gbString s_expected = type_to_string(y->Proc.params);
+ gbString s_got = type_to_string(x->Proc.params);
+ error_line("\tNote: The input parameter types differ between the procedure signature types\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ } else {
+ gbString s_expected = type_to_string(y);
+ gbString s_got = type_to_string(x);
+ error_line("\tNote: The signature type do not match whatsoever\n");
+ error_line("\t Expected: %s\n", s_expected);
+ error_line("\t Got: %s\n", s_got);
+ gb_string_free(s_got);
+ gb_string_free(s_expected);
+ }
+ }
}
break;
}
@@ -1409,11 +1469,16 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
poly->Struct.soa_kind != StructSoa_None) {
bool ok = is_polymorphic_type_assignable(c, poly->Struct.soa_elem, source->Struct.soa_elem, true, modify_type);
if (ok) switch (source->Struct.soa_kind) {
- case StructSoa_Fixed:
+ case StructSoa_None:
default:
GB_PANIC("Unhandled SOA Kind");
break;
-
+ case StructSoa_Fixed:
+ if (modify_type) {
+ Type *type = make_soa_struct_fixed(c, nullptr, poly->Struct.node, poly->Struct.soa_elem, poly->Struct.soa_count, nullptr);
+ gb_memmove(poly, type, gb_size_of(*type));
+ }
+ break;
case StructSoa_Slice:
if (modify_type) {
Type *type = make_soa_struct_slice(c, nullptr, poly->Struct.node, poly->Struct.soa_elem);
@@ -1435,6 +1500,13 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
// return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
}
return false;
+
+ case Type_BitField:
+ if (source->kind == Type_BitField) {
+ return is_polymorphic_type_assignable(c, poly->BitField.backing_type, source->BitField.backing_type, true, modify_type);
+ }
+ return false;
+
case Type_Tuple:
GB_PANIC("This should never happen");
return false;
@@ -1748,7 +1820,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
case Entity_ImportName:
if (!allow_import_name) {
- error(n, "Use of import '%.*s' not in selector", LIT(name));
+ error(n, "Use of import name '%.*s' not in the form of 'x.y'", LIT(name));
}
return e;
case Entity_LibraryName:
@@ -1781,6 +1853,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
gb_string_free(str);
return false;
}
+ if (o->mode == Addressing_Type) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Expected an expression for operator '%.*s', got type '%s'", LIT(op.string), str);
+ gb_string_free(str);
+ return false;
+ }
+
Type *type = base_type(core_array_type(o->type));
gbString str = nullptr;
switch (op.kind) {
@@ -2188,6 +2267,17 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0) {
if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
gbString b = type_to_string(type);
+ defer (gb_string_free(b));
+
+ if (is_type_enum(o->type)) {
+ if (check_is_castable_to(c, o, type)) {
+ gbString ot = type_to_string(o->type);
+ error_line("\tSuggestion: Try casting the '%s' expression to '%s'", ot, b);
+ gb_string_free(ot);
+ }
+ return true;
+ }
+
i64 sz = type_size_of(type);
i64 bit_size = 8*sz;
@@ -2237,7 +2327,6 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o,
}
}
- gb_string_free(b);
return true;
}
@@ -2420,32 +2509,6 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
}
-gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
- Entity *e = entity_of_node(expr);
- if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) {
- GB_ASSERT(e->kind == Entity_Variable);
-
- ERROR_BLOCK();
-
- if ((e->flags & EntityFlag_ForValue) != 0) {
- Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
-
- error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed.");
-
- if (is_type_map(parent_type)) {
- error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string));
- } else {
- error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string));
- }
- } else {
- GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0);
-
- error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed.");
- error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
- }
- }
-}
-
gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
switch (op.kind) {
case Token_And: { // Pointer address
@@ -2473,7 +2536,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
{
ERROR_BLOCK();
error(op, "Cannot take the pointer address of '%s'", str);
- if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) {
+ if (e == nullptr) {
+ break;
+ }
+ if ((e->flags & EntityFlag_ForValue) != 0) {
Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
if (parent_type != nullptr && is_type_string(parent_type)) {
@@ -2483,9 +2549,17 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
} else {
error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n");
}
+
+ if (is_type_map(parent_type)) {
+ error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string));
+ } else {
+ error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string));
+ }
}
- if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) {
+ if ((e->flags & EntityFlag_SwitchValue) != 0) {
error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n");
+
+ error_line("\t Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
}
}
break;
@@ -2507,11 +2581,6 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
o->type = alloc_type_pointer(o->type);
}
} else {
- if (ast_node_expect(node, Ast_UnaryExpr)) {
- ast_node(ue, UnaryExpr, node);
- check_old_for_or_switch_value_usage(ue->expr);
- }
-
o->type = alloc_type_pointer(o->type);
}
@@ -3282,6 +3351,9 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
if (is_type_untyped(x->type)) {
Type *final_type = type;
if (is_const_expr && !is_type_constant_type(type)) {
+ if (is_type_union(type)) {
+ convert_to_typed(c, x, type);
+ }
final_type = default_type(x->type);
}
update_untyped_expr_type(c, x->expr, final_type, true);
@@ -4230,7 +4302,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
- if (!is_type_boolean(target_type)) {
+ if (!is_type_boolean(target_type) &&
+ !is_type_integer(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
return;
@@ -6187,6 +6260,20 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
}
return false;
}
+
+ if (ast_file_vet_style(ctx->file)) {
+ Ast *c = unparen_expr(clause);
+ if (c->kind == Ast_BinaryExpr && c->BinaryExpr.op.kind == Token_CmpAnd) {
+ ERROR_BLOCK();
+ error(c, "Prefer to separate 'where' clauses with a comma rather than '&&'");
+ gbString x = expr_to_string(c->BinaryExpr.left);
+ gbString y = expr_to_string(c->BinaryExpr.right);
+ error_line("\tSuggestion: '%s, %s'\n", x, y);
+ gb_string_free(y);
+ gb_string_free(x);
+ }
+ }
+
}
}
@@ -7261,14 +7348,9 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3);
s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string));
- Type *params = nullptr;
- switch (bt->kind) {
- case Type_Struct: params = bt->Struct.polymorphic_params; break;
- case Type_Union: params = bt->Union.polymorphic_params; break;
- }
-
- if (params != nullptr) for_array(i, params->Tuple.variables) {
- Entity *v = params->Tuple.variables[i];
+ TypeTuple *tuple = get_record_polymorphic_params(e->type);
+ if (tuple != nullptr) for_array(i, tuple->variables) {
+ Entity *v = tuple->variables[i];
String name = v->token.string;
if (i > 0) {
s = gb_string_append_fmt(s, ", ");
@@ -7350,13 +7432,15 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
String name = bd->name.string;
if (
name == "location" ||
+ name == "exists" ||
name == "assert" ||
name == "panic" ||
name == "defined" ||
name == "config" ||
name == "load" ||
name == "load_directory" ||
- name == "load_hash"
+ name == "load_hash" ||
+ name == "hash"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -7589,7 +7673,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (decl->proc_lit) {
ast_node(pl, ProcLit, decl->proc_lit);
if (pl->inlining == ProcInlining_no_inline) {
- error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'");
+ error(call, "'#force_inline' cannot be applied to a procedure that has been marked as '#force_no_inline'");
}
}
}
@@ -7741,13 +7825,18 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
return true;
case Type_Matrix:
- *max_count = t->Matrix.column_count;
if (indirection) {
o->mode = Addressing_Variable;
} else if (o->mode != Addressing_Variable) {
o->mode = Addressing_Value;
}
- o->type = alloc_type_array(t->Matrix.elem, t->Matrix.row_count);
+ if (t->Matrix.is_row_major) {
+ *max_count = t->Matrix.row_count;
+ o->type = alloc_type_array(t->Matrix.elem, t->Matrix.column_count);
+ } else {
+ *max_count = t->Matrix.column_count;
+ o->type = alloc_type_array(t->Matrix.elem, t->Matrix.row_count);
+ }
return true;
case Type_Slice:
@@ -7792,8 +7881,8 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
if (is_type_pointer(original_type) && indirection) {
Type *ptr = base_type(original_type);
- if (ptr->kind == Type_Pointer && o->mode == Addressing_SoaVariable) {
- o->type = ptr->Pointer.elem;
+ if (ptr->kind == Type_MultiPointer && o->mode == Addressing_SoaVariable) {
+ o->type = ptr->MultiPointer.elem;
o->mode = Addressing_Value;
return true;
}
@@ -8265,6 +8354,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
name == "assert" ||
name == "defined" ||
name == "config" ||
+ name == "exists" ||
name == "load" ||
name == "load_hash" ||
name == "load_directory" ||
@@ -8788,6 +8878,10 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
case Type_Array:
ft = bt->Array.elem;
break;
+ case Type_BitField:
+ is_constant = false;
+ ft = bt->BitField.fields[index]->type;
+ break;
default:
GB_PANIC("invalid type: %s", type_to_string(ft));
break;
@@ -8814,6 +8908,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
case Type_Array:
nested_ft = bt->Array.elem;
break;
+ case Type_BitField:
+ nested_ft = bt->BitField.fields[index]->type;
+ break;
default:
GB_PANIC("invalid type %s", type_to_string(nested_ft));
break;
@@ -10143,7 +10240,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
- } else if (ok) {
+ } else if (ok && !is_type_matrix(t)) {
ExactValue value = type_and_value_of_expr(ie->expr).value;
o->mode = Addressing_Constant;
bool success = false;
@@ -10229,6 +10326,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
case Type_Struct:
if (is_type_soa_struct(t)) {
valid = true;
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ max_count = t->Struct.soa_count;
+ if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot slice #soa array '%s', value is not addressable", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
}
break;
@@ -10768,6 +10876,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return Expr_Expr;
case_end;
+ case Ast_DistinctType:
case Ast_TypeidType:
case Ast_PolyType:
case Ast_ProcType: