aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp387
1 files changed, 263 insertions, 124 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 80a27bd79..b4836416e 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -1679,9 +1679,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
case Token_Not:
if (!is_type_boolean(type)) {
+ ERROR_BLOCK();
str = expr_to_string(o->expr);
- error(op, "Operator '%.*s' is only allowed on boolean expression", LIT(op.string));
+ error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
gb_string_free(str);
+ if (is_type_integer(type)) {
+ error_line("\tSuggestion: Did you mean to use the bitwise not operator '~'?\n");
+ }
}
break;
@@ -2019,6 +2023,47 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
}
+gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
+ if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
+ gbString b = type_to_string(type);
+
+ i64 sz = type_size_of(type);
+ BigInt *bi = &o->value.value_integer;
+ if (is_type_unsigned(type)) {
+ if (big_int_is_neg(bi)) {
+ error_line("\tA negative value cannot be represented by the unsigned integer type '%s'\n", b);
+ } else {
+ BigInt one = big_int_make_u64(1);
+ BigInt max_size = big_int_make_u64(1);
+ BigInt bits = big_int_make_i64(8*sz);
+ big_int_shl_eq(&max_size, &bits);
+ big_int_sub_eq(&max_size, &one);
+ String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ }
+ } else {
+ BigInt zero = big_int_make_u64(0);
+ BigInt one = big_int_make_u64(1);
+ BigInt max_size = big_int_make_u64(1);
+ BigInt bits = big_int_make_i64(8*sz - 1);
+ big_int_shl_eq(&max_size, &bits);
+ if (big_int_is_neg(bi)) {
+ big_int_neg(&max_size, &max_size);
+ String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ } else {
+ big_int_sub_eq(&max_size, &one);
+ String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ }
+ }
+
+ gb_string_free(b);
+
+ return true;
+ }
+ return false;
+}
gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
@@ -2050,6 +2095,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
error_line("\tSuggestion: the expression may be casted to %s\n", b);
+ } else if (check_integer_exceed_suggestion(c, o, type)) {
+ return;
}
}
@@ -2089,8 +2136,8 @@ gb_internal void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type
}
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
- } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
- error_line("\tSuggestion: the expression may be casted to %s\n", b);
+ } else if (check_integer_exceed_suggestion(c, o, type)) {
+ return;
}
}
@@ -2116,16 +2163,22 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
o->mode = Addressing_Invalid;
);
+ ERROR_BLOCK();
+
+
if (is_type_numeric(o->type) && is_type_numeric(type)) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
} else {
- ERROR_BLOCK();
- error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s);
+ if (are_types_identical(o->type, type)) {
+ error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b);
+ } else {
+ error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c);
+ }
+
check_assignment_error_suggestion(ctx, o, type);
}
} else {
- ERROR_BLOCK();
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
check_assignment_error_suggestion(ctx, o, type);
}
@@ -2344,7 +2397,7 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t
}
-gb_internal void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
+gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Operand *y, TokenKind op) {
if (x->mode == Addressing_Type && y->mode == Addressing_Type) {
bool comp = are_types_identical(x->type, y->type);
switch (op) {
@@ -2432,7 +2485,7 @@ gb_internal void check_comparison(CheckerContext *c, Operand *x, Operand *y, Tok
}
if (err_str != nullptr) {
- error(x->expr, "Cannot compare expression, %s", err_str);
+ error(node, "Cannot compare expression, %s", err_str);
x->type = t_untyped_bool;
} else {
if (x->mode == Addressing_Constant &&
@@ -2597,10 +2650,12 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
x->type = t_untyped_integer;
}
+ x->expr = node;
x->value = exact_value_shift(be->op.kind, x_val, y_val);
+
if (is_type_typed(x->type)) {
- check_is_expressible(c, x, base_type(x->type));
+ check_is_expressible(c, x, x->type);
}
return;
}
@@ -2915,17 +2970,38 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool can_convert = check_cast_internal(c, x, type);
if (!can_convert) {
- gbString expr_str = expr_to_string(x->expr);
- gbString to_type = type_to_string(type);
- gbString from_type = type_to_string(x->type);
+ TEMPORARY_ALLOCATOR_GUARD();
+ gbString expr_str = expr_to_string(x->expr, temporary_allocator());
+ gbString to_type = type_to_string(type, temporary_allocator());
+ gbString from_type = type_to_string(x->type, temporary_allocator());
+
+ x->mode = Addressing_Invalid;
+
+ begin_error_block();
error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type);
- gb_string_free(from_type);
- gb_string_free(to_type);
- gb_string_free(expr_str);
+ if (is_const_expr) {
+ gbString val_str = exact_value_to_string(x->value);
+ if (is_type_float(x->type) && is_type_integer(type)) {
+ error_line("\t%s cannot be represented without truncation/rounding as the type '%s'\n", val_str, to_type);
+
+ // NOTE(bill): keep the mode and modify the type to minimize errors further on
+ x->mode = Addressing_Constant;
+ x->type = type;
+ } else {
+ error_line("\t'%s' cannot be represented as the type '%s'\n", val_str, to_type);
+ if (is_type_numeric(type)) {
+ // NOTE(bill): keep the mode and modify the type to minimize errors further on
+ x->mode = Addressing_Constant;
+ x->type = type;
+ }
+ }
+ gb_string_free(val_str);
+ }
check_cast_error_suggestion(c, x, type);
- x->mode = Addressing_Invalid;
+ end_error_block();
+
return;
}
@@ -3422,7 +3498,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
if (token_is_comparison(op.kind)) {
- check_comparison(c, x, y, op.kind);
+ check_comparison(c, node, x, y, op.kind);
return;
}
@@ -4539,7 +4615,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
entity = scope_lookup_current(import_scope, entity_name);
bool allow_builtin = false;
if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
- error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
+ error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4559,7 +4635,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
if (!is_entity_exported(entity, allow_builtin)) {
gbString sel_str = expr_to_string(selector);
- error(op_expr, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
+ error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
gb_string_free(sel_str);
// NOTE(bill): make the state valid still, even if it's "invalid"
// operand->mode = Addressing_Invalid;
@@ -4730,20 +4806,29 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
gbString op_str = expr_to_string(op_expr);
gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
- error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
- if (operand->type != nullptr && selector->kind == Ast_Ident) {
- String const &name = selector->Ident.token.string;
- Type *bt = base_type(operand->type);
- if (operand->type->kind == Type_Named &&
- operand->type->Named.type_name &&
- operand->type->Named.type_name->kind == Entity_TypeName &&
- operand->type->Named.type_name->TypeName.objc_metadata) {
- check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
- } else if (bt->kind == Type_Struct) {
- check_did_you_mean_type(name, bt->Struct.fields);
- } else if (bt->kind == Type_Enum) {
- check_did_you_mean_type(name, bt->Enum.fields);
+ if (operand->mode == Addressing_Type) {
+ if (is_type_polymorphic(operand->type, true)) {
+ error(op_expr, "Type '%s' has no field nor polymorphic parameter '%s'", op_str, sel_str);
+ } else {
+ error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str);
+ }
+ } else {
+ error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
+
+ if (operand->type != nullptr && selector->kind == Ast_Ident) {
+ String const &name = selector->Ident.token.string;
+ Type *bt = base_type(operand->type);
+ if (operand->type->kind == Type_Named &&
+ operand->type->Named.type_name &&
+ operand->type->Named.type_name->kind == Entity_TypeName &&
+ operand->type->Named.type_name->TypeName.objc_metadata) {
+ check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
+ } else if (bt->kind == Type_Struct) {
+ check_did_you_mean_type(name, bt->Struct.fields);
+ } else if (bt->kind == Type_Enum) {
+ check_did_you_mean_type(name, bt->Enum.fields);
+ }
}
}
@@ -5418,7 +5503,18 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
data->score = score;
data->result_type = final_proc_type->Proc.results;
data->gen_entity = gen_entity;
- add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
+
+
+ Ast *proc_lit = nullptr;
+ if (ce->proc->tav.value.kind == ExactValue_Procedure) {
+ Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure);
+ if (vp && vp->kind == Ast_ProcLit) {
+ proc_lit = vp;
+ }
+ }
+ if (proc_lit == nullptr) {
+ add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
+ }
}
return err;
@@ -7033,7 +7129,7 @@ gb_internal bool ternary_compare_types(Type *x, Type *y) {
}
-gb_internal bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) {
+gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) {
if (!is_ast_range(node)) {
return false;
}
@@ -7082,9 +7178,17 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *
}
Type *type = x->type;
- if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
- error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
- return false;
+
+ if (is_for_loop) {
+ if (!is_type_integer(type) && !is_type_float(type) && !is_type_enum(type)) {
+ error(ie->op, "Only numerical types are allowed within interval expressions");
+ return false;
+ }
+ } else {
+ if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
+ error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
+ return false;
+ }
}
if (x->mode == Addressing_Constant &&
@@ -7790,6 +7894,124 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no
return Expr_Expr;
}
+
+gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<Ast *> const &elems, Operand *o, Type *type, bool &is_constant) {
+ Type *bt = base_type(type);
+
+ StringSet fields_visited = {};
+ defer (string_set_destroy(&fields_visited));
+
+ StringMap<String> fields_visited_through_raw_union = {};
+ defer (string_map_destroy(&fields_visited_through_raw_union));
+
+ for (Ast *elem : elems) {
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
+
+ Entity *field = bt->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
+ if (string_set_update(&fields_visited, name)) {
+ if (sel.index.count > 1) {
+ if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
+ error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ } else {
+ error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string));
+ }
+ } else {
+ error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
+ }
+ continue;
+ } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
+ error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ continue;
+ }
+ if (sel.indirect) {
+ error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name));
+ continue;
+ }
+
+ if (sel.index.count > 1) {
+ if (is_constant) {
+ Type *ft = type;
+ for (i32 index : sel.index) {
+ Type *bt = base_type(ft);
+ switch (bt->kind) {
+ case Type_Struct:
+ if (bt->Struct.is_raw_union) {
+ is_constant = false;
+ break;
+ }
+ ft = bt->Struct.fields[index]->type;
+ break;
+ case Type_Array:
+ ft = bt->Array.elem;
+ break;
+ default:
+ GB_PANIC("invalid type: %s", type_to_string(ft));
+ break;
+ }
+ }
+ if (is_constant &&
+ (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
+ is_constant = false;
+ }
+ }
+
+ Type *nested_ft = bt;
+ for (i32 index : sel.index) {
+ Type *bt = base_type(nested_ft);
+ switch (bt->kind) {
+ case Type_Struct:
+ if (bt->Struct.is_raw_union) {
+ for (Entity *re : bt->Struct.fields) {
+ string_map_set(&fields_visited_through_raw_union, re->token.string, sel.entity->token.string);
+ }
+ }
+ nested_ft = bt->Struct.fields[index]->type;
+ break;
+ case Type_Array:
+ nested_ft = bt->Array.elem;
+ break;
+ default:
+ GB_PANIC("invalid type %s", type_to_string(nested_ft));
+ break;
+ }
+ }
+ field = sel.entity;
+ }
+
+
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
+
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+}
+
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);
@@ -7877,45 +8099,13 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
gb_string_free(type_str);
} else {
- Ast *elem = cl->elems[0];
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- break;
- }
-
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- break;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- break;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
}
-
}
}
break;
}
-
isize field_count = t->Struct.fields.count;
isize min_field_count = t->Struct.fields.count;
for (isize i = min_field_count-1; i >= 0; i--) {
@@ -7929,58 +8119,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
if (cl->elems[0]->kind == Ast_FieldValue) {
- TEMPORARY_ALLOCATOR_GUARD();
-
- bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
-
- for (Ast *elem : cl->elems) {
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- continue;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- if (fields_visited[sel.index[0]]) {
- error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- fields_visited[sel.index[0]] = true;
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
} else {
bool seen_field_value = false;
@@ -8097,7 +8236,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
Operand x = {};
Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr);
+ bool ok = check_range(c, fv->field, false, &x, &y, nullptr);
if (!ok) {
continue;
}
@@ -8313,7 +8452,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
Operand x = {};
Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ bool ok = check_range(c, fv->field, false, &x, &y, nullptr, index_type);
if (!ok) {
continue;
}