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.cpp1809
1 files changed, 1431 insertions, 378 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 5cc548739..231ece2f4 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -79,7 +79,6 @@ gb_internal Type * check_type_expr (CheckerContext *c, Ast *exp
gb_internal Type * make_optional_ok_type (Type *value, bool typed=true);
gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
-gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
gb_internal void check_not_tuple (CheckerContext *c, Operand *operand);
gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
gb_internal gbString expr_to_string (Ast *expression);
@@ -100,15 +99,16 @@ gb_internal void check_union_type (CheckerContext *c, Type *un
gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
-gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
+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);
gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint);
-gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand=true);
gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type);
gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
@@ -119,6 +119,16 @@ gb_internal bool is_diverging_expr(Ast *expr);
gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_);
+gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr);
+
+gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands);
+
+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);
+
+gb_internal bool is_exact_value_zero(ExactValue const &v);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -184,6 +194,8 @@ gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity
gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
GB_ASSERT(e->kind == Entity_TypeName);
GB_ASSERT(e->TypeName.objc_metadata != nullptr);
@@ -204,6 +216,8 @@ gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, b
}
gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
@@ -217,6 +231,8 @@ gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> con
gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
@@ -229,6 +245,8 @@ gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> con
}
gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
+ if (build_context.terse_errors) { return; }
+
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.count, name);
@@ -265,9 +283,21 @@ 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) {
+
+ if (x != nullptr && 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 != nullptr && x->kind == Ast_CallExpr) {
error(o->expr, "'%s' call does not return a value and cannot be used as a value", err);
} else {
error(o->expr, "'%s' used as a value", err);
@@ -472,7 +502,9 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
nctx.no_polymorphic_errors = false;
// NOTE(bill): Reset scope from the failed procedure type
- scope_reset(scope);
+ scope->head_child.store(nullptr, std::memory_order_relaxed);
+ string_map_clear(&scope->elements);
+ ptr_set_clear(&scope->imported);
// LEAK NOTE(bill): Cloning this AST may be leaky but this is not really an issue due to arena-based allocation
Ast *cloned_proc_type_node = clone_ast(pt->node);
@@ -520,13 +552,15 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
final_proc_type->Proc.is_poly_specialized = true;
final_proc_type->Proc.is_polymorphic = true;
- final_proc_type->Proc.variadic = src->Proc.variadic;
- final_proc_type->Proc.require_results = src->Proc.require_results;
- final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
- final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
- final_proc_type->Proc.diverging = src->Proc.diverging;
- final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
- final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
+ final_proc_type->Proc.variadic = src->Proc.variadic;
+ final_proc_type->Proc.require_results = src->Proc.require_results;
+ final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
+ final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
+ final_proc_type->Proc.diverging = src->Proc.diverging;
+ final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
+ final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
+ final_proc_type->Proc.enable_target_feature = src->Proc.enable_target_feature;
+ final_proc_type->Proc.require_target_feature = src->Proc.require_target_feature;
for (isize i = 0; i < operands.count; i++) {
@@ -550,6 +584,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);
@@ -558,6 +593,16 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
entity->file = base_entity->file;
entity->pkg = base_entity->pkg;
entity->flags = 0;
+
+ entity->Procedure.optimization_mode = base_entity->Procedure.optimization_mode;
+
+ if (base_entity->flags & EntityFlag_Cold) {
+ entity->flags |= EntityFlag_Cold;
+ }
+ if (base_entity->flags & EntityFlag_Disabled) {
+ entity->flags |= EntityFlag_Disabled;
+ }
+
d->entity = entity;
AstFile *file = nullptr;
@@ -613,7 +658,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
-gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) {
if (c == nullptr) {
GB_ASSERT(operand->mode == Addressing_Value);
GB_ASSERT(is_type_typed(operand->type));
@@ -822,7 +867,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (dst->Union.variants.count == 1) {
Type *vt = dst->Union.variants[0];
- i64 score = check_distance_between_types(c, operand, vt);
+ i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
if (score >= 0) {
return score+2;
}
@@ -830,7 +875,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
i64 prev_lowest_score = -1;
i64 lowest_score = -1;
for (Type *vt : dst->Union.variants) {
- i64 score = check_distance_between_types(c, operand, vt);
+ i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
if (score >= 0) {
if (lowest_score < 0) {
lowest_score = score;
@@ -852,20 +897,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_relative_pointer(dst)) {
- i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type);
- if (score >= 0) {
- return score+2;
- }
- }
-
- if (is_type_relative_multi_pointer(dst)) {
- i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
- if (score >= 0) {
- return score+2;
- }
- }
-
if (is_type_proc(dst)) {
if (are_types_identical(src, dst)) {
return 3;
@@ -886,19 +917,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
- if (is_type_array(dst)) {
- Type *elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, elem);
- if (distance >= 0) {
- return distance + 6;
+ if (allow_array_programming) {
+ if (is_type_array(dst)) {
+ Type *elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming);
+ if (distance >= 0) {
+ return distance + 6;
+ }
}
- }
- if (is_type_simd_vector(dst)) {
- Type *dst_elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, dst_elem);
- if (distance >= 0) {
- return distance + 6;
+ if (is_type_simd_vector(dst)) {
+ Type *dst_elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
+ if (distance >= 0) {
+ return distance + 6;
+ }
}
}
@@ -908,7 +941,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
if (dst->Matrix.row_count == dst->Matrix.column_count) {
Type *dst_elem = base_array_type(dst);
- i64 distance = check_distance_between_types(c, operand, dst_elem);
+ i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
if (distance >= 0) {
return distance + 7;
}
@@ -956,9 +989,9 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
}
-gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
+gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
i64 score = 0;
- i64 distance = check_distance_between_types(c, operand, type);
+ i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
bool ok = distance >= 0;
if (ok) {
score = assign_score_function(distance, is_variadic);
@@ -968,9 +1001,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o
}
-gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) {
i64 score = 0;
- return check_is_assignable_to_with_score(c, operand, type, &score);
+ return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming);
}
gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) {
@@ -1005,12 +1038,6 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
case Type_DynamicArray:
type = type->DynamicArray.elem;
continue;
- case Type_RelativePointer:
- type = type->RelativePointer.pointer_type;
- continue;
- case Type_RelativeMultiPointer:
- type = type->RelativeMultiPointer.pointer_type;
- continue;
}
return nullptr;
}
@@ -1024,16 +1051,19 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
return;
}
+ // Grab definite or indefinite article matching `context_name`, or "" if not found.
+ String article = error_article(context_name);
+
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == nullptr || is_type_any(type)) {
if (type == nullptr && is_type_untyped_uninit(operand->type)) {
- error(operand->expr, "Use of --- in %.*s", LIT(context_name));
+ error(operand->expr, "Use of --- in %.*s%.*s", LIT(article), LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
if (type == nullptr && is_type_untyped_nil(operand->type)) {
- error(operand->expr, "Use of untyped nil in %.*s", LIT(context_name));
+ error(operand->expr, "Use of untyped nil in %.*s%.*s", LIT(article), LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
@@ -1088,9 +1118,10 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
// TODO(bill): is this a good enough error message?
error(operand->expr,
- "Cannot assign overloaded procedure group '%s' to '%s' in %.*s",
+ "Cannot assign overloaded procedure group '%s' to '%s' in %.*s%.*s",
expr_str,
op_type_str,
+ LIT(article),
LIT(context_name));
operand->mode = Addressing_Invalid;
}
@@ -1116,21 +1147,28 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
switch (operand->mode) {
case Addressing_Builtin:
error(operand->expr,
- "Cannot assign built-in procedure '%s' in %.*s",
+ "Cannot assign built-in procedure '%s' to %.*s%.*s",
expr_str,
+ LIT(article),
LIT(context_name));
break;
case Addressing_Type:
if (is_type_polymorphic(operand->type)) {
error(operand->expr,
- "Cannot assign '%s' which is a polymorphic type in %.*s",
+ "Cannot assign '%s', a polymorphic type, to %.*s%.*s",
op_type_str,
+ LIT(article),
LIT(context_name));
} else {
+ ERROR_BLOCK();
error(operand->expr,
- "Cannot assign '%s' which is a type in %.*s",
+ "Cannot assign '%s', a type, to %.*s%.*s",
op_type_str,
+ LIT(article),
LIT(context_name));
+ if (type && are_types_identical(type, t_any)) {
+ error_line("\tSuggestion: 'typeid_of(%s)'", expr_str);
+ }
}
break;
default:
@@ -1156,12 +1194,85 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
ERROR_BLOCK();
error(operand->expr,
- "Cannot assign value '%s' of type '%s%s' to '%s%s' in %.*s",
+ "Cannot assign value '%s' of type '%s%s' to '%s%s' in %.*s%.*s",
expr_str,
op_type_str, op_type_extra,
type_str, type_extra,
+ LIT(article),
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") {
+ 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 &&
+ x->Proc.diverging != y->Proc.diverging) {
+
+ gbString s_expected = type_to_string(y);
+ if (y->Proc.diverging) {
+ s_expected = gb_string_appendc(s_expected, " -> !");
+ }
+
+ gbString s_got = type_to_string(x);
+ if (x->Proc.diverging) {
+ s_got = gb_string_appendc(s_got, " -> !");
+ }
+
+ error_line("\tNote: One of the procedures is diverging while the other isn't\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.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;
}
@@ -1233,7 +1344,7 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
}
case Type_Pointer:
if (source->kind == Type_Pointer) {
- isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem);
+ isize level = check_is_assignable_to_using_subtype(source->Pointer.elem, poly->Pointer.elem, /*level*/0, /*src_is_ptr*/false, /*allow_polymorphic*/true);
if (level > 0) {
return true;
}
@@ -1350,6 +1461,16 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
if (!is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type)) {
return false;
}
+
+ // For generic types like bit_set[$T] the upper and lower of the poly type will be zeroes since
+ // it could not figure that stuff out when the poly type was created.
+ if (poly->BitSet.upper == 0 && modify_type) {
+ poly->BitSet.upper = source->BitSet.upper;
+ }
+ if (poly->BitSet.lower == 0 && modify_type) {
+ poly->BitSet.lower = source->BitSet.lower;
+ }
+
if (poly->BitSet.underlying == nullptr) {
if (modify_type) {
poly->BitSet.underlying = source->BitSet.underlying;
@@ -1384,11 +1505,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);
@@ -1405,9 +1531,18 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
return ok;
}
- // return check_is_assignable_to(c, &o, poly);
+
+ // NOTE(bill): Check for subtypes of
+ // 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;
@@ -1518,6 +1653,55 @@ gb_internal bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
+struct CIdentSuggestion {
+ String name;
+ String msg;
+};
+
+// NOTE(bill): this linear look-up table might be slow but because it's an error case, it should be fine
+gb_internal CIdentSuggestion const c_ident_suggestions[] = {
+ {str_lit("while"), str_lit("'for'? Odin only has one loop construct: 'for'")},
+
+ {str_lit("sizeof"), str_lit("'size_of'?")},
+ {str_lit("alignof"), str_lit("'align_of'?")},
+ {str_lit("offsetof"), str_lit("'offset_of'?")},
+
+ {str_lit("_Bool"), str_lit("'bool'?")},
+
+ {str_lit("char"), str_lit("'u8', 'i8', or 'c.char' (which is part of 'core:c')?")},
+ {str_lit("short"), str_lit("'i16' or 'c.short' (which is part of 'core:c')?")},
+ {str_lit("long"), str_lit("'c.long' (which is part of 'core:c')?")},
+ {str_lit("float"), str_lit("'f32'?")},
+ {str_lit("double"), str_lit("'f64'?")},
+ {str_lit("unsigned"), str_lit("'c.uint' (which is part of 'core:c')?")},
+ {str_lit("signed"), str_lit("'c.int' (which is part of 'core:c')?")},
+
+ {str_lit("size_t"), str_lit("'uint', or 'c.size_t' (which is part of 'core:c')?")},
+ {str_lit("ssize_t"), str_lit("'int', or 'c.ssize_t' (which is part of 'core:c')?")},
+
+ {str_lit("uintptr_t"), str_lit("'uintptr'?")},
+ {str_lit("intptr_t"), str_lit("'uintptr' or `int` or something else?")},
+ {str_lit("ptrdiff_t"), str_lit("'int' or 'c.ptrdiff_t' (which is part of 'core:c')?")},
+ {str_lit("intmax_t"), str_lit("'c.intmax_t' (which is part of 'core:c')?")},
+ {str_lit("uintmax_t"), str_lit("'c.uintmax_t' (which is part of 'core:c')?")},
+
+ {str_lit("uint8_t"), str_lit("'u8'?")},
+ {str_lit("int8_t"), str_lit("'i8'?")},
+ {str_lit("uint16_t"), str_lit("'u16'?")},
+ {str_lit("int16_t"), str_lit("'i16'?")},
+ {str_lit("uint32_t"), str_lit("'u32'?")},
+ {str_lit("int32_t"), str_lit("'i32'?")},
+ {str_lit("uint64_t"), str_lit("'u64'?")},
+ {str_lit("int64_t"), str_lit("'i64'?")},
+ {str_lit("uint128_t"), str_lit("'u128'?")},
+ {str_lit("int128_t"), str_lit("'i128'?")},
+
+ {str_lit("float32"), str_lit("'f32'?")},
+ {str_lit("float64"), str_lit("'f64'?")},
+ {str_lit("float32_t"), str_lit("'f32'?")},
+ {str_lit("float64_t"), str_lit("'f64'?")},
+};
+
gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
GB_ASSERT(n->kind == Ast_Ident);
o->mode = Addressing_Invalid;
@@ -1529,7 +1713,16 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value");
} else {
+ ERROR_BLOCK();
error(n, "Undeclared name: %.*s", LIT(name));
+
+ // NOTE(bill): Loads of checks for C programmers
+
+ for (CIdentSuggestion const &suggestion : c_ident_suggestions) {
+ if (name == suggestion.name) {
+ error_line("\tSuggestion: Did you mean %.*s\n", LIT(suggestion.msg));
+ }
+ }
}
o->type = t_invalid;
o->mode = Addressing_Invalid;
@@ -1655,7 +1848,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
if (check_cycle(c, e, true)) {
o->type = t_invalid;
}
- if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
o->type = base_type(o->type);
}
@@ -1663,7 +1856,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:
@@ -1696,6 +1889,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) {
@@ -1717,17 +1917,34 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
case Token_Not:
if (!is_type_boolean(type) || is_type_array_like(o->type)) {
ERROR_BLOCK();
- str = expr_to_string(o->expr);
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");
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean to do one of the following?\n");
+ error_line("\t\t'%s == 0'?\n", str);
+ error_line("\t\tUse of the bitwise not operator '~'?\n");
+ gb_string_free(str);
}
} else {
o->type = t_untyped_bool;
}
break;
+ case Token_Mul:
+ {
+ ERROR_BLOCK();
+ error(op, "Operator '%.*s' is not a valid unary operator in Odin", LIT(op.string));
+ if (is_type_pointer(o->type)) {
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean '%s^'?\n", str);
+ o->type = type_deref(o->type);
+ } else if (is_type_multi_pointer(o->type)) {
+ str = expr_to_string(o->expr);
+ error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str);
+ o->type = type_deref(o->type, true);
+ }
+ }
+ break;
default:
error(op, "Unknown operator '%.*s'", LIT(op.string));
return false;
@@ -1876,33 +2093,55 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
BigInt i = v.value_integer;
- i64 bit_size = type_size_of(type);
+ i64 byte_size = type_size_of(type);
BigInt umax = {};
BigInt imin = {};
BigInt imax = {};
- if (bit_size < 16) {
- big_int_from_u64(&umax, unsigned_integer_maxs[bit_size]);
- big_int_from_i64(&imin, signed_integer_mins[bit_size]);
- big_int_from_i64(&imax, signed_integer_maxs[bit_size]);
- } else {
+ if (c->bit_field_bit_size > 0) {
+ i64 bit_size = gb_min(cast(i64)(8*byte_size), cast(i64)c->bit_field_bit_size);
+
big_int_from_u64(&umax, 1);
big_int_from_i64(&imin, 1);
big_int_from_i64(&imax, 1);
- BigInt bi128 = {};
- BigInt bi127 = {};
- big_int_from_i64(&bi128, 128);
- big_int_from_i64(&bi127, 127);
+ BigInt bu = {};
+ BigInt bi = {};
+ big_int_from_i64(&bu, bit_size);
+ big_int_from_i64(&bi, bit_size-1);
- big_int_shl_eq(&umax, &bi128);
+ big_int_shl_eq(&umax, &bu);
mp_decr(&umax);
- big_int_shl_eq(&imin, &bi127);
+ big_int_shl_eq(&imin, &bi);
big_int_neg(&imin, &imin);
- big_int_shl_eq(&imax, &bi127);
+ big_int_shl_eq(&imax, &bi);
mp_decr(&imax);
+ } else {
+ if (byte_size < 16) {
+ big_int_from_u64(&umax, unsigned_integer_maxs[byte_size]);
+ big_int_from_i64(&imin, signed_integer_mins[byte_size]);
+ big_int_from_i64(&imax, signed_integer_maxs[byte_size]);
+ } else {
+ big_int_from_u64(&umax, 1);
+ big_int_from_i64(&imin, 1);
+ big_int_from_i64(&imax, 1);
+
+ BigInt bi128 = {};
+ BigInt bi127 = {};
+ big_int_from_i64(&bi128, 128);
+ big_int_from_i64(&bi127, 127);
+
+ big_int_shl_eq(&umax, &bi128);
+ mp_decr(&umax);
+
+ big_int_shl_eq(&imin, &bi127);
+ big_int_neg(&imin, &imin);
+
+ big_int_shl_eq(&imax, &bi127);
+ mp_decr(&imax);
+ }
}
switch (type->Basic.kind) {
@@ -2061,48 +2300,85 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
}
-gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
+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;
+ bool size_changed = false;
+ if (max_bit_size > 0) {
+ size_changed = (bit_size != max_bit_size);
+ bit_size = gb_min(bit_size, max_bit_size);
+ }
BigInt *bi = &o->value.value_integer;
if (is_type_unsigned(type)) {
+ BigInt one = big_int_make_u64(1);
+ BigInt max_size = big_int_make_u64(1);
+ BigInt bits = big_int_make_i64(bit_size);
+ big_int_shl_eq(&max_size, &bits);
+ big_int_sub_eq(&max_size, &one);
+
if (big_int_is_neg(bi)) {
error_line("\tA negative value cannot be represented by the unsigned integer type '%s'\n", b);
+ BigInt dst = {};
+ big_int_neg(&dst, bi);
+ if (big_int_cmp(&dst, &max_size) < 0) {
+ big_int_sub_eq(&dst, &one);
+ String dst_str = big_int_to_string(temporary_allocator(), &dst);
+ gbString t = type_to_string(type);
+ error_line("\tSuggestion: ~%s(%.*s)\n", t, LIT(dst_str));
+ gb_string_free(t);
+ }
} 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));
+
+ if (size_changed) {
+ error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+ } else {
+ 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);
+ BigInt bits = big_int_make_i64(bit_size - 1);
big_int_shl_eq(&max_size, &bits);
+
+ String max_size_str = {};
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));
+ max_size_str = big_int_to_string(temporary_allocator(), &max_size);
} else {
big_int_sub_eq(&max_size, &one);
- String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ }
+
+ if (size_changed) {
+ error_line("\tThe maximum value that can be represented with that bit_field's field of '%s | %u' is '%.*s'\n", b, bit_size, LIT(max_size_str));
+ } else {
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) {
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
defer(
@@ -2133,8 +2409,23 @@ 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)) {
+ } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) {
return;
+ } else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) {
+ gbString s = expr_to_string(c->type_hint_expr);
+ error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s);
+ gb_string_free(s);
+ } else if (is_type_pointer(type) &&
+ o->mode == Addressing_Variable &&
+ are_types_identical(type_deref(type), o->type)) {
+ gbString s = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean `&%s`\n", s);
+ gb_string_free(s);
+ } else if (is_type_pointer(o->type) &&
+ are_types_identical(type_deref(o->type), type)) {
+ gbString s = expr_to_string(o->expr);
+ error_line("\tSuggestion: Did you mean `%s^`\n", s);
+ gb_string_free(s);
}
}
@@ -2203,18 +2494,22 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
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 {
+ i64 max_bit_size = 0;
+ if (ctx->bit_field_bit_size) {
+ max_bit_size = ctx->bit_field_bit_size;
+ }
+
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);
+ check_assignment_error_suggestion(ctx, o, type, max_bit_size);
}
} else {
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
@@ -2225,6 +2520,11 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
}
gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
+ if (o->expr && o->expr->kind == Ast_SelectorExpr) {
+ if (o->expr->SelectorExpr.is_bit_field) {
+ return true;
+ }
+ }
if (o->mode == Addressing_OptionalOk) {
Ast *expr = unselector_expr(o->expr);
if (expr->kind != Ast_TypeAssertion) {
@@ -2255,35 +2555,82 @@ 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) {
- if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) {
- return;
- }
+gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
+ type = base_type(type);
+ GB_ASSERT(type->kind == Type_BitSet);
- Entity *e = entity_of_node(expr);
- if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) {
- GB_ASSERT(e->kind == Entity_Variable);
+ i64 lower = type->BitSet.lower;
+ i64 upper = type->BitSet.upper;
+ Type *elem = type->BitSet.elem;
+ Type *underlying = type->BitSet.underlying;
+ bool is_backed = underlying != nullptr;
+ gb_unused(is_backed);
- begin_error_block();
- defer (end_error_block());
+ BigInt b_lower = {};
+ BigInt b_upper = {};
+ big_int_from_i64(&b_lower, lower);
+ big_int_from_i64(&b_upper, upper);
- 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 with '-strict-style'.");
+ BigInt one = {};
+ big_int_from_u64(&one, 1);
- 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));
+ BigInt mask = {};
+
+ if (elem == nullptr) {
+ big_int_from_i64(&mask, -1);
+ } else if (is_type_enum(elem)) {
+ Type *e = base_type(elem);
+ GB_ASSERT(e->kind == Type_Enum);
+ gb_unused(e);
+
+ if ((big_int_cmp(&e->Enum.min_value->value_integer, &b_lower) == 0 || is_backed) &&
+ big_int_cmp(&e->Enum.max_value->value_integer, &b_upper) == 0) {
+
+ i64 lower_base = is_backed ? gb_min(0, lower) : lower;
+ BigInt b_lower_base = {};
+ big_int_from_i64(&b_lower_base, lower_base);
+
+ for (Entity *f : e->Enum.fields) {
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (f->Constant.value.kind != ExactValue_Integer) {
+ continue;
+ }
+
+ BigInt shift_amount = f->Constant.value.value_integer;
+ big_int_sub_eq(&shift_amount, &b_lower_base);
+
+
+ BigInt value = {};
+ big_int_shl(&value, &one, &shift_amount);
+
+ big_int_or(&mask, &mask, &value);
}
+
} else {
- GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0);
+ // TODO(bill): enum range based");
+ big_int_from_i64(&mask, -1);
+ }
+ } else {
+ i64 lower_base = lower;
+ for (i64 x = lower; x <= upper; x++) {
+ BigInt shift_amount = {};
+ big_int_from_i64(&shift_amount, x - lower_base);
- error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
- error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
+ BigInt value = {};
+ big_int_shl(&value, &one, &shift_amount);
+
+ big_int_or(&mask, &mask, &value);
}
}
+
+
+ ExactValue res = {};
+ res.kind = ExactValue_Integer;
+ res.value_integer = mask;
+ return res;
}
gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
@@ -2298,6 +2645,8 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
Entity *e = entity_of_node(ue->expr);
if (e != nullptr && (e->flags & EntityFlag_Param) != 0) {
error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
+ } else if (e != nullptr && (e->flags & EntityFlag_BitFieldField) != 0) {
+ error(op, "Cannot take the pointer address of '%s' which is a bit_field's field", str);
} else {
switch (o->mode) {
case Addressing_Constant:
@@ -2309,10 +2658,12 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
break;
default:
{
- begin_error_block();
- defer (end_error_block());
+ 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)) {
@@ -2322,9 +2673,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 (parent_type != nullptr && 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;
@@ -2339,18 +2698,13 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
ast_node(ue, UnaryExpr, node);
if (ast_node_expect(ue->expr, Ast_IndexExpr)) {
ast_node(ie, IndexExpr, ue->expr);
- Type *soa_type = type_of_expr(ie->expr);
+ Type *soa_type = type_deref(type_of_expr(ie->expr));
GB_ASSERT(is_type_soa_struct(soa_type));
o->type = alloc_type_soa_pointer(soa_type);
} else {
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);
}
@@ -2376,6 +2730,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
if (o->mode == Addressing_Constant) {
Type *type = base_type(o->type);
if (!is_type_constant_type(o->type)) {
+ if (is_type_array_like(o->type)) {
+ o->mode = Addressing_Value;
+ return;
+ }
+
gbString xt = type_to_string(o->type);
gbString err_str = expr_to_string(node);
error(op, "Invalid type, '%s', for constant unary expression '%s'", xt, err_str);
@@ -2413,6 +2772,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
}
o->value = exact_unary_operator_value(op.kind, o->value, precision, is_unsigned);
+ if (op.kind == Token_Xor && is_type_bit_set(type)) {
+ ExactValue mask = exact_bit_set_all_set_mask(type);
+ o->value = exact_binary_operator_value(Token_And, o->value, mask);
+ }
if (is_type_typed(type)) {
if (node != nullptr) {
@@ -2759,7 +3122,7 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
x->mode = Addressing_Value;
if (type_hint) {
if (is_type_integer(type_hint)) {
- x->type = type_hint;
+ convert_to_typed(c, x, type_hint);
} else {
gbString x_str = expr_to_string(x->expr);
gbString to_type = type_to_string(type_hint);
@@ -2872,6 +3235,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
}
+ if (is_type_bit_field(src)) {
+ return are_types_identical(core_type(src->BitField.backing_type), dst);
+ }
+ if (is_type_bit_field(dst)) {
+ return are_types_identical(src, core_type(dst->BitField.backing_type));
+ }
+
if (is_type_integer(src) && is_type_rune(dst)) {
return true;
}
@@ -2983,6 +3353,13 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
}
// proc <-> proc
if (is_type_proc(src) && is_type_proc(dst)) {
+ if (is_type_polymorphic(dst)) {
+ if (is_type_polymorphic(src) &&
+ operand->mode == Addressing_Variable) {
+ return true;
+ }
+ return false;
+ }
return true;
}
@@ -2995,6 +3372,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
return true;
}
+
+ if (is_type_array(dst)) {
+ Type *elem = base_array_type(dst);
+ if (check_is_castable_to(c, operand, elem)) {
+ return true;
+ }
+ }
+
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
if (src->SimdVector.count != dst->SimdVector.count) {
return false;
@@ -3053,7 +3438,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
}
-gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
+gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type, bool forbid_identical = false) {
if (!is_operand_value(*x)) {
error(x->expr, "Only values can be casted");
x->mode = Addressing_Invalid;
@@ -3062,7 +3447,6 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
bool can_convert = check_cast_internal(c, x, type);
-
if (!can_convert) {
TEMPORARY_ALLOCATOR_GUARD();
gbString expr_str = expr_to_string(x->expr, temporary_allocator());
@@ -3071,7 +3455,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
x->mode = Addressing_Invalid;
- begin_error_block();
+ ERROR_BLOCK();
error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type);
if (is_const_expr) {
gbString val_str = exact_value_to_string(x->value);
@@ -3094,37 +3478,70 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
}
check_cast_error_suggestion(c, x, type);
- end_error_block();
-
return;
}
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);
+ } else {
+ Type *src = core_type(x->type);
+ Type *dst = core_type(type);
+ if (src != dst) {
+ bool const REQUIRE = true;
+ if (is_type_integer_128bit(src) && is_type_float(dst)) {
+ add_package_dependency(c, "runtime", "floattidf_unsigned", REQUIRE);
+ add_package_dependency(c, "runtime", "floattidf", REQUIRE);
+ } else if (is_type_integer_128bit(dst) && is_type_float(src)) {
+ add_package_dependency(c, "runtime", "fixunsdfti", REQUIRE);
+ add_package_dependency(c, "runtime", "fixunsdfdi", REQUIRE);
+ } else if (src == t_f16 && is_type_float(dst)) {
+ add_package_dependency(c, "runtime", "gnu_h2f_ieee", REQUIRE);
+ add_package_dependency(c, "runtime", "extendhfsf2", REQUIRE);
+ } else if (is_type_float(dst) && dst == t_f16) {
+ add_package_dependency(c, "runtime", "truncsfhf2", REQUIRE);
+ add_package_dependency(c, "runtime", "truncdfhf2", REQUIRE);
+ add_package_dependency(c, "runtime", "gnu_f2h_ieee", REQUIRE);
+ }
+ }
+ // If we check polymorphic procedures, we risk erring on
+ // identical casts that cannot be foreseen or otherwise
+ // forbidden, so just skip them.
+ if (forbid_identical && check_vet_flags(c) & VetFlag_Cast &&
+ (c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig))) {
+ Type *src_exact = x->type;
+ Type *dst_exact = type;
+
+ if (src_exact != nullptr &&
+ dst_exact != nullptr &&
+ are_types_identical(src_exact, dst_exact)
+ ) {
+ gbString oper_str = expr_to_string(x->expr);
+ gbString to_type = type_to_string(dst_exact);
+ error(x->expr, "Unneeded cast of '%s' to identical type '%s'", oper_str, to_type);
+ gb_string_free(oper_str);
+ gb_string_free(to_type);
+ }
+ }
}
x->type = type;
}
-gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
+gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t, bool forbid_identical = false) {
if (!is_operand_value(*o)) {
error(o->expr, "'transmute' can only be applied to values");
o->mode = Addressing_Invalid;
return false;
}
- // if (o->mode == Addressing_Constant) {
- // gbString expr_str = expr_to_string(o->expr);
- // error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str);
- // gb_string_free(expr_str);
- // o->mode = Addressing_Invalid;
- // o->expr = node;
- // return false;
- // }
+ Operand src = *o;
Type *src_t = o->type;
Type *dst_t = t;
@@ -3177,10 +3594,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
if (are_types_identical(src_bt, dst_bt)) {
return true;
}
- if (is_type_integer(src_t) && is_type_integer(dst_t)) {
+ if ((is_type_integer(src_t) && is_type_integer(dst_t)) ||
+ is_type_integer(src_t) && is_type_bit_set(dst_t)) {
if (types_have_same_internal_endian(src_t, dst_t)) {
ExactValue src_v = exact_value_to_integer(o->value);
- GB_ASSERT(src_v.kind == ExactValue_Integer);
+ GB_ASSERT(src_v.kind == ExactValue_Integer || src_v.kind == ExactValue_Invalid);
BigInt v = src_v.value_integer;
BigInt smax = {};
@@ -3208,6 +3626,37 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
return true;
}
}
+ } else {
+ // If we check polymorphic procedures, we risk erring on
+ // identical casts that cannot be foreseen or otherwise
+ // forbidden, so just skip them.
+ if (forbid_identical && check_vet_flags(c) & VetFlag_Cast &&
+ (c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig)) &&
+ check_is_castable_to(c, &src, dst_t)) {
+ if (are_types_identical(src_t, dst_t)) {
+ gbString oper_str = expr_to_string(o->expr);
+ gbString to_type = type_to_string(dst_t);
+ error(o->expr, "Unneeded transmute of '%s' to identical type '%s'", oper_str, to_type);
+ gb_string_free(oper_str);
+ gb_string_free(to_type);
+ } else if (is_type_internally_pointer_like(src_t) &&
+ is_type_internally_pointer_like(dst_t)) {
+ error(o->expr, "Use of 'transmute' where 'cast' would be preferred since the types are pointer-like");
+ } else if (are_types_identical(src_bt, dst_bt)) {
+ gbString oper_str = expr_to_string(o->expr);
+ gbString to_type = type_to_string(dst_t);
+ error(o->expr, "Unneeded transmute of '%s' to identical type '%s'", oper_str, to_type);
+ gb_string_free(oper_str);
+ gb_string_free(to_type);
+ } else if (is_type_integer(src_t) && is_type_integer(dst_t) &&
+ types_have_same_internal_endian(src_t, dst_t)) {
+ gbString oper_type = type_to_string(src_t);
+ gbString to_type = type_to_string(dst_t);
+ error(o->expr, "Use of 'transmute' where 'cast' would be preferred since both are integers of the same endianness, from '%s' to '%s'", oper_type, to_type);
+ gb_string_free(to_type);
+ gb_string_free(oper_type);
+ }
+ }
}
o->mode = Addressing_Value;
@@ -3223,6 +3672,13 @@ gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x
}
}
}
+ if (is_type_simd_vector(x->type) && !is_type_simd_vector(y->type)) {
+ if (check_is_assignable_to(c, y, x->type)) {
+ if (check_binary_op(c, x, op)) {
+ return true;
+ }
+ }
+ }
return false;
}
@@ -3244,6 +3700,13 @@ gb_internal Type *check_matrix_type_hint(Type *matrix, Type *type_hint) {
Type *th = base_type(type_hint);
if (are_types_identical(th, xt)) {
return type_hint;
+ } else if (xt->kind == Type_Matrix && th->kind == Type_Matrix) {
+ if (!are_types_identical(xt->Matrix.elem, th->Matrix.elem)) {
+ // ignore
+ } if (xt->Matrix.row_count == th->Matrix.row_count &&
+ xt->Matrix.column_count == th->Matrix.column_count) {
+ return type_hint;
+ }
} else if (xt->kind == Type_Matrix && th->kind == Type_Array) {
if (!are_types_identical(xt->Matrix.elem, th->Array.elem)) {
// ignore
@@ -3278,14 +3741,23 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
if (xt->Matrix.column_count != yt->Matrix.row_count) {
goto matrix_error;
}
+
+ if (xt->Matrix.is_row_major != yt->Matrix.is_row_major) {
+ goto matrix_error;
+ }
+
x->mode = Addressing_Value;
if (are_types_identical(xt, yt)) {
+ if (are_types_identical(x->type, y->type)) {
+ return;
+ }
if (!is_type_named(x->type) && is_type_named(y->type)) {
// prefer the named type
x->type = y->type;
}
} else {
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count);
+ bool is_row_major = xt->Matrix.is_row_major && yt->Matrix.is_row_major;
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count, nullptr, nullptr, is_row_major);
}
goto matrix_success;
} else if (yt->kind == Type_Array) {
@@ -3299,10 +3771,10 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
// Treat arrays as column vectors
x->mode = Addressing_Value;
- if (type_hint == nullptr && xt->Matrix.row_count == yt->Array.count) {
+ if (xt->Matrix.row_count == yt->Array.count) {
x->type = y->type;
} else {
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1);
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, 1, nullptr, nullptr, xt->Matrix.is_row_major);
}
goto matrix_success;
}
@@ -3330,10 +3802,10 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand
// Treat arrays as row vectors
x->mode = Addressing_Value;
- if (type_hint == nullptr && yt->Matrix.column_count == xt->Array.count) {
+ if (yt->Matrix.column_count == xt->Array.count) {
x->type = x->type;
} else {
- x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count);
+ x->type = alloc_type_matrix(yt->Matrix.elem, 1, yt->Matrix.column_count, nullptr, nullptr, yt->Matrix.is_row_major);
}
goto matrix_success;
} else if (are_types_identical(yt->Matrix.elem, xt)) {
@@ -3387,10 +3859,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// NOTE(bill): Allow comparisons between types
if (is_ise_expr(be->left)) {
// Evalute the right before the left for an '.X' expression
- check_expr_or_type(c, y, be->right, type_hint);
+ check_expr_or_type(c, y, be->right, nullptr /* ignore type hint */);
check_expr_or_type(c, x, be->left, y->type);
} else {
- check_expr_or_type(c, x, be->left, type_hint);
+ check_expr_or_type(c, x, be->left, nullptr /* ignore type hint */);
check_expr_or_type(c, y, be->right, x->type);
}
bool xt = x->mode == Addressing_Type;
@@ -3419,6 +3891,12 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in
check_expr(c, y, be->right);
Type *rhs_type = type_deref(y->type);
+ if (rhs_type == nullptr) {
+ error(y->expr, "Cannot use '%.*s' on an expression with no value", LIT(op.string));
+ x->mode = Addressing_Invalid;
+ x->expr = node;
+ return;
+ }
if (is_type_bit_set(rhs_type)) {
Type *elem = base_type(rhs_type)->BitSet.elem;
@@ -3558,6 +4036,60 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
return;
}
+ bool REQUIRE = true;
+ Type *bt = base_type(x->type);
+ if (op.kind == Token_Mod || op.kind == Token_ModEq ||
+ op.kind == Token_ModMod || op.kind == Token_ModModEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128: add_package_dependency(c, "runtime", "umodti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "modti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
+ case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
+ case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break;
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
+ case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
+ case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
+
+ case Basic_u128: add_package_dependency(c, "runtime", "udivti3", REQUIRE); break;
+ case Basic_i128: add_package_dependency(c, "runtime", "divti3", REQUIRE); break;
+ }
+ } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
+ case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
+ case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
+
+
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__multi3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shl || op.kind == Token_ShlEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__ashlti3", REQUIRE);
+ }
+ break;
+ }
+ } else if (op.kind == Token_Shr || op.kind == Token_ShrEq) {
+ if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+ case Basic_u128:
+ case Basic_i128:
+ if (is_arch_wasm()) {
+ add_package_dependency(c, "runtime", "__lshrti3", REQUIRE);
+ }
+ break;
+ }
+ }
+
if (token_is_shift(op.kind)) {
check_shift(c, x, y, node, type_hint);
return;
@@ -3726,25 +4258,6 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
return;
}
- if (op.kind == Token_Quo || op.kind == Token_QuoEq) {
- Type *bt = base_type(x->type);
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_complex32: add_package_dependency(c, "runtime", "quo_complex32"); break;
- case Basic_complex64: add_package_dependency(c, "runtime", "quo_complex64"); break;
- case Basic_complex128: add_package_dependency(c, "runtime", "quo_complex128"); break;
- case Basic_quaternion64: add_package_dependency(c, "runtime", "quo_quaternion64"); break;
- case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
- case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
- }
- } else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
- Type *bt = base_type(x->type);
- if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
- case Basic_quaternion64: add_package_dependency(c, "runtime", "mul_quaternion64"); break;
- case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
- case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
- }
- }
-
x->mode = Addressing_Value;
}
@@ -3891,7 +4404,7 @@ gb_internal void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue
}
}
-gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
+gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type, bool ignore_error_block=false) {
gbString expr_str = expr_to_string(operand->expr);
gbString type_str = type_to_string(target_type);
gbString from_type_str = type_to_string(operand->type);
@@ -3905,7 +4418,9 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type
}
}
}
- ERROR_BLOCK();
+ if (!ignore_error_block) {
+ begin_error_block();
+ }
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
if (operand->value.kind == ExactValue_String) {
@@ -3920,6 +4435,11 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type
gb_string_free(type_str);
gb_string_free(expr_str);
operand->mode = Addressing_Invalid;
+
+ if (!ignore_error_block) {
+ end_error_block();
+ }
+
}
gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
@@ -3941,8 +4461,8 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
}
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
- GB_ASSERT_NOT_NULL(target_type);
- if (operand->mode == Addressing_Invalid ||
+ // GB_ASSERT_NOT_NULL(target_type);
+ if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
target_type == t_invalid) {
@@ -3983,7 +4503,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;
@@ -4042,15 +4563,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
+ case Type_SimdVector: {
+ Type *elem = base_array_type(t);
+ if (check_is_assignable_to(c, operand, elem)) {
+ operand->mode = Addressing_Value;
+ } else {
+ operand->mode = Addressing_Invalid;
+ convert_untyped_error(c, operand, target_type);
+ return;
+ }
+
+ break;
+ }
+
case Type_Matrix: {
Type *elem = base_array_type(t);
if (check_is_assignable_to(c, operand, elem)) {
if (t->Matrix.row_count != t->Matrix.column_count) {
operand->mode = Addressing_Invalid;
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
error_line("\tNote: Only a square matrix types can be initialized with a scalar value\n");
return;
} else {
@@ -4066,6 +4599,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
case Type_Union:
+ // IMPORTANT NOTE HACK(bill): This is just to allow for comparisons against `0` with the `os.Error` type
+ // as a kind of transition period
+ if (!build_context.strict_style &&
+ operand->mode == Addressing_Constant &&
+ target_type->kind == Type_Named &&
+ (c->pkg == nullptr || c->pkg->name != "os") &&
+ target_type->Named.name == "Error") {
+ Entity *e = target_type->Named.type_name;
+ if (e->pkg && e->pkg->name == "os") {
+ if (is_exact_value_zero(operand->value) &&
+ (operand->value.kind == ExactValue_Integer ||
+ operand->value.kind == ExactValue_Float)) {
+ operand->mode = Addressing_Value;
+ // target_type = t_untyped_nil;
+ operand->value = empty_exact_value;
+ update_untyped_expr_value(c, operand->expr, operand->value);
+ break;
+ }
+ }
+ }
+ // "fallthrough"
if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
TEMPORARY_ALLOCATOR_GUARD();
@@ -4104,17 +4658,21 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
defer (gb_string_free(type_str));
if (valid_count == 1) {
+ Type *new_type = t->Union.variants[first_success_index];
+ target_type = new_type;
+ if (is_type_union(new_type)) {
+ convert_to_typed(c, operand, new_type);
+ break;
+ }
+ operand->type = new_type;
operand->mode = Addressing_Value;
- operand->type = t->Union.variants[first_success_index];
- target_type = t->Union.variants[first_success_index];
break;
} else if (valid_count > 1) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
GB_ASSERT(first_success_index >= 0);
operand->mode = Addressing_Invalid;
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
error_line("Ambiguous type conversion to '%s', which variant did you mean:\n\t", type_str);
i32 j = 0;
@@ -4136,13 +4694,13 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else if (is_type_untyped_uninit(operand->type)) {
target_type = t_untyped_uninit;
} else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
operand->mode = Addressing_Invalid;
- convert_untyped_error(c, operand, target_type);
+ convert_untyped_error(c, operand, target_type, true);
if (count > 0) {
error_line("'%s' is a union which only excepts the following types:\n", type_str);
+
error_line("\t");
for (i32 i = 0; i < count; i++) {
Type *v = t->Union.variants[i];
@@ -4196,7 +4754,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
check_expr_with_type_hint(c, &operand, index_value, type_hint);
if (operand.mode == Addressing_Invalid) {
if (value) *value = 0;
- return false;
+ // NOTE(bill): return true here to propagate the errors better
+ return true;
}
Type *index_type = t_int;
@@ -4352,7 +4911,8 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
String name = fv->field->Ident.token.string;
Selection sub_sel = lookup_field(node->tav.type, name, false);
defer (array_free(&sub_sel.index));
- if (sub_sel.index[0] == index) {
+ if (sub_sel.index.count > 0 &&
+ sub_sel.index[0] == index) {
value = fv->value->tav.value;
found = true;
break;
@@ -4416,7 +4976,7 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
TypeAndValue tav = fv->value->tav;
if (success_) *success_ = true;
if (finish_) *finish_ = false;
- return tav.value;;
+ return tav.value;
}
}
@@ -4440,8 +5000,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
+ } else if (is_type_proc(tav.type)) {
+ if (success_) *success_ = true;
+ if (finish_) *finish_ = false;
+ return tav.value;
} else {
- GB_ASSERT(is_type_untyped_nil(tav.type));
+ GB_ASSERT_MSG(is_type_untyped_nil(tav.type), "%s", type_to_string(tav.type));
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
@@ -4491,7 +5055,6 @@ gb_internal ExactValue get_constant_field(CheckerContext *c, Operand const *oper
return value;
}
}
-
if (success_) *success_ = true;
return value;
} else if (value.kind == ExactValue_Quaternion) {
@@ -4575,7 +5138,8 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s
if (entity->kind == Entity_Builtin) {
// NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
// This means that we should just ignore the found result through it
- *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
+ *allow_builtin = entity->scope == import_scope ||
+ (entity->scope != builtin_pkg->scope && entity->scope != intrinsics_pkg->scope);
} else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
is_declared = false;
}
@@ -4585,7 +5149,27 @@ gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_s
// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
- if (node->kind == Ast_Ident) {
+ if (node == nullptr) {
+ return nullptr;
+ }
+ /*if (node->kind == Ast_TernaryWhenExpr) {
+ ast_node(we, TernaryWhenExpr, node);
+ if (we->cond == nullptr) {
+ return nullptr;
+ }
+ if (we->cond->tav.mode != Addressing_Constant) {
+ return nullptr;
+ }
+ if (we->cond->tav.value.kind != ExactValue_Bool) {
+ return nullptr;
+ }
+ if (we->cond->tav.value.value_bool) {
+ return check_entity_from_ident_or_selector(c, we->x, ident_only);
+ } else {
+ Entity *e = check_entity_from_ident_or_selector(c, we->y, ident_only);
+ return e;
+ }
+ } else */if (node->kind == Ast_Ident) {
String name = node->Ident.token.string;
return scope_lookup(c->scope, name);
} else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
@@ -4673,10 +5257,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Selection sel = {}; // NOTE(bill): Not used if it's an import name
if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) {
+ ERROR_BLOCK();
error(node, "Illegal use of -> selector shorthand outside of a call");
- operand->mode = Addressing_Invalid;
- operand->expr = node;
- return nullptr;
+ gbString x = expr_to_string(se->expr);
+ gbString y = expr_to_string(se->selector);
+ error_line("\tSuggestion: Did you mean '%s.%s'?\n", x, y);
+ gb_string_free(y);
+ gb_string_free(x);
+
+ // TODO(bill): Should this terminate here or propagate onwards?
+ // operand->mode = Addressing_Invalid;
+ // operand->expr = node;
+ // return nullptr;
}
operand->expr = node;
@@ -4702,7 +5294,14 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
add_entity_use(c, op_expr, e);
expr_entity = e;
- if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
+ if (e != nullptr && (e->kind == Entity_Procedure || e->kind == Entity_ProcGroup) && selector->kind == Ast_Ident) {
+ gbString sel_str = expr_to_string(selector);
+ error(node, "'%s' is not declared by by '%.*s'", sel_str, LIT(e->token.string));
+ gb_string_free(sel_str);
+ operand->mode = Addressing_Invalid;
+ operand->expr = node;
+ return nullptr;
+ } else if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
// IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
// It pretty much needs to be in this order and this way
// If you can clean this up, please do but be really careful
@@ -4710,10 +5309,19 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Scope *import_scope = e->ImportName.scope;
String entity_name = selector->Ident.token.string;
+ if (import_scope == nullptr) {
+ ERROR_BLOCK();
+ error(node, "'%.*s' is not imported in this file, '%.*s' is unavailable", LIT(import_name), LIT(entity_name));
+ operand->mode = Addressing_Invalid;
+ operand->expr = node;
+ return nullptr;
+ }
+
check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name);
bool allow_builtin = false;
if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
+ ERROR_BLOCK();
error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4784,28 +5392,34 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
}
+ if (operand->type && is_type_soa_struct(type_deref(operand->type))) {
+ complete_soa_type(c->checker, type_deref(operand->type), false);
+ }
if (entity == nullptr && selector->kind == Ast_Ident) {
String field_name = selector->Ident.token.string;
Type *t = type_deref(operand->type);
if (t == nullptr) {
error(operand->expr, "Cannot use a selector expression on 0-value expression");
- } else if (is_type_dynamic_array(t)) {
- init_mem_allocator(c->checker);
- }
- sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type);
- entity = sel.entity;
+ } else {
+ if (is_type_dynamic_array(t)) {
+ init_mem_allocator(c->checker);
+ }
+ sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type);
+ entity = sel.entity;
- // NOTE(bill): Add type info needed for fields like 'names'
- if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) {
- add_type_info_type(c, operand->type);
- }
- if (is_type_enum(operand->type)) {
- add_type_info_type(c, operand->type);
+ // NOTE(bill): Add type info needed for fields like 'names'
+ if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) {
+ add_type_info_type(c, operand->type);
+ }
+ if (is_type_enum(operand->type)) {
+ add_type_info_type(c, operand->type);
+ }
}
}
- if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
+ if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr &&
+ (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -4860,8 +5474,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
Type *original_type = operand->type;
Type *array_type = base_type(type_deref(original_type));
- GB_ASSERT(array_type->kind == Type_Array);
- i64 array_count = array_type->Array.count;
+ GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
+
+ i64 array_count = get_array_type_count(array_type);
+
for (u8 i = 0; i < index_count; i++) {
u8 idx = indices>>(i*2) & 3;
if (idx >= array_count) {
@@ -4881,7 +5497,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
se->swizzle_count = index_count;
se->swizzle_indices = indices;
-
AddressingMode prev_mode = operand->mode;
operand->mode = Addressing_SwizzleValue;
operand->type = determine_swizzle_array_type(original_type, type_hint, index_count);
@@ -4895,6 +5510,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
break;
}
+ if (array_type->kind == Type_SimdVector) {
+ operand->mode = Addressing_Value;
+ }
+
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
@@ -4914,6 +5533,8 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str);
}
} else {
+ ERROR_BLOCK();
+
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) {
@@ -5008,6 +5629,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
operand->type = entity->type;
operand->expr = node;
+ if (entity->flags & EntityFlag_BitFieldField) {
+ add_package_dependency(c, "runtime", "__write_bits");
+ add_package_dependency(c, "runtime", "__read_bits");
+ }
+
switch (entity->kind) {
case Entity_Constant:
operand->value = entity->Constant.value;
@@ -5021,6 +5647,9 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
}
break;
case Entity_Variable:
+ if (sel.is_bit_field) {
+ se->is_bit_field = true;
+ }
if (sel.indirect) {
operand->mode = Addressing_Variable;
} else if (operand->mode == Addressing_Context) {
@@ -5126,7 +5755,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
if (o.type && is_type_no_copy(o.type)) {
Ast *expr = unparen_expr(o.expr);
- if (expr && o.mode != Addressing_Constant) {
+ if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) {
if (expr->kind == Ast_CallExpr) {
// Okay
} else {
@@ -5569,10 +6198,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Operand *variadic_operand = &ordered_operands[pt->variadic_index];
if (vari_expand) {
- GB_ASSERT(variadic_operands.count != 0);
- *variadic_operand = variadic_operands[0];
- variadic_operand->type = default_type(variadic_operand->type);
- actually_variadic = true;
+ if (variadic_operands.count == 0) {
+ error(call, "'..' in the wrong position");
+ } else {
+ GB_ASSERT(variadic_operands.count != 0);
+ *variadic_operand = variadic_operands[0];
+ variadic_operand->type = default_type(variadic_operand->type);
+ actually_variadic = true;
+ }
} else {
AstFile *f = call->file();
@@ -5606,9 +6239,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
- if (is_blank_ident(e->token)) {
- continue;
- }
if (e->kind == Entity_Variable) {
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
ordered_operands[i].mode = Addressing_Value;
@@ -5639,14 +6269,20 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 {
+ bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast));
i64 s = 0;
- if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) {
+ if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) {
bool ok = false;
- if (e && e->flags & EntityFlag_AnyInt) {
+ if (e && (e->flags & EntityFlag_AnyInt)) {
if (is_type_integer(param_type)) {
ok = check_is_castable_to(c, o, param_type);
}
}
+ if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) {
+ if (show_error) {
+ error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument");
+ }
+ }
if (ok) {
s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
} else {
@@ -5655,7 +6291,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
err = CallArgumentError_WrongTypes;
}
-
} else if (show_error) {
check_assignment(c, o, param_type, str_lit("procedure argument"));
}
@@ -5738,20 +6373,25 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
if (param_is_variadic) {
- continue;
+ if (!named_variadic_param) {
+ continue;
+ }
}
- score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
+ score += eval_param_and_score(c, o, e->type, err, false, e, show_error);
}
}
if (variadic) {
- Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
+ Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index];
+ Type *slice = var_entity->type;
GB_ASSERT(is_type_slice(slice));
Type *elem = base_type(slice)->Slice.elem;
Type *t = elem;
if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type));
+ if (show_error) {
+ error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type));
+ }
err = CallArgumentError_AmbiguousPolymorphicVariadic;
}
@@ -5771,7 +6411,24 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
return CallArgumentError_MultipleVariadicExpand;
}
}
- score += eval_param_and_score(c, o, t, err, true, nullptr, show_error);
+ score += eval_param_and_score(c, o, t, err, true, var_entity, show_error);
+ }
+
+ if (!vari_expand && variadic_operands.count != 0) {
+ // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
+ if (c->decl) {
+ bool found = false;
+ for (auto &vr : c->decl->variadic_reuses) {
+ if (are_types_identical(slice, vr.slice_type)) {
+ vr.max_count = gb_max(vr.max_count, variadic_operands.count);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ array_add(&c->decl->variadic_reuses, VariadicReuseData{slice, variadic_operands.count});
+ }
+ }
}
}
@@ -5899,10 +6556,27 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
}
}
- if (call_expr) error(call_expr, "at caller location");
+ if (call_expr) {
+ TokenPos pos = ast_token(call_expr).pos;
+ error_line("%s at caller location\n", token_pos_to_string(pos));
+ }
}
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);
+ }
+ }
+
}
}
@@ -6244,12 +6918,17 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
array_add(&proc_entities, proc);
}
+ int max_matched_features = 0;
gbString expr_name = expr_to_string(operand->expr);
defer (gb_string_free(expr_name));
for_array(i, procs) {
Entity *p = procs[i];
+ if (p->flags & EntityFlag_Disabled) {
+ continue;
+ }
+
Type *pt = base_type(p->type);
if (pt != nullptr && is_type_proc(pt)) {
CallArgumentData data = {};
@@ -6280,13 +6959,26 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
item.score += assign_score_function(1);
}
+ max_matched_features = gb_max(max_matched_features, matched_target_features(&pt->Proc));
+
item.index = index;
array_add(&valids, item);
}
}
+ if (max_matched_features > 0) {
+ for_array(i, valids) {
+ Entity *p = procs[valids[i].index];
+ Type *t = base_type(p->type);
+ GB_ASSERT(t->kind == Type_Proc);
+
+ int matched = matched_target_features(&t->Proc);
+ valids[i].score += assign_score_function(max_matched_features-matched);
+ }
+ }
+
if (valids.count > 1) {
- gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
+ array_sort(valids, valid_index_and_score_cmp);
i64 best_score = valids[0].score;
Entity *best_entity = proc_entities[valids[0].index];
GB_ASSERT(best_entity != nullptr);
@@ -6333,8 +7025,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
};
if (valids.count == 0) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
if (positional_operands.count == 0 && named_operands.count == 0) {
@@ -6349,9 +7040,73 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
if (procs.count > 0) {
error_line("Did you mean to use one of the following:\n");
}
+
+ // Try to reduce the list further for `$T: typeid` like parameters
+ bool *possibly_ignore = gb_alloc_array(temporary_allocator(), bool, procs.count);
+ isize possibly_ignore_set = 0;
+
+ if (true) {
+ // NOTE(bill): This currently only checks for #soa types
+ for_array(i, procs) {
+ Entity *proc = procs[i];
+ Type *t = base_type(proc->type);
+ if (t == nullptr || t->kind != Type_Proc) {
+ continue;
+ }
+
+ TypeProc *pt = &t->Proc;
+ if (pt->param_count == 0) {
+ continue;
+ }
+
+ for_array(j, pt->params->Tuple.variables) {
+ Entity *v = pt->params->Tuple.variables[j];
+ if (v->kind != Entity_TypeName) {
+ continue;
+ }
+
+ Type *dst_t = base_type(v->type);
+ while (dst_t->kind == Type_Generic && dst_t->Generic.specialized) {
+ dst_t = dst_t->Generic.specialized;
+ }
+
+ if (j >= positional_operands.count) {
+ continue;
+ }
+ Operand const &o = positional_operands[j];
+ if (o.mode != Addressing_Type) {
+ continue;
+ }
+ Type *t = base_type(o.type);
+ if (t->kind == dst_t->kind) {
+ continue;
+ }
+ Type *st = base_type(type_deref(o.type));
+ Type *dt = base_type(type_deref(dst_t));
+ if (st->kind == dt->kind) {
+ continue;
+ }
+ if (is_type_soa_struct(st)) {
+ possibly_ignore[i] = true;
+ possibly_ignore_set += 1;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (possibly_ignore_set == procs.count) {
+ possibly_ignore_set = 0;
+ }
+
+
isize max_name_length = 0;
isize max_type_length = 0;
- for (Entity *proc : procs) {
+ for_array(i, procs) {
+ if (possibly_ignore_set != 0 && possibly_ignore[i]) {
+ continue;
+ }
+ Entity *proc = procs[i];
Type *t = base_type(proc->type);
if (t == t_invalid) continue;
String prefix = {};
@@ -6381,7 +7136,11 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
}
spaces[max_spaces] = 0;
- for (Entity *proc : procs) {
+ for_array(i, procs) {
+ if (possibly_ignore_set != 0 && possibly_ignore[i]) {
+ continue;
+ }
+ Entity *proc = procs[i];
TokenPos pos = proc->token.pos;
Type *t = base_type(proc->type);
if (t == t_invalid) continue;
@@ -6424,11 +7183,14 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
data.result_type = t_invalid;
} else if (valids.count > 1) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
- print_argument_types();
+ if (positional_operands.count == 0 && named_operands.count == 0) {
+ error_line("\tNo given arguments\n");
+ } else {
+ print_argument_types();
+ }
for (auto const &valid : valids) {
Entity *proc = proc_entities[valid.index];
@@ -6641,6 +7403,8 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Array<Operand> operands = {};
defer (array_free(&operands));
+ CallArgumentError err = CallArgumentError_None;
+
bool named_fields = false;
{
// NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves
@@ -6658,12 +7422,17 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Ast *arg = ce->args[i];
ast_node(fv, FieldValue, arg);
+ if (fv->value == nullptr) {
+ error(fv->eq, "Expected a value");
+ err = CallArgumentError_InvalidFieldValue;
+ continue;
+ }
if (fv->field->kind == Ast_Ident) {
String name = fv->field->Ident.token.string;
isize index = lookup_polymorphic_record_parameter(original_type, name);
if (index >= 0) {
TypeTuple *params = get_record_polymorphic_params(original_type);
- Entity *e = params->variables[i];
+ Entity *e = params->variables[index];
if (e->kind == Entity_Constant) {
check_expr_with_type_hint(c, &operands[i], fv->value, e->type);
continue;
@@ -6696,7 +7465,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
- CallArgumentError err = CallArgumentError_None;
+ if (err != 0) {
+ operand->mode = Addressing_Invalid;
+ return err;
+ }
TypeTuple *tuple = get_record_polymorphic_params(original_type);
isize param_count = tuple->variables.count;
@@ -6714,7 +7486,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
Array<Operand> ordered_operands = operands;
if (!named_fields) {
- ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
+ ordered_operands = array_make<Operand>(permanent_allocator(), operands.count);
array_copy(&ordered_operands, operands, 0);
} else {
TEMPORARY_ALLOCATOR_GUARD();
@@ -6902,8 +7674,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
}
{
- bool failure = false;
- Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
+ GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
+
+ mutex_lock(&found_gen_types->mutex);
+ defer (mutex_unlock(&found_gen_types->mutex));
+ Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
+
if (found_entity) {
operand->mode = Addressing_Type;
operand->type = found_entity->type;
@@ -6953,14 +7729,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(bt);
+ 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, ", ");
@@ -6973,8 +7744,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
s = write_type_to_string(s, v->type, false);
}
} else if (v->kind == Entity_Constant) {
- s = gb_string_append_fmt(s, "=");
- s = write_exact_value_to_string(s, v->Constant.value);
+ if (v->Constant.value.kind != ExactValue_Invalid) {
+ s = gb_string_append_fmt(s, "=");
+ s = write_exact_value_to_string(s, v->Constant.value);
+ }
}
}
s = gb_string_append_fmt(s, ")");
@@ -7042,13 +7815,16 @@ 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_or"
+ name == "hash" ||
+ name == "caller_expression"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -7190,6 +7966,14 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
add_entity_use(c, operand->expr, initial_entity);
+
+ if (initial_entity->Procedure.entry_point_only) {
+ if (c->curr_proc_decl && c->curr_proc_decl->entity == c->info->entry_point) {
+ // Okay
+ } else {
+ error(operand->expr, "Procedures with the attribute '@(entry_point_only)' can only be called directly from the user-level entry point procedure");
+ }
+ }
}
if (operand->mode != Addressing_ProcGroup) {
@@ -7234,6 +8018,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
pt = data.gen_entity->type;
}
}
+ pt = base_type(pt);
if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) {
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
@@ -7261,8 +8046,11 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ bool is_call_inlined = false;
+
switch (inlining) {
case ProcInlining_inline:
+ is_call_inlined = true;
if (proc != nullptr) {
Entity *e = entity_from_expr(proc);
if (e != nullptr && e->kind == Entity_Procedure) {
@@ -7270,7 +8058,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'");
}
}
}
@@ -7278,6 +8066,53 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
break;
case ProcInlining_no_inline:
break;
+ case ProcInlining_none:
+ if (proc != nullptr) {
+ Entity *e = entity_from_expr(proc);
+ if (e != nullptr && e->kind == Entity_Procedure) {
+ DeclInfo *decl = e->decl_info;
+ if (decl->proc_lit) {
+ ast_node(pl, ProcLit, decl->proc_lit);
+ if (pl->inlining == ProcInlining_inline) {
+ is_call_inlined = true;
+ }
+ }
+ }
+ }
+ }
+
+ {
+ String invalid;
+ if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) {
+ if (!check_target_feature_is_valid_for_target_arch(pt->Proc.require_target_feature, &invalid)) {
+ error(call, "Called procedure requires target feature '%.*s' which is invalid for the build target", LIT(invalid));
+ } else if (!check_target_feature_is_enabled(pt->Proc.require_target_feature, &invalid)) {
+ error(call, "Calling this procedure requires target feature '%.*s' to be enabled", LIT(invalid));
+ }
+ }
+
+ if (pt->kind == Type_Proc && pt->Proc.enable_target_feature.len != 0) {
+ if (!check_target_feature_is_valid_for_target_arch(pt->Proc.enable_target_feature, &invalid)) {
+ error(call, "Called procedure enables target feature '%.*s' which is invalid for the build target", LIT(invalid));
+ }
+
+ // NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features.
+ if (is_call_inlined) {
+ if (c->curr_proc_decl == nullptr) {
+ error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
+ } else {
+ GB_ASSERT(c->curr_proc_decl->entity);
+ GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
+ String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
+ if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
+ ERROR_BLOCK();
+ error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
+
+ error_line("\tSuggested Example: @(enable_target_feature=\"%.*s\")\n", LIT(invalid));
+ }
+ }
+ }
+ }
}
operand->expr = call;
@@ -7381,13 +8216,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:
@@ -7397,17 +8237,6 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
}
return true;
- case Type_RelativeMultiPointer:
- {
- Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
- GB_ASSERT(pointer_type->kind == Type_MultiPointer);
- o->type = pointer_type->MultiPointer.elem;
- if (o->mode != Addressing_Constant) {
- o->mode = Addressing_Variable;
- }
- }
- return true;
-
case Type_DynamicArray:
o->type = t->DynamicArray.elem;
if (o->mode != Addressing_Constant) {
@@ -7420,7 +8249,7 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
*max_count = t->Struct.soa_count;
}
o->type = t->Struct.soa_elem;
- if (o->mode == Addressing_SoaVariable || o->mode == Addressing_Variable) {
+ if (o->mode == Addressing_SoaVariable || o->mode == Addressing_Variable || indirection) {
o->mode = Addressing_SoaVariable;
} else {
o->mode = Addressing_Value;
@@ -7432,8 +8261,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;
}
@@ -7636,6 +8465,8 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
String name = ise->selector->Ident.token.string;
if (is_type_enum(th)) {
+ ERROR_BLOCK();
+
Type *bt = base_type(th);
GB_ASSERT(bt->kind == Type_Enum);
@@ -7644,6 +8475,15 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
error(node, "Undeclared name '%.*s' for type '%s'", LIT(name), typ);
check_did_you_mean_type(name, bt->Enum.fields);
+ } else if (is_type_bit_set(th) && is_type_enum(th->BitSet.elem)) {
+ ERROR_BLOCK();
+
+ gbString typ = type_to_string(th);
+ gbString str = expr_to_string(node);
+ error(node, "Cannot convert enum value to '%s'", typ);
+ error_line("\tSuggestion: Did you mean '{ %s }'?\n", str);
+ gb_string_free(typ);
+ gb_string_free(str);
} else {
gbString typ = type_to_string(th);
gbString str = expr_to_string(node);
@@ -7658,7 +8498,7 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
}
-gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) {
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_, bool change_operand) {
switch (x->mode) {
case Addressing_MapIndex:
case Addressing_OptionalOk:
@@ -7676,22 +8516,28 @@ gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type *
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
if (is_type_proc(pt)) {
Type *tuple = pt->Proc.results;
- add_type_and_value(c, x->expr, x->mode, tuple, x->value);
if (pt->Proc.result_count >= 2) {
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
}
- expr->CallExpr.optional_ok_one = false;
- x->type = tuple;
+ if (change_operand) {
+ expr->CallExpr.optional_ok_one = false;
+ x->type = tuple;
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
+ }
return;
}
}
Type *tuple = make_optional_ok_type(x->type);
+
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
- add_type_and_value(c, x->expr, x->mode, tuple, x->value);
- x->type = tuple;
- GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
+
+ if (change_operand) {
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
+ x->type = tuple;
+ GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
+ }
}
@@ -7779,11 +8625,10 @@ gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Op
}
uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
+ GB_ASSERT(key != 0);
+ isize count = multi_map_count(seen, key);
+ if (count) {
TEMPORARY_ALLOCATOR_GUARD();
-
- isize count = multi_map_count(seen, key);
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
multi_map_get_all(seen, key, taps);
@@ -7857,37 +8702,75 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
o->mode = Addressing_Constant;
String name = bd->name.string;
if (name == "file") {
+ String file = get_file_path_string(bd->token.pos.file_id);
+ if (build_context.obfuscate_source_code_locations) {
+ file = obfuscate_string(file, "F");
+ }
+ o->type = t_untyped_string;
+ o->value = exact_value_string(file);
+ } else if (name == "directory") {
+ String file = get_file_path_string(bd->token.pos.file_id);
+ String path = dir_from_path(file);
+ if (build_context.obfuscate_source_code_locations) {
+ path = obfuscate_string(path, "D");
+ }
o->type = t_untyped_string;
- o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ o->value = exact_value_string(path);
} else if (name == "line") {
+ i32 line = bd->token.pos.line;
+ if (build_context.obfuscate_source_code_locations) {
+ line = obfuscate_i32(line);
+ }
o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.line);
+ o->value = exact_value_i64(line);
} else if (name == "procedure") {
if (c->curr_proc_decl == nullptr) {
error(node, "#procedure may only be used within procedures");
o->type = t_untyped_string;
o->value = exact_value_string(str_lit(""));
} else {
+ String p = c->proc_name;
+ if (build_context.obfuscate_source_code_locations) {
+ p = obfuscate_string(p, "P");
+ }
o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
+ o->value = exact_value_string(p);
}
} else if (name == "caller_location") {
init_core_source_code_location(c->checker);
error(node, "#caller_location may only be used as a default argument parameter");
o->type = t_source_code_location;
o->mode = Addressing_Value;
+ } else if (name == "caller_expression") {
+ error(node, "#caller_expression may only be used as a default argument parameter");
+ o->type = t_string;
+ o->mode = Addressing_Value;
+ } else if (name == "branch_location") {
+ if (!c->in_defer) {
+ error(node, "#branch_location may only be used within a 'defer' statement");
+ } else if (c->curr_proc_decl) {
+ Entity *e = c->curr_proc_decl->entity;
+ if (e != nullptr) {
+ GB_ASSERT(e->kind == Entity_Procedure);
+ e->Procedure.uses_branch_location = true;
+ }
+ }
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
} else {
if (name == "location") {
init_core_source_code_location(c->checker);
- error(node, "'#%.*s' must be used in a call expression", LIT(name));
+ error(node, "'#location' must be used as a call, i.e. #location(proc), where #location() defaults to the procedure in which it was used.");
o->type = t_source_code_location;
o->mode = Addressing_Value;
} else if (
name == "assert" ||
name == "defined" ||
name == "config" ||
+ name == "exists" ||
name == "load" ||
name == "load_hash" ||
+ name == "load_directory" ||
name == "load_or"
) {
error(node, "'#%.*s' must be used as a call", LIT(name));
@@ -7939,11 +8822,6 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n
return kind;
}
- if (x.type == nullptr || x.type == t_invalid ||
- y.type == nullptr || y.type == t_invalid) {
- return kind;
- }
-
bool use_type_hint = type_hint != nullptr && (is_operand_nil(x) || is_operand_nil(y));
convert_to_typed(c, &x, use_type_hint ? type_hint : y.type);
@@ -8179,6 +9057,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no
// NOTE(bill): allow implicit conversion between boolean types
// within 'or_return' to improve the experience using third-party code
} else if (!check_is_assignable_to(c, &rhs, end_type)) {
+ ERROR_BLOCK();
// TODO(bill): better error message
gbString a = type_to_string(right_type);
gbString b = type_to_string(end_type);
@@ -8240,7 +9119,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
// okay
} else {
gbString s = type_to_string(right_type);
- error(node, "'%.*s' requires a boolean or nil-able type, got %s", s);
+ error(node, "'%.*s' requires a boolean or nil-able type, got %s", LIT(name), s);
gb_string_free(s);
}
}
@@ -8261,6 +9140,7 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
switch (be->token.kind) {
case Token_or_break:
+ node->viral_state_flags |= ViralStateFlag_ContainsOrBreak;
if ((c->stmt_flags & Stmt_BreakAllowed) == 0 && label == nullptr) {
error(be->token, "'%.*s' only allowed in non-inline loops or 'switch' statements", LIT(name));
}
@@ -8273,6 +9153,10 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
}
if (label != nullptr) {
+ if (c->in_defer) {
+ error(label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(name));
+ return Expr_Expr;
+ }
if (label->kind != Ast_Ident) {
error(label, "A branch statement's label name must be an identifier");
return Expr_Expr;
@@ -8323,50 +9207,74 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
StringMap<String> fields_visited_through_raw_union = {};
defer (string_map_destroy(&fields_visited_through_raw_union));
+ String assignment_str = str_lit("structure literal");
+ if (bt->kind == Type_BitField) {
+ assignment_str = str_lit("bit_field literal");
+ }
+
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);
+ Ast *ident = fv->field;
+ if (ident->kind == Ast_ImplicitSelectorExpr) {
+ gbString expr_str = expr_to_string(ident);
+ error(ident, "Field names do not start with a '.', remove the '.' in structure literal", expr_str);
+ gb_string_free(expr_str);
+
+ ident = ident->ImplicitSelectorExpr.selector;
+ }
+ if (ident->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(ident);
error(elem, "Invalid field name '%s' in structure literal", expr_str);
gb_string_free(expr_str);
continue;
}
- String name = fv->field->Ident.token.string;
+ String name = ident->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));
+ error(ident, "Unknown field '%.*s' in structure literal", LIT(name));
continue;
}
- Entity *field = bt->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
+ Entity *field = nullptr;
+ if (bt->kind == Type_Struct) {
+ field = bt->Struct.fields[sel.index[0]];
+ } else if (bt->kind == Type_BitField) {
+ field = bt->BitField.fields[sel.index[0]];
+ } else {
+ GB_PANIC("Unknown type");
+ }
+
+
+ add_entity_use(c, ident, 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));
+ error(ident, "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));
+ error(ident, "Duplicate or reused field '%.*s' in %.*s", LIT(sel.entity->token.string), LIT(assignment_str));
}
} else {
- error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
+ error(ident, "Duplicate field '%.*s' in %.*s", LIT(field->token.string), LIT(assignment_str));
}
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));
+ error(ident, "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));
+ error(ident, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a %.*s", cast(int)sel.index.count-1, LIT(name), LIT(assignment_str));
continue;
}
if (sel.index.count > 1) {
+ GB_ASSERT(bt->kind == Type_Struct);
+
if (is_constant) {
Type *ft = type;
for (i32 index : sel.index) {
@@ -8382,6 +9290,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;
@@ -8408,6 +9320,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;
@@ -8427,10 +9342,52 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ u8 prev_bit_field_bit_size = c->bit_field_bit_size;
+ if (field->kind == Entity_Variable && field->Variable.bit_field_bit_size) {
+ // HACK NOTE(bill): This is a bit of a hack, but it will work fine for this use case
+ c->bit_field_bit_size = field->Variable.bit_field_bit_size;
+ }
+
+ check_assignment(c, &o, field->type, assignment_str);
+
+ c->bit_field_bit_size = prev_bit_field_bit_size;
}
}
+gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
+ type_expr = unparen_expr(type_expr);
+ if (type_expr == nullptr) {
+ return false;
+ }
+
+ // [?]Type
+ if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) {
+ Ast *count = type_expr->ArrayType.count;
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ return true;
+ }
+ }
+ return false;
+}
+
+gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
+ if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
+ ERROR_BLOCK();
+ error(node, "Compound literals of dynamic types are disabled by default");
+ error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
+ error_line("\tWarning: Please understand that dynamic literals will implicitly allocate using the current 'context.allocator' in that scope\n");
+ if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+ error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+ } else if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+ }
+ return false;
+ }
+
+ return cl->elems.count > 0;
+}
+
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);
@@ -8441,20 +9398,35 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
bool is_to_be_determined_array_count = false;
bool is_constant = true;
- if (cl->type != nullptr) {
+
+ Ast *type_expr = cl->type;
+
+ bool used_type_hint_expr = false;
+ if (type_expr == nullptr && c->type_hint_expr != nullptr) {
+ if (is_expr_inferred_fixed_array(c->type_hint_expr)) {
+ type_expr = clone_ast(c->type_hint_expr);
+ used_type_hint_expr = true;
+ }
+ }
+
+ if (type_expr != nullptr) {
type = nullptr;
// [?]Type
- if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
- Ast *count = cl->type->ArrayType.count;
- if (count->kind == Ast_UnaryExpr &&
- count->UnaryExpr.op.kind == Token_Question) {
- type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
- is_to_be_determined_array_count = true;
+ if (type_expr->kind == Ast_ArrayType) {
+ Ast *count = type_expr->ArrayType.count;
+ if (count != nullptr) {
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ type = alloc_type_array(check_type(c, type_expr->ArrayType.elem), -1);
+ is_to_be_determined_array_count = true;
+ }
+ } else {
+ type = alloc_type_slice(check_type(c, type_expr->ArrayType.elem));
}
if (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
+ if (type_expr->ArrayType.tag != nullptr) {
+ Ast *tag = type_expr->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -8463,10 +9435,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
}
}
- }
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ } else if (type_expr->kind == Ast_DynamicArrayType && type_expr->DynamicArrayType.tag != nullptr) {
if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
+ Ast *tag = type_expr->DynamicArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -8477,7 +9448,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
if (type == nullptr) {
- type = check_type(c, cl->type);
+ type = check_type(c, type_expr);
}
}
@@ -8503,6 +9474,12 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (cl->elems.count == 0) {
break; // NOTE(bill): No need to init
}
+
+ if (t->Struct.soa_kind != StructSoa_None) {
+ error(node, "#soa arrays are not supported for compound literals");
+ break;
+ }
+
if (t->Struct.is_raw_union) {
if (cl->elems.count > 0) {
// NOTE: unions cannot be constant
@@ -8525,6 +9502,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
break;
}
+ wait_signal_until_available(&t->Struct.fields_wait_signal);
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--) {
@@ -8610,11 +9588,6 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
elem_type = t->DynamicArray.elem;
context_name = str_lit("dynamic array literal");
is_constant = false;
-
- if (!build_context.no_dynamic_literals) {
- add_package_dependency(c, "runtime", "__dynamic_array_reserve");
- add_package_dependency(c, "runtime", "__dynamic_array_append");
- }
} else if (t->kind == Type_SimdVector) {
elem_type = t->SimdVector.elem;
context_name = str_lit("simd vector literal");
@@ -8789,8 +9762,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (t->kind == Type_DynamicArray) {
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
+ if (check_for_dynamic_literals(c, node, cl)) {
+ add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_array_append");
}
}
@@ -9030,15 +10004,15 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
continue;
}
ExactValue v = f->Constant.value;
- auto found = map_get(&seen, hash_exact_value(v));
+ uintptr hash = hash_exact_value(v);
+ auto found = map_get(&seen, hash);
if (!found) {
array_add(&unhandled, f);
}
}
if (unhandled.count > 0) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string));
@@ -9049,9 +10023,11 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
error_line("\t%.*s\n", LIT(f->token.string));
}
}
- error_line("\n");
- error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ if (!build_context.terse_errors) {
+ error_line("\n");
+ error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ }
}
}
@@ -9177,9 +10153,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
}
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
+ if (check_for_dynamic_literals(c, node, cl)) {
add_map_reserve_dependencies(c);
add_map_set_dependencies(c);
}
@@ -9192,10 +10166,14 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
Type *et = base_type(t->BitSet.elem);
isize field_count = 0;
- if (et->kind == Type_Enum) {
+ if (et != nullptr && et->kind == Type_Enum) {
field_count = et->Enum.fields.count;
}
+ if (is_type_array(bit_set_to_int(t))) {
+ is_constant = false;
+ }
+
if (cl->elems[0]->kind == Ast_FieldValue) {
error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
is_constant = false;
@@ -9212,6 +10190,22 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
is_constant = o->mode == Addressing_Constant;
}
+ if (elem->kind == Ast_BinaryExpr) {
+ switch (elem->BinaryExpr.op.kind) {
+ case Token_Or:
+ {
+ gbString x = expr_to_string(elem->BinaryExpr.left);
+ gbString y = expr_to_string(elem->BinaryExpr.right);
+ gbString e = expr_to_string(elem);
+ error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
+ gb_string_free(e);
+ gb_string_free(y);
+ gb_string_free(x);
+ }
+ break;
+ }
+ }
+
check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
if (o->mode == Addressing_Constant) {
i64 lower = t->BitSet.lower;
@@ -9220,7 +10214,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (lower <= v && v <= upper) {
// okay
} else {
- error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
+ gbString s = expr_to_string(o->expr);
+ error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
+ gb_string_free(s);
continue;
}
}
@@ -9228,6 +10224,21 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
break;
}
+ case Type_BitField: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ is_constant = false;
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('bit_field') compound literals are only allowed to contain 'field = value' elements", type_str);
+ gb_string_free(type_str);
+ } else {
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
+ }
+ break;
+ }
+
default: {
if (cl->elems.count == 0) {
@@ -9259,7 +10270,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (tav.mode != Addressing_Constant) {
continue;
}
- GB_ASSERT(tav.value.kind == ExactValue_Integer);
+ if (tav.value.kind != ExactValue_Integer) {
+ continue;
+ }
i64 v = big_int_to_i64(&tav.value.value_integer);
i64 lower = bt->BitSet.lower;
u64 index = cast(u64)(v-lower);
@@ -9629,8 +10642,6 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
// Okay
} else if (is_type_string(t)) {
// Okay
- } else if (is_type_relative_multi_pointer(t)) {
- // Okay
} else if (is_type_matrix(t)) {
// Okay
} else {
@@ -9673,23 +10684,30 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
if (is_const) {
if (index < 0) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot index a constant '%s'", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ if (!build_context.terse_errors) {
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ }
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
- } else if (ok) {
- ExactValue value = type_and_value_of_expr(ie->expr).value;
+ } else if (ok && !is_type_matrix(t)) {
+ TypeAndValue tav = type_and_value_of_expr(ie->expr);
+ ExactValue value = tav.value;
o->mode = Addressing_Constant;
bool success = false;
bool finish = false;
o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
if (!success) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ if (!build_context.terse_errors) {
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ }
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
@@ -9763,15 +10781,21 @@ 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;
- case Type_RelativeMultiPointer:
- valid = true;
- o->type = type_deref(o->type);
- break;
-
case Type_EnumeratedArray:
{
gbString str = expr_to_string(o->expr);
@@ -9848,16 +10872,6 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
x[i:n] -> []T
*/
o->type = alloc_type_slice(t->MultiPointer.elem);
- } else if (t->kind == Type_RelativeMultiPointer && se->high != nullptr) {
- /*
- x[:] -> [^]T
- x[i:] -> [^]T
- x[:n] -> []T
- x[i:n] -> []T
- */
- Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
- GB_ASSERT(pointer_type->kind == Type_MultiPointer);
- o->type = alloc_type_slice(pointer_type->MultiPointer.elem);
}
@@ -9875,9 +10889,12 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
}
}
if (!all_constant) {
+ ERROR_BLOCK();
gbString str = expr_to_string(o->expr);
error(o->expr, "Cannot slice '%s' with non-constant indices", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ if (!build_context.terse_errors) {
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ }
gb_string_free(str);
o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
o->expr = node;
@@ -9941,7 +10958,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case Token_context:
{
if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
- error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl);
+ error(node, "'context' is only allowed within procedures");
return kind;
}
if (unparen_expr(c->assignment_lhs_hint) == node) {
@@ -10064,6 +11081,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_end;
case_ast_node(re, OrReturnExpr, node);
+ node->viral_state_flags |= ViralStateFlag_ContainsOrReturn;
return check_or_return_expr(c, o, node, type_hint);
case_end;
@@ -10114,10 +11132,10 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
if (o->mode != Addressing_Invalid) {
switch (tc->token.kind) {
case Token_transmute:
- check_transmute(c, node, o, type);
+ check_transmute(c, node, o, type, true);
break;
case Token_cast:
- check_cast(c, o, type);
+ check_cast(c, o, type, true);
break;
default:
error(node, "Invalid AST: Invalid casting expression");
@@ -10214,34 +11232,18 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
} else if (t->kind == Type_SoaPointer) {
o->mode = Addressing_SoaVariable;
o->type = type_deref(t);
- } else if (t->kind == Type_RelativePointer) {
- if (o->mode != Addressing_Variable) {
- gbString str = expr_to_string(o->expr);
- gbString typ = type_to_string(o->type);
- error(o->expr, "Cannot dereference relative pointer '%s' of type '%s' as it does not have a variable addressing mode", str, typ);
- gb_string_free(typ);
- gb_string_free(str);
- }
-
- // NOTE(bill): This is required because when dereferencing, the original type has been lost
- add_type_info_type(c, o->type);
-
- Type *ptr_type = base_type(t->RelativePointer.pointer_type);
- GB_ASSERT(ptr_type->kind == Type_Pointer);
- o->mode = Addressing_Variable;
- o->type = ptr_type->Pointer.elem;
} else {
gbString str = expr_to_string(o->expr);
gbString typ = type_to_string(o->type);
- begin_error_block();
+ ERROR_BLOCK();
error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ);
if (o->type && is_type_multi_pointer(o->type)) {
- error_line("\tDid you mean '%s[0]'?\n", str);
+ if (!build_context.terse_errors) {
+ error_line("\tDid you mean '%s[0]'?\n", str);
+ }
}
- end_error_block();
-
gb_string_free(typ);
gb_string_free(str);
o->mode = Addressing_Invalid;
@@ -10298,6 +11300,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:
@@ -10762,6 +11765,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(pt, PointerType, node);
+ if (pt->tag) {
+ str = write_expr_to_string(str, pt->tag, false);
+ }
str = gb_string_append_rune(str, '^');
str = write_expr_to_string(str, pt->type, shorthand);
case_end;
@@ -10772,6 +11778,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(at, ArrayType, node);
+ if (at->tag) {
+ str = write_expr_to_string(str, at->tag, false);
+ }
str = gb_string_append_rune(str, '[');
if (at->count != nullptr &&
at->count->kind == Ast_UnaryExpr &&
@@ -10785,6 +11794,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(at, DynamicArrayType, node);
+ if (at->tag) {
+ str = write_expr_to_string(str, at->tag, false);
+ }
str = gb_string_appendc(str, "[dynamic]");
str = write_expr_to_string(str, at->elem, shorthand);
case_end;
@@ -10826,6 +11838,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
if (f->flags&FieldFlag_any_int) {
str = gb_string_appendc(str, "#any_int ");
}
+ if (f->flags&FieldFlag_no_broadcast) {
+ str = gb_string_appendc(str, "#no_broadcast ");
+ }
if (f->flags&FieldFlag_const) {
str = gb_string_appendc(str, "#const ");
}
@@ -10892,6 +11907,18 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
if (field->flags&FieldFlag_c_vararg) {
str = gb_string_appendc(str, "#c_vararg ");
}
+ if (field->flags&FieldFlag_any_int) {
+ str = gb_string_appendc(str, "#any_int ");
+ }
+ if (field->flags&FieldFlag_no_broadcast) {
+ str = gb_string_appendc(str, "#no_broadcast ");
+ }
+ if (field->flags&FieldFlag_const) {
+ str = gb_string_appendc(str, "#const ");
+ }
+ if (field->flags&FieldFlag_subtype) {
+ str = gb_string_appendc(str, "#subtype ");
+ }
str = write_expr_to_string(str, field->type, shorthand);
}
@@ -11035,6 +12062,32 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
+ case_ast_node(f, BitFieldField, node);
+ str = write_expr_to_string(str, f->name, shorthand);
+ str = gb_string_appendc(str, ": ");
+ str = write_expr_to_string(str, f->type, shorthand);
+ str = gb_string_appendc(str, " | ");
+ str = write_expr_to_string(str, f->bit_size, shorthand);
+ case_end;
+ case_ast_node(bf, BitFieldType, node);
+ str = gb_string_appendc(str, "bit_field ");
+ if (!shorthand) {
+ str = write_expr_to_string(str, bf->backing_type, shorthand);
+ }
+ str = gb_string_appendc(str, " {");
+ if (shorthand) {
+ str = gb_string_appendc(str, "...");
+ } else {
+ for_array(i, bf->fields) {
+ if (i > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+ str = write_expr_to_string(str, bf->fields[i], false);
+ }
+ }
+ str = gb_string_appendc(str, "}");
+ case_end;
+
case_ast_node(ia, InlineAsmExpr, node);
str = gb_string_appendc(str, "asm(");
for_array(i, ia->param_types) {