aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2022-02-15 15:47:24 +0000
committerGitHub <noreply@github.com>2022-02-15 15:47:24 +0000
commitf5697dd7f2e71411957faf9fa620e62363491083 (patch)
tree931db0d1c595326524bd7a541b0fb2c6d4a7f186 /src
parent6223f48c3fc096b543a51aa1c993a6f1127f2a0f (diff)
parenta23ee1edc16dc447b9e342ba56b5871e0424082e (diff)
Merge branch 'master' into odin-global-constants-as-enums
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp53
-rw-r--r--src/check_builtin.cpp43
-rw-r--r--src/check_decl.cpp91
-rw-r--r--src/check_expr.cpp3443
-rw-r--r--src/check_stmt.cpp108
-rw-r--r--src/check_type.cpp46
-rw-r--r--src/checker.cpp158
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/docs_format.cpp20
-rw-r--r--src/docs_writer.cpp62
-rw-r--r--src/entity.cpp6
-rw-r--r--src/error.cpp5
-rw-r--r--src/llvm_backend.hpp1
-rw-r--r--src/llvm_backend_const.cpp8
-rw-r--r--src/llvm_backend_debug.cpp71
-rw-r--r--src/llvm_backend_expr.cpp108
-rw-r--r--src/llvm_backend_general.cpp7
-rw-r--r--src/llvm_backend_proc.cpp9
-rw-r--r--src/llvm_backend_stmt.cpp7
-rw-r--r--src/llvm_backend_type.cpp15
-rw-r--r--src/llvm_backend_utility.cpp41
-rw-r--r--src/main.cpp3
-rw-r--r--src/parser.cpp130
-rw-r--r--src/parser.hpp19
-rw-r--r--src/parser_pos.cpp6
-rw-r--r--src/ptr_set.cpp9
-rw-r--r--src/string.cpp12
-rw-r--r--src/tokenizer.cpp8
-rw-r--r--src/types.cpp23
29 files changed, 2722 insertions, 1794 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index b4a934ec8..610e4f847 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -165,6 +165,13 @@ enum TimingsExportFormat : i32 {
TimingsExportCSV = 2,
};
+enum ErrorPosStyle {
+ ErrorPosStyle_Default, // path(line:column) msg
+ ErrorPosStyle_Unix, // path:line:column: msg
+
+ ErrorPosStyle_COUNT
+};
+
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
@@ -175,7 +182,9 @@ struct BuildContext {
String ODIN_ROOT; // Odin ROOT
bool ODIN_DEBUG; // Odin in debug mode
bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
-bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+
+ ErrorPosStyle ODIN_ERROR_POS_STYLE;
TargetEndianKind endian_kind;
@@ -254,6 +263,7 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil
isize thread_count;
PtrMap<char const *, ExactValue> defined_values;
+
};
@@ -843,6 +853,22 @@ bool has_asm_extension(String const &path) {
return false;
}
+// temporary
+char *token_pos_to_string(TokenPos const &pos) {
+ gbString s = gb_string_make_reserve(temporary_allocator(), 128);
+ String file = get_file_path_string(pos.file_id);
+ switch (build_context.ODIN_ERROR_POS_STYLE) {
+ default: /*fallthrough*/
+ case ErrorPosStyle_Default:
+ s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
+ break;
+ case ErrorPosStyle_Unix:
+ s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column);
+ break;
+ }
+ return s;
+}
+
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -855,6 +881,31 @@ void init_build_context(TargetMetrics *cross_target) {
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
+
+ {
+ char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator());
+ if (found) {
+ ErrorPosStyle kind = ErrorPosStyle_Default;
+ String style = make_string_c(found);
+ style = string_trim_whitespace(style);
+ if (style == "" || style == "default" || style == "odin") {
+ kind = ErrorPosStyle_Default;
+ } else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") {
+ kind = ErrorPosStyle_Unix;
+ } else {
+ gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style));
+ gb_printf_err("Valid formats:\n");
+ gb_printf_err("\t\"default\" or \"odin\"\n");
+ gb_printf_err("\t\tpath(line:column) message\n");
+ gb_printf_err("\t\"unix\"\n");
+ gb_printf_err("\t\tpath:line:column: message\n");
+ gb_exit(1);
+ }
+
+ build_context.ODIN_ERROR_POS_STYLE = kind;
+ }
+ }
+
bc->copy_file_contents = true;
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index a42741976..1fb3d6037 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -2183,9 +2183,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
operand->mode = Addressing_Value;
- if (is_type_array(t)) {
+ if (t->kind == Type_Array) {
+ i32 rank = type_math_rank(t);
// Do nothing
- operand->type = x.type;
+ operand->type = x.type;
+ if (rank > 2) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank);
+ gb_string_free(s);
+ return false;
+ } else if (rank == 2) {
+ Type *inner = base_type(t->Array.elem);
+ GB_ASSERT(inner->kind == Type_Array);
+ Type *elem = inner->Array.elem;
+ Type *array_inner = alloc_type_array(elem, t->Array.count);
+ Type *array_outer = alloc_type_array(array_inner, inner->Array.count);
+ operand->type = array_outer;
+
+ i64 elements = t->Array.count*inner->Array.count;
+ i64 size = type_size_of(operand->type);
+ if (!is_type_valid_for_matrix_elems(elem)) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s);
+ gb_string_free(s);
+ } else if (size > MATRIX_ELEMENT_MAX_SIZE) {
+ gbString s = type_to_string(x.type);
+ error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE);
+ gb_string_free(s);
+ }
+ }
} else {
GB_ASSERT(t->kind == Type_Matrix);
operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
@@ -3310,9 +3344,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_simple_compare:
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
+ case BuiltinProc_type_is_valid_matrix_elements:
case BuiltinProc_type_is_named:
case BuiltinProc_type_is_pointer:
case BuiltinProc_type_is_array:
+ case BuiltinProc_type_is_enumerated_array:
case BuiltinProc_type_is_slice:
case BuiltinProc_type_is_dynamic_array:
case BuiltinProc_type_is_map:
@@ -3320,10 +3356,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_union:
case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc:
- case BuiltinProc_type_is_bit_field:
- case BuiltinProc_type_is_bit_field_value:
case BuiltinProc_type_is_bit_set:
case BuiltinProc_type_is_simd_vector:
+ case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record:
case BuiltinProc_type_is_unspecialized_polymorphic_record:
case BuiltinProc_type_has_nil:
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index f9bc17ba4..f6dade812 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -238,6 +238,51 @@ isize total_attribute_count(DeclInfo *decl) {
return attribute_count;
}
+Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
+ // NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
+ //
+ // X :: enum {A, B, C}
+ // Y :: distinct X
+ //
+ // To make Y be just like X, it will need to copy the elements of X and change their type
+ // so that they match Y rather than X.
+ GB_ASSERT(original_enum_type != nullptr);
+ GB_ASSERT(named_type != nullptr);
+ GB_ASSERT(original_enum_type->kind == Type_Enum);
+ GB_ASSERT(named_type->kind == Type_Named);
+
+ Scope *parent = original_enum_type->Enum.scope->parent;
+ Scope *scope = create_scope(nullptr, parent);
+
+
+ Type *et = alloc_type_enum();
+ et->Enum.base_type = original_enum_type->Enum.base_type;
+ et->Enum.min_value = original_enum_type->Enum.min_value;
+ et->Enum.max_value = original_enum_type->Enum.max_value;
+ et->Enum.min_value_index = original_enum_type->Enum.min_value_index;
+ et->Enum.max_value_index = original_enum_type->Enum.max_value_index;
+ et->Enum.scope = scope;
+
+ auto fields = array_make<Entity *>(permanent_allocator(), original_enum_type->Enum.fields.count);
+ for_array(i, fields) {
+ Entity *old = original_enum_type->Enum.fields[i];
+
+ Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value);
+ e->file = old->file;
+ e->identifier = clone_ast(old->identifier);
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ e->Constant.flags = old->Constant.flags;
+ e->Constant.docs = old->Constant.docs;
+ e->Constant.comment = old->Constant.comment;
+
+ fields[i] = e;
+ add_entity(ctx, scope, nullptr, e);
+ add_entity_use(ctx, e->identifier, e);
+ }
+ et->Enum.fields = fields;
+ return et;
+}
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
@@ -258,7 +303,11 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
- named->Named.base = base_type(bt);
+ Type *base = base_type(bt);
+ if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) {
+ base = clone_enum_type(ctx, base, named);
+ }
+ named->Named.base = base;
if (is_distinct && is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
@@ -385,7 +434,45 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
Operand operand = {};
if (init != nullptr) {
- Entity *entity = nullptr;
+ Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
+ if (entity != nullptr && entity->kind == Entity_TypeName) {
+ // @TypeAliasingProblem
+ // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
+ // being "confused" as constants
+ //
+ // A :: B
+ // C :: proc "c" (^A)
+ // B :: struct {x: C}
+ //
+ // A gets evaluated first, and then checks B.
+ // B then checks C.
+ // C then tries to check A which is unresolved but thought to be a constant.
+ // Therefore within C's check, A errs as "not a type".
+ //
+ // This is because a const declaration may or may not be a type and this cannot
+ // be determined from a syntactical standpoint.
+ // This check allows the compiler to override the entity to be checked as a type.
+ //
+ // There is no problem if B is prefixed with the `#type` helper enforcing at
+ // both a syntax and semantic level that B must be a type.
+ //
+ // A :: #type B
+ //
+ // This approach is not fool proof and can fail in case such as:
+ //
+ // X :: type_of(x)
+ // X :: Foo(int).Type
+ //
+ // Since even these kind of declarations may cause weird checking cycles.
+ // For the time being, these are going to be treated as an unfortunate error
+ // until there is a proper delaying system to try declaration again if they
+ // have failed.
+
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
+ entity = nullptr;
if (init->kind == Ast_Ident) {
entity = check_ident(ctx, &operand, init, nullptr, e->type, true);
} else if (init->kind == Ast_SelectorExpr) {
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 8667d8734..3f31ac810 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -508,6 +508,10 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+ if (c == nullptr) {
+ GB_ASSERT(operand->mode == Addressing_Value);
+ GB_ASSERT(is_type_typed(operand->type));
+ }
if (operand->mode == Addressing_Invalid ||
type == t_invalid) {
return -1;
@@ -673,6 +677,42 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 1;
}
}
+
+ // TODO(bill): Determine which rule is a better on in practice
+ #if 1
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ return score+2;
+ }
+ }
+ #else
+ // NOTE(bill): check to see you can assign to it with one of the variants?
+ i64 prev_lowest_score = -1;
+ i64 lowest_score = -1;
+ for_array(i, dst->Union.variants) {
+ Type *vt = dst->Union.variants[i];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ if (lowest_score < 0) {
+ lowest_score = score;
+ } else {
+ if (prev_lowest_score < 0) {
+ prev_lowest_score = lowest_score;
+ } else {
+ prev_lowest_score = gb_min(prev_lowest_score, lowest_score);
+ }
+ lowest_score = gb_min(lowest_score, score);
+ }
+ }
+ }
+ if (lowest_score >= 0) {
+ if (prev_lowest_score != lowest_score) { // remove possible ambiguities
+ return lowest_score+2;
+ }
+ }
+ #endif
}
if (is_type_relative_pointer(dst)) {
@@ -782,6 +822,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
return check_is_assignable_to_with_score(c, operand, type, &score);
}
+bool internal_check_is_assignable_to(Type *src, Type *dst) {
+ Operand x = {};
+ x.type = src;
+ x.mode = Addressing_Value;
+ return check_is_assignable_to(nullptr, &x, dst);
+}
+
AstPackage *get_package_of_type(Type *type) {
for (;;) {
if (type == nullptr) {
@@ -1286,7 +1333,6 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
-
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;
@@ -1422,8 +1468,12 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
case Entity_TypeName:
o->mode = Addressing_Type;
if (check_cycle(c, e, true)) {
- type = t_invalid;
+ o->type = t_invalid;
}
+ if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ o->type = base_type(o->type);
+ }
+
break;
case Entity_ImportName:
@@ -3419,7 +3469,6 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
if (operand->value.kind == ExactValue_String) {
String key = operand->value.value_string;
if (is_type_string(operand->type) && is_type_enum(target_type)) {
- gb_printf_err("HERE!\n");
Type *et = base_type(target_type);
check_did_you_mean_type(key, et->Enum.fields, ".");
}
@@ -4065,6 +4114,101 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n
}
+bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) {
+ bool is_declared = entity != nullptr;
+ if (is_declared) {
+ 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;
+ } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
+ is_declared = false;
+ }
+ }
+ return is_declared;
+}
+
+// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
+Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
+ 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) {
+ ast_node(se, SelectorExpr, node);
+ if (se->token.kind == Token_ArrowRight) {
+ return nullptr;
+ }
+
+ Ast *op_expr = se->expr;
+ Ast *selector = unparen_expr(se->selector);
+ if (selector == nullptr) {
+ return nullptr;
+ }
+ if (selector->kind != Ast_Ident) {
+ return nullptr;
+ }
+
+ Entity *entity = nullptr;
+ Entity *expr_entity = nullptr;
+ bool check_op_expr = true;
+
+ if (op_expr->kind == Ast_Ident) {
+ String op_name = op_expr->Ident.token.string;
+ Entity *e = scope_lookup(c->scope, op_name);
+ if (e == nullptr) {
+ return nullptr;
+ }
+ add_entity_use(c, op_expr, e);
+ expr_entity = e;
+
+ 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
+ String import_name = op_name;
+ Scope *import_scope = e->ImportName.scope;
+ String entity_name = selector->Ident.token.string;
+
+ 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)) {
+ return nullptr;
+ }
+
+ check_entity_decl(c, entity, nullptr, nullptr);
+ if (entity->kind == Entity_ProcGroup) {
+ return entity;
+ }
+ GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
+ }
+ }
+
+ Operand operand = {};
+ if (check_op_expr) {
+ check_expr_base(c, &operand, op_expr, nullptr);
+ if (operand.mode == Addressing_Invalid) {
+ return nullptr;
+ }
+ }
+
+ if (entity == nullptr && selector->kind == Ast_Ident) {
+ String field_name = selector->Ident.token.string;
+ if (is_type_dynamic_array(type_deref(operand.type))) {
+ init_mem_allocator(c->checker);
+ }
+ auto sel = lookup_field(operand.type, field_name, operand.mode == Addressing_Type);
+ entity = sel.entity;
+ }
+
+ if (entity != nullptr) {
+ return entity;
+ }
+ }
+ return nullptr;
+}
+
+
Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
@@ -4113,18 +4257,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name);
- bool is_declared = entity != nullptr;
bool allow_builtin = false;
- if (is_declared) {
- 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;
- } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
- is_declared = false;
- }
- }
- if (!is_declared) {
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4214,7 +4348,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
}
- if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
+ if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
// TODO(bill): Simd_Vector swizzling
String field_name = selector->Ident.token.string;
@@ -5756,8 +5890,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -5770,6 +5908,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *e = entity_of_node(ident);
+
CallArgumentData data = {};
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
gb_unused(err);
@@ -5778,7 +5917,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (entity_to_use != nullptr) {
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
-
if (data.gen_entity != nullptr) {
Entity *e = data.gen_entity;
DeclInfo *decl = data.gen_entity->decl_info;
@@ -5790,8 +5928,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -6085,7 +6227,8 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
}
// NOTE(bill): Add type info the parameters
- add_type_info_type(c, o->type);
+ // TODO(bill, 2022-01-23): why was this line added in the first place? I'm commenting it out for the time being
+ // add_type_info_type(c, o->type);
}
{
@@ -6860,433 +7003,395 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ
}
-ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
- u32 prev_state_flags = c->state_flags;
- defer (c->state_flags = prev_state_flags);
- if (node->state_flags != 0) {
- u32 in = node->state_flags;
- u32 out = c->state_flags;
+struct TypeAndToken {
+ Type *type;
+ Token token;
+};
- if (in & StateFlag_no_bounds_check) {
- out |= StateFlag_no_bounds_check;
- out &= ~StateFlag_bounds_check;
- } else if (in & StateFlag_bounds_check) {
- out |= StateFlag_bounds_check;
- out &= ~StateFlag_no_bounds_check;
- }
+typedef PtrMap<uintptr, TypeAndToken> SeenMap;
- c->state_flags = out;
+void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) {
+ if (operand.mode != Addressing_Constant) {
+ return;
+ }
+ if (operand.value.kind == ExactValue_Invalid) {
+ return;
}
- ExprKind kind = Expr_Stmt;
-
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
-
- switch (node->kind) {
- default:
- return kind;
+ uintptr key = hash_exact_value(operand.value);
+ TypeAndToken *found = map_get(seen, key);
+ if (found != nullptr) {
+ isize count = multi_map_count(seen, key);
+ TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
- case_ast_node(be, BadExpr, node)
- return kind;
- case_end;
+ multi_map_get_all(seen, key, taps);
+ for (isize i = 0; i < count; i++) {
+ TypeAndToken tap = taps[i];
+ if (!are_types_identical(operand.type, tap.type)) {
+ continue;
+ }
- case_ast_node(i, Implicit, node)
- switch (i->kind) {
- 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);
- return kind;
- }
- if (unparen_expr(c->assignment_lhs_hint) == node) {
- c->scope->flags |= ScopeFlag_ContextDefined;
- }
+ TokenPos pos = tap.token.pos;
+ if (use_expr) {
+ gbString expr_str = expr_to_string(operand.expr);
+ error(operand.expr,
+ "Duplicate case '%s'\n"
+ "\tprevious case at %s",
+ expr_str,
+ token_pos_to_string(pos));
+ gb_string_free(expr_str);
+ } else {
+ error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
+ }
+ return;
+ }
+ }
- if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
- error(node, "'context' has not been defined within this scope");
- // Continue with value
- }
+ TypeAndToken tap = {operand.type, ast_token(operand.expr)};
+ multi_map_insert(seen, key, tap);
+}
- init_core_context(c->checker);
- o->mode = Addressing_Context;
- o->type = t_context;
- }
- break;
- default:
- error(node, "Illegal implicit name '%.*s'", LIT(i->string));
- return kind;
- }
- case_end;
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) {
+ if (is_type_enum(x.type)) {
+ // TODO(bill): Fix this logic so it's fast!!!
- case_ast_node(i, Ident, node);
- check_ident(c, o, node, nullptr, type_hint, false);
- case_end;
+ i64 v0 = exact_value_to_i64(lhs.value);
+ i64 v1 = exact_value_to_i64(rhs.value);
+ Operand v = {};
+ v.mode = Addressing_Constant;
+ v.type = x.type;
+ v.expr = x.expr;
- case_ast_node(u, Undef, node);
- o->mode = Addressing_Value;
- o->type = t_untyped_undef;
- case_end;
+ Type *bt = base_type(x.type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for (i64 vi = v0; vi <= v1; vi++) {
+ if (upper_op != Token_LtEq && vi == v1) {
+ break;
+ }
+ bool found = false;
+ for_array(j, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[j];
+ GB_ASSERT(f->kind == Entity_Constant);
- case_ast_node(bl, BasicLit, node);
- Type *t = t_invalid;
- switch (node->tav.value.kind) {
- case ExactValue_String: t = t_untyped_string; break;
- case ExactValue_Float: t = t_untyped_float; break;
- case ExactValue_Complex: t = t_untyped_complex; break;
- case ExactValue_Quaternion: t = t_untyped_quaternion; break;
- case ExactValue_Integer:
- t = t_untyped_integer;
- if (bl->token.kind == Token_Rune) {
- t = t_untyped_rune;
+ i64 fv = exact_value_to_i64(f->Constant.value);
+ if (fv == vi) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ v.value = exact_value_i64(vi);
+ add_constant_switch_case(ctx, seen, v);
}
- break;
- default:
- GB_PANIC("Unhandled value type for basic literal");
- break;
}
+ } else {
+ add_constant_switch_case(ctx, seen, lhs);
+ if (upper_op == Token_LtEq) {
+ add_constant_switch_case(ctx, seen, rhs);
+ }
+ }
+}
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) {
+ add_constant_switch_case(ctx, seen, x);
+}
- o->mode = Addressing_Constant;
- o->type = t;
- o->value = node->tav.value;
- case_end;
+ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(bd, BasicDirective, node);
- case_ast_node(bd, BasicDirective, node);
- o->mode = Addressing_Constant;
- String name = bd->name.string;
- if (name == "file") {
+ ExprKind kind = Expr_Expr;
+
+ o->mode = Addressing_Constant;
+ String name = bd->name.string;
+ if (name == "file") {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ } else if (name == "line") {
+ o->type = t_untyped_integer;
+ o->value = exact_value_i64(bd->token.pos.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(get_file_path_string(bd->token.pos.file_id));
- } else if (name == "line") {
- o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.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 {
- o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
- }
- } else if (name == "caller_location") {
+ o->value = exact_value_string(str_lit(""));
+ } else {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(c->proc_name);
+ }
+ } 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 == "location") {
init_core_source_code_location(c->checker);
- error(node, "#caller_location may only be used as a default argument parameter");
+ error(node, "'#%.*s' must be used in a call expression", LIT(name));
o->type = t_source_code_location;
o->mode = Addressing_Value;
+ } else if (
+ name == "assert" ||
+ name == "defined" ||
+ name == "config" ||
+ name == "load" ||
+ name == "load_hash" ||
+ name == "load_or"
+ ) {
+ error(node, "'#%.*s' must be used as a call", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
} else {
- if (name == "location") {
- init_core_source_code_location(c->checker);
- error(node, "'#%.*s' must be used in a call expression", LIT(name));
- o->type = t_source_code_location;
- o->mode = Addressing_Value;
- } else if (
- name == "assert" ||
- name == "defined" ||
- name == "config" ||
- name == "load" ||
- name == "load_hash" ||
- name == "load_or"
- ) {
- error(node, "'#%.*s' must be used as a call", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- } else {
- error(node, "Unknown directive: #%.*s", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- }
-
+ error(node, "Unknown directive: #%.*s", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
}
- case_end;
- case_ast_node(pg, ProcGroup, node);
- error(node, "Illegal use of a procedure group");
- o->mode = Addressing_Invalid;
- case_end;
+ }
+ return kind;
+}
- case_ast_node(pl, ProcLit, node);
- CheckerContext ctx = *c;
+ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {Addressing_Invalid};
+ ast_node(te, TernaryIfExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
- DeclInfo *decl = nullptr;
- Type *type = alloc_type(Type_Proc);
- check_open_scope(&ctx, pl->type);
- {
- decl = make_decl_info(ctx.scope, ctx.decl);
- decl->proc_lit = node;
- ctx.decl = decl;
- defer (ctx.decl = ctx.decl->parent);
+ if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
+ error(te->cond, "Non-boolean condition in ternary if expression");
+ }
- if (pl->tags != 0) {
- error(node, "A procedure literal cannot have tags");
- pl->tags = 0; // TODO(bill): Should I zero this?!
- }
+ Operand x = {Addressing_Invalid};
+ Operand y = {Addressing_Invalid};
+ check_expr_or_type(c, &x, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
- check_procedure_type(&ctx, type, pl->type);
- if (!is_type_proc(type)) {
- gbString str = expr_to_string(node);
- error(node, "Invalid procedure literal '%s'", str);
- gb_string_free(str);
- check_close_scope(&ctx);
- return kind;
- }
+ if (te->y != nullptr) {
+ check_expr_or_type(c, &y, te->y, type_hint);
+ node->viral_state_flags |= te->y->viral_state_flags;
+ } else {
+ error(node, "A ternary expression must have an else clause");
+ return kind;
+ }
- if (pl->body == nullptr) {
- error(node, "A procedure literal must have a body");
- return kind;
- }
+ if (x.type == nullptr || x.type == t_invalid ||
+ y.type == nullptr || y.type == t_invalid) {
+ return kind;
+ }
- pl->decl = decl;
- check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags);
- }
- check_close_scope(&ctx);
+ convert_to_typed(c, &x, y.type);
+ if (x.mode == Addressing_Invalid) {
+ return kind;
+ }
+ convert_to_typed(c, &y, x.type);
+ if (y.mode == Addressing_Invalid) {
+ x.mode = Addressing_Invalid;
+ return kind;
+ }
- o->mode = Addressing_Value;
- o->type = type;
- case_end;
+ if (!ternary_compare_types(x.type, y.type)) {
+ gbString its = type_to_string(x.type);
+ gbString ets = type_to_string(y.type);
+ error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
+ gb_string_free(ets);
+ gb_string_free(its);
+ return kind;
+ }
- case_ast_node(te, TernaryIfExpr, node);
- Operand cond = {Addressing_Invalid};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
+ o->type = x.type;
+ if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+ o->type = y.type;
+ }
- if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
- error(te->cond, "Non-boolean condition in ternary if expression");
+ o->mode = Addressing_Value;
+ o->expr = node;
+ if (type_hint != nullptr && is_type_untyped(o->type)) {
+ if (check_cast_internal(c, &x, type_hint) &&
+ check_cast_internal(c, &y, type_hint)) {
+ convert_to_typed(c, o, type_hint);
+ update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
}
+ }
+ return kind;
+}
- Operand x = {Addressing_Invalid};
- Operand y = {Addressing_Invalid};
- check_expr_or_type(c, &x, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
+ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {};
+ ast_node(te, TernaryWhenExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
+ if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
+ error(te->cond, "Expected a constant boolean condition in ternary when expression");
+ return kind;
+ }
+
+ if (cond.value.value_bool) {
+ check_expr_or_type(c, o, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
+ } else {
if (te->y != nullptr) {
- check_expr_or_type(c, &y, te->y, type_hint);
+ check_expr_or_type(c, o, te->y, type_hint);
node->viral_state_flags |= te->y->viral_state_flags;
} else {
- error(node, "A ternary expression must have an else clause");
- return kind;
- }
-
- if (x.type == nullptr || x.type == t_invalid ||
- y.type == nullptr || y.type == t_invalid) {
- return kind;
- }
-
- convert_to_typed(c, &x, y.type);
- if (x.mode == Addressing_Invalid) {
- return kind;
- }
- convert_to_typed(c, &y, x.type);
- if (y.mode == Addressing_Invalid) {
- x.mode = Addressing_Invalid;
+ error(node, "A ternary when expression must have an else clause");
return kind;
}
+ }
+ return kind;
+}
- if (!ternary_compare_types(x.type, y.type)) {
- gbString its = type_to_string(x.type);
- gbString ets = type_to_string(y.type);
- error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
- gb_string_free(ets);
- gb_string_free(its);
- return kind;
- }
+ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(oe, OrElseExpr, node);
- o->type = x.type;
- if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
- o->type = y.type;
- }
+ String name = oe->token.string;
+ Ast *arg = oe->x;
+ Ast *default_value = oe->y;
+ Operand x = {};
+ Operand y = {};
+ check_multi_expr_with_type_hint(c, &x, arg, type_hint);
+ if (x.mode == Addressing_Invalid) {
o->mode = Addressing_Value;
+ o->type = t_invalid;
o->expr = node;
- if (type_hint != nullptr && is_type_untyped(o->type)) {
- if (check_cast_internal(c, &x, type_hint) &&
- check_cast_internal(c, &y, type_hint)) {
- convert_to_typed(c, o, type_hint);
- update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
- }
- }
- case_end;
-
- case_ast_node(te, TernaryWhenExpr, node);
- Operand cond = {};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
-
- if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
- error(te->cond, "Expected a constant boolean condition in ternary when expression");
- return kind;
- }
-
- if (cond.value.value_bool) {
- check_expr_or_type(c, o, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
- } else {
- if (te->y != nullptr) {
- check_expr_or_type(c, o, te->y, type_hint);
- node->viral_state_flags |= te->y->viral_state_flags;
- } else {
- error(node, "A ternary when expression must have an else clause");
- return kind;
- }
- }
- case_end;
+ return Expr_Expr;
+ }
- case_ast_node(oe, OrElseExpr, node);
- String name = oe->token.string;
- Ast *arg = oe->x;
- Ast *default_value = oe->y;
+ check_multi_expr_with_type_hint(c, &y, default_value, x.type);
+ error_operand_no_value(&y);
+ if (y.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
- Operand x = {};
- Operand y = {};
- check_multi_expr_with_type_hint(c, &x, arg, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
- check_multi_expr_with_type_hint(c, &y, default_value, x.type);
- error_operand_no_value(&y);
- if (y.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
+ if (left_type != nullptr) {
+ check_assignment(c, &y, left_type, name);
+ } else {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ }
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
+ if (left_type == nullptr) {
+ left_type = t_invalid;
+ }
+ o->mode = Addressing_Value;
+ o->type = left_type;
+ o->expr = node;
+ return Expr_Expr;
+}
- if (left_type != nullptr) {
- check_assignment(c, &y, left_type, name);
- } else {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
- }
+ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(re, OrReturnExpr, node);
- if (left_type == nullptr) {
- left_type = t_invalid;
- }
+ String name = re->token.string;
+ Operand x = {};
+ check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
+ if (x.mode == Addressing_Invalid) {
o->mode = Addressing_Value;
- o->type = left_type;
+ o->type = t_invalid;
o->expr = node;
return Expr_Expr;
- case_end;
-
- case_ast_node(re, OrReturnExpr, node);
- String name = re->token.string;
- Operand x = {};
- check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
+ }
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_return_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_return_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
- if (right_type == nullptr) {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
+ if (right_type == nullptr) {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ } else {
+ Type *proc_type = base_type(c->curr_proc_sig);
+ GB_ASSERT(proc_type->kind == Type_Proc);
+ Type *result_type = proc_type->Proc.results;
+ if (result_type == nullptr) {
+ error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
} else {
- Type *proc_type = base_type(c->curr_proc_sig);
- GB_ASSERT(proc_type->kind == Type_Proc);
- Type *result_type = proc_type->Proc.results;
- if (result_type == nullptr) {
- error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
- } else {
- GB_ASSERT(result_type->kind == Type_Tuple);
+ GB_ASSERT(result_type->kind == Type_Tuple);
- auto const &vars = result_type->Tuple.variables;
- Type *end_type = vars[vars.count-1]->type;
+ auto const &vars = result_type->Tuple.variables;
+ Type *end_type = vars[vars.count-1]->type;
- if (vars.count > 1) {
- if (!proc_type->Proc.has_named_results) {
- error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
- }
+ if (vars.count > 1) {
+ if (!proc_type->Proc.has_named_results) {
+ error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
}
+ }
- Operand rhs = {};
- rhs.type = right_type;
- rhs.mode = Addressing_Value;
+ Operand rhs = {};
+ rhs.type = right_type;
+ rhs.mode = Addressing_Value;
- // TODO(bill): better error message
- if (!check_is_assignable_to(c, &rhs, end_type)) {
- gbString a = type_to_string(right_type);
- gbString b = type_to_string(end_type);
- gbString ret_type = type_to_string(result_type);
- error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
- if (vars.count == 1) {
- error_line("\tProcedure return value type: %s\n", ret_type);
- } else {
- error_line("\tProcedure return value types: (%s)\n", ret_type);
- }
- gb_string_free(ret_type);
- gb_string_free(b);
- gb_string_free(a);
+ // TODO(bill): better error message
+ if (!check_is_assignable_to(c, &rhs, end_type)) {
+ gbString a = type_to_string(right_type);
+ gbString b = type_to_string(end_type);
+ gbString ret_type = type_to_string(result_type);
+ error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
+ if (vars.count == 1) {
+ error_line("\tProcedure return value type: %s\n", ret_type);
+ } else {
+ error_line("\tProcedure return value types: (%s)\n", ret_type);
}
+ gb_string_free(ret_type);
+ gb_string_free(b);
+ gb_string_free(a);
}
}
+ }
- o->expr = node;
- o->type = left_type;
- if (left_type != nullptr) {
- o->mode = Addressing_Value;
- } else {
- o->mode = Addressing_NoValue;
- }
+ o->expr = node;
+ o->type = left_type;
+ if (left_type != nullptr) {
+ o->mode = Addressing_Value;
+ } else {
+ o->mode = Addressing_NoValue;
+ }
- if (c->curr_proc_sig == nullptr) {
- error(node, "'%.*s' can only be used within a procedure", LIT(name));
- }
-
- if (c->in_defer) {
- error(node, "'or_return' cannot be used within a defer statement");
- }
+ if (c->curr_proc_sig == nullptr) {
+ error(node, "'%.*s' can only be used within a procedure", LIT(name));
+ }
- return Expr_Expr;
- case_end;
+ if (c->in_defer) {
+ error(node, "'or_return' cannot be used within a defer statement");
+ }
- case_ast_node(cl, CompoundLit, node);
- Type *type = type_hint;
- if (type != nullptr && is_type_untyped(type)) {
- type = nullptr;
- }
- bool is_to_be_determined_array_count = false;
- bool is_constant = true;
- if (cl->type != 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 (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
- GB_ASSERT(tag->kind == Ast_BasicDirective);
- String name = tag->BasicDirective.name.string;
- if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
- }
- }
- }
- }
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
- if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
+ return Expr_Expr;
+}
+
+ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(cl, CompoundLit, node);
+
+ Type *type = type_hint;
+ if (type != nullptr && is_type_untyped(type)) {
+ type = nullptr;
+ }
+ bool is_to_be_determined_array_count = false;
+ bool is_constant = true;
+ if (cl->type != 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 (cl->elems.count > 0) {
+ if (cl->type->ArrayType.tag != nullptr) {
+ Ast *tag = cl->type->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name.string;
if (name == "soa") {
@@ -7295,472 +7400,268 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
}
}
-
- if (type == nullptr) {
- type = check_type(c, cl->type);
+ }
+ if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ if (cl->elems.count > 0) {
+ Ast *tag = cl->type->DynamicArrayType.tag;
+ GB_ASSERT(tag->kind == Ast_BasicDirective);
+ String name = tag->BasicDirective.name.string;
+ if (name == "soa") {
+ error(node, "#soa arrays are not supported for compound literals");
+ return kind;
+ }
}
}
if (type == nullptr) {
- error(node, "Missing type in compound literal");
- return kind;
+ type = check_type(c, cl->type);
}
+ }
+ if (type == nullptr) {
+ error(node, "Missing type in compound literal");
+ return kind;
+ }
- Type *t = base_type(type);
- if (is_type_polymorphic(t)) {
- gbString str = type_to_string(type);
- error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
- o->expr = node;
- o->type = type;
- gb_string_free(str);
- return kind;
- }
+ Type *t = base_type(type);
+ if (is_type_polymorphic(t)) {
+ gbString str = type_to_string(type);
+ error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
+ o->expr = node;
+ o->type = type;
+ gb_string_free(str);
+ return kind;
+ }
- switch (t->kind) {
- case Type_Struct: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- if (t->Struct.is_raw_union) {
- if (cl->elems.count > 0) {
- // NOTE: unions cannot be constant
- is_constant = false;
- if (cl->elems[0]->kind != Ast_FieldValue) {
+ switch (t->kind) {
+ case Type_Struct: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ if (t->Struct.is_raw_union) {
+ if (cl->elems.count > 0) {
+ // NOTE: unions cannot be constant
+ is_constant = false;
+
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
+ gb_string_free(type_str);
+ } else {
+ if (cl->elems.count != 1) {
gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
gb_string_free(type_str);
} else {
- if (cl->elems.count != 1) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
- gb_string_free(type_str);
- } else {
- Ast *elem = cl->elems[0];
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- break;
- }
-
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- break;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- break;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ Ast *elem = cl->elems[0];
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ break;
}
- }
- }
- break;
- }
-
-
- isize field_count = t->Struct.fields.count;
- isize min_field_count = t->Struct.fields.count;
- for (isize i = min_field_count-1; i >= 0; i--) {
- Entity *e = t->Struct.fields[i];
- GB_ASSERT(e->kind == Entity_Variable);
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- min_field_count--;
- } else {
- break;
- }
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- continue;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- if (fields_visited[sel.index[0]]) {
- error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
- continue;
- }
+ String name = fv->field->Ident.token.string;
- fields_visited[sel.index[0]] = true;
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ break;
+ }
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ break;
+ }
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- } else {
- bool seen_field_value = false;
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
- for_array(index, cl->elems) {
- Entity *field = nullptr;
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- seen_field_value = true;
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- } else if (seen_field_value) {
- error(elem, "Value elements cannot be used after a 'field = value'");
- continue;
- }
- if (index >= field_count) {
- error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- break;
- }
- if (field == nullptr) {
- field = t->Struct.fields[index];
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
}
- Operand o = {};
- check_expr_or_type(c, &o, elem, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- if (cl->elems.count < field_count) {
- if (min_field_count < field_count) {
- if (cl->elems.count < min_field_count) {
- error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
- }
- } else {
- error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- }
}
}
-
break;
}
- case Type_Slice:
- case Type_Array:
- case Type_DynamicArray:
- case Type_SimdVector:
- case Type_Matrix:
- {
- Type *elem_type = nullptr;
- String context_name = {};
- i64 max_type_count = -1;
- if (t->kind == Type_Slice) {
- elem_type = t->Slice.elem;
- context_name = str_lit("slice literal");
- } else if (t->kind == Type_Array) {
- elem_type = t->Array.elem;
- context_name = str_lit("array literal");
- if (!is_to_be_determined_array_count) {
- max_type_count = t->Array.count;
- }
- } else if (t->kind == Type_DynamicArray) {
- 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");
- max_type_count = t->SimdVector.count;
- } else if (t->kind == Type_Matrix) {
- elem_type = t->Matrix.elem;
- context_name = str_lit("matrix literal");
- max_type_count = t->Matrix.row_count*t->Matrix.column_count;
+ 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--) {
+ Entity *e = t->Struct.fields[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ min_field_count--;
} else {
- GB_PANIC("unreachable");
- }
-
-
- i64 max = 0;
-
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
-
- if (bet == t_invalid) {
break;
}
+ }
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
- if (is_type_simd_vector(t)) {
- error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
- } else {
- RangeCache rc = range_cache_make(heap_allocator());
- defer (range_cache_destroy(&rc));
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- 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 (is_ast_range(fv->field)) {
- Token op = fv->field->BinaryExpr.op;
-
- Operand x = {};
- Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr);
- if (!ok) {
- continue;
- }
- if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
- error(x.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
- error(y.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- i64 lo = exact_value_to_i64(x.value);
- i64 hi = exact_value_to_i64(y.value);
- i64 max_index = hi;
- if (op.kind == Token_RangeHalf) { // ..< (exclusive)
- hi -= 1;
- } else { // .. (inclusive)
- max_index += 1;
- }
-
- bool new_range = range_cache_add_range(&rc, lo, hi);
- if (!new_range) {
- error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
- continue;
- }
-
-
- if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
- continue;
- }
- if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
- continue;
- }
-
- if (max < hi) {
- max = max_index;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- } else {
- Operand op_index = {};
- check_expr(c, &op_index, fv->field);
-
- if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
- error(elem, "Expected a constant integer as an array field");
- continue;
- }
- // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
-
- i64 index = exact_value_to_i64(op_index.value);
-
- if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
- continue;
- }
-
- bool new_index = range_cache_add_index(&rc, index);
- if (!new_index) {
- error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
- continue;
- }
-
- if (max < index+1) {
- max = index+1;
- }
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- }
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
- cl->max_count = max;
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ continue;
}
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
+ if (fields_visited[sel.index[0]]) {
+ error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
+ fields_visited[sel.index[0]] = true;
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
- is_constant = is_constant && operand.mode == Addressing_Constant;
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
}
-
- if (max < index) {
- max = index;
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- }
-
- if (t->kind == Type_Array) {
- if (is_to_be_determined_array_count) {
- t->Array.count = max;
- } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->Array.count) {
- error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
- }
- }
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
}
+ } else {
+ bool seen_field_value = false;
+
+ for_array(index, cl->elems) {
+ Entity *field = nullptr;
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ seen_field_value = true;
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ } else if (seen_field_value) {
+ error(elem, "Value elements cannot be used after a 'field = value'");
+ continue;
+ }
+ if (index >= field_count) {
+ error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ break;
+ }
-
- if (t->kind == Type_SimdVector) {
- if (!is_constant) {
- error(node, "Expected all constant elements for a simd vector");
+ if (field == nullptr) {
+ field = t->Struct.fields[index];
}
- }
+ Operand o = {};
+ check_expr_or_type(c, &o, elem, field->type);
- 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 (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
}
- }
- if (t->kind == Type_Matrix) {
- if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < max_type_count) {
- error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max);
- }
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ if (cl->elems.count < field_count) {
+ if (min_field_count < field_count) {
+ if (cl->elems.count < min_field_count) {
+ error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
+ }
+ } else {
+ error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
}
}
-
- break;
}
- case Type_EnumeratedArray:
- {
- Type *elem_type = t->EnumeratedArray.elem;
- Type *index_type = t->EnumeratedArray.index;
- String context_name = str_lit("enumerated array literal");
- i64 max_type_count = t->EnumeratedArray.count;
+ break;
+ }
- gbString index_type_str = type_to_string(index_type);
- defer (gb_string_free(index_type_str));
+ case Type_Slice:
+ case Type_Array:
+ case Type_DynamicArray:
+ case Type_SimdVector:
+ case Type_Matrix:
+ {
+ Type *elem_type = nullptr;
+ String context_name = {};
+ i64 max_type_count = -1;
+ if (t->kind == Type_Slice) {
+ elem_type = t->Slice.elem;
+ context_name = str_lit("slice literal");
+ } else if (t->kind == Type_Array) {
+ elem_type = t->Array.elem;
+ context_name = str_lit("array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Array.count;
+ }
+ } else if (t->kind == Type_DynamicArray) {
+ elem_type = t->DynamicArray.elem;
+ context_name = str_lit("dynamic array literal");
+ is_constant = false;
- i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
- i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
+ 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");
+ max_type_count = t->SimdVector.count;
+ } else if (t->kind == Type_Matrix) {
+ elem_type = t->Matrix.elem;
+ context_name = str_lit("matrix literal");
+ max_type_count = t->Matrix.row_count*t->Matrix.column_count;
+ } else {
+ GB_PANIC("unreachable");
+ }
- String total_lo_string = {};
- String total_hi_string = {};
- GB_ASSERT(is_type_enum(index_type));
- {
- Type *bt = base_type(index_type);
- GB_ASSERT(bt->kind == Type_Enum);
- for_array(i, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[i];
- if (f->kind != Entity_Constant) {
- continue;
- }
- if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
- total_lo_string = f->token.string;
- }
- if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
- total_hi_string = f->token.string;
- }
- if (total_lo_string.len != 0 && total_hi_string.len != 0) {
- break;
- }
- }
- }
- i64 max = 0;
+ i64 max = 0;
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
- if (bet == t_invalid) {
- break;
- }
+ if (bet == t_invalid) {
+ break;
+ }
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ if (is_type_simd_vector(t)) {
+ error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
+ } else {
RangeCache rc = range_cache_make(heap_allocator());
defer (range_cache_destroy(&rc));
@@ -7777,49 +7678,42 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
Operand x = {};
Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ bool ok = check_range(c, fv->field, &x, &y, nullptr);
if (!ok) {
continue;
}
- if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
+ error(x.expr, "Expected a constant integer as an array field");
continue;
}
- if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
+ error(y.expr, "Expected a constant integer as an array field");
continue;
}
i64 lo = exact_value_to_i64(x.value);
i64 hi = exact_value_to_i64(y.value);
i64 max_index = hi;
- if (op.kind == Token_RangeHalf) {
+ if (op.kind == Token_RangeHalf) { // ..< (exclusive)
hi -= 1;
+ } else { // .. (inclusive)
+ max_index += 1;
}
bool new_range = range_cache_add_range(&rc, lo, hi);
if (!new_range) {
- gbString lo_str = expr_to_string(x.expr);
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
- gb_string_free(hi_str);
- gb_string_free(lo_str);
+ error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
continue;
}
- // NOTE(bill): These are sanity checks for invalid enum values
- if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
- gbString lo_str = expr_to_string(x.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(lo_str);
+ if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
continue;
}
- if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(hi_str);
+ if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
continue;
}
@@ -7834,27 +7728,24 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
is_constant = is_constant && operand.mode == Addressing_Constant;
} else {
Operand op_index = {};
- check_expr_with_type_hint(c, &op_index, fv->field, index_type);
+ check_expr(c, &op_index, fv->field);
- if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
- error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
+ error(elem, "Expected a constant integer as an array field");
continue;
}
+ // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
i64 index = exact_value_to_i64(op_index.value);
- if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(idx_str);
+ if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
continue;
}
bool new_index = range_cache_add_index(&rc, index);
if (!new_index) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
- gb_string_free(idx_str);
+ error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
continue;
}
@@ -7871,951 +7762,1429 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
cl->max_count = max;
+ }
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
+ }
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- if (max < index) {
- max = index;
+ if (t->kind == Type_Array) {
+ if (is_to_be_determined_array_count) {
+ t->Array.count = max;
+ } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->Array.count) {
+ error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
}
}
+ }
+
+ if (t->kind == Type_SimdVector) {
+ if (!is_constant) {
+ error(node, "Expected all constant elements for a simd vector");
+ }
+ }
+
+
+ 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 (t->kind == Type_Matrix) {
if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->EnumeratedArray.count) {
- error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
- } else {
- error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
+ if (0 < max && max < max_type_count) {
+ error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max);
}
}
+ }
+ break;
+ }
+
+ case Type_EnumeratedArray:
+ {
+ Type *elem_type = t->EnumeratedArray.elem;
+ Type *index_type = t->EnumeratedArray.index;
+ String context_name = str_lit("enumerated array literal");
+ i64 max_type_count = t->EnumeratedArray.count;
+
+ gbString index_type_str = type_to_string(index_type);
+ defer (gb_string_free(index_type_str));
+
+ i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
+ i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
+
+ String total_lo_string = {};
+ String total_hi_string = {};
+ GB_ASSERT(is_type_enum(index_type));
+ {
+ Type *bt = base_type(index_type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for_array(i, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
+ total_lo_string = f->token.string;
+ }
+ if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
+ total_hi_string = f->token.string;
+ }
+ if (total_lo_string.len != 0 && total_hi_string.len != 0) {
+ break;
+ }
+ }
+ }
+
+ i64 max = 0;
+
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
+
+ if (bet == t_invalid) {
break;
}
+ bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial");
+
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ map_init(&seen, heap_allocator());
+ defer (map_destroy(&seen));
+
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ RangeCache rc = range_cache_make(heap_allocator());
+ defer (range_cache_destroy(&rc));
- case Type_Basic: {
- if (!is_type_any(t)) {
- if (cl->elems.count != 0) {
- error(node, "Illegal compound literal");
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
}
- break;
- }
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- { // Checker values
- Type *field_types[2] = {t_rawptr, t_typeid};
- isize field_count = 2;
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool fields_visited[2] = {};
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
+ ast_node(fv, FieldValue, elem);
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- if (sel.entity == nullptr) {
- error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
+ if (is_ast_range(fv->field)) {
+ Token op = fv->field->BinaryExpr.op;
- isize index = sel.index[0];
+ Operand x = {};
+ Operand y = {};
+ bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ if (!ok) {
+ continue;
+ }
+ if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
- if (fields_visited[index]) {
- error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
+ if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
- fields_visited[index] = true;
- check_expr(c, o, fv->value);
+ i64 lo = exact_value_to_i64(x.value);
+ i64 hi = exact_value_to_i64(y.value);
+ i64 max_index = hi;
+ if (op.kind == Token_RangeHalf) {
+ hi -= 1;
+ }
+
+ bool new_range = range_cache_add_range(&rc, lo, hi);
+ if (!new_range) {
+ gbString lo_str = expr_to_string(x.expr);
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
+ gb_string_free(hi_str);
+ gb_string_free(lo_str);
+ continue;
+ }
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ // NOTE(bill): These are sanity checks for invalid enum values
+ if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
+ gbString lo_str = expr_to_string(x.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(lo_str);
+ continue;
}
+ if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(hi_str);
+ continue;
+ }
+
+ if (max < hi) {
+ max = max_index;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ TokenKind upper_op = Token_LtEq;
+ if (op.kind == Token_RangeHalf) {
+ upper_op = Token_Lt;
+ }
+ add_to_seen_map(c, &seen, upper_op, x, x, y);
} else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
+ Operand op_index = {};
+ check_expr_with_type_hint(c, &op_index, fv->field, index_type);
+ if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
+ error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
- check_expr(c, o, elem);
- if (index >= field_count) {
- error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
- break;
- }
+ i64 index = exact_value_to_i64(op_index.value);
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
+ if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
+ }
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ bool new_index = range_cache_add_index(&rc, index);
+ if (!new_index) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
}
- if (cl->elems.count < field_count) {
- error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
+
+ if (max < index+1) {
+ max = index+1;
}
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ add_to_seen_map(c, &seen, op_index);
}
}
- break;
+ cl->max_count = max;
+
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
+
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
}
- case Type_Map: {
- if (cl->elems.count == 0) {
- break;
+ bool was_error = false;
+ if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->EnumeratedArray.count) {
+ error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
+ was_error = true;
+ } else {
+ error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
+ was_error = true;
}
- is_constant = false;
- { // Checker values
- bool key_is_typeid = is_type_typeid(t->Map.key);
- bool value_is_typeid = is_type_typeid(t->Map.value);
+ }
+
+ // NOTE(bill): Check for missing cases when `#partial literal` is not present
+ if (cl->elems.count > 0 && !was_error && !is_partial) {
+ Type *et = base_type(index_type);
+ GB_ASSERT(et->kind == Type_Enum);
+ auto fields = et->Enum.fields;
+
+ auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count);
+
+ for_array(i, fields) {
+ Entity *f = fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ ExactValue v = f->Constant.value;
+ auto found = map_get(&seen, hash_exact_value(v));
+ if (!found) {
+ array_add(&unhandled, f);
+ }
+ }
+
+ if (unhandled.count > 0) {
+ begin_error_block();
+ defer (end_error_block());
+
+ if (unhandled.count == 1) {
+ error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string));
+ } else {
+ error(node, "Unhandled enumerated array cases:");
+ for_array(i, unhandled) {
+ Entity *f = unhandled[i];
+ error_line("\t%.*s\n", LIT(f->token.string));
+ }
+ }
+ error_line("\n");
+
+ error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ }
+ }
+
+ break;
+ }
+
+ case Type_Basic: {
+ if (!is_type_any(t)) {
+ if (cl->elems.count != 0) {
+ error(node, "Illegal compound literal");
+ }
+ break;
+ }
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ { // Checker values
+ Type *field_types[2] = {t_rawptr, t_typeid};
+ isize field_count = 2;
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool fields_visited[2] = {};
for_array(i, cl->elems) {
Ast *elem = cl->elems[i];
if (elem->kind != Ast_FieldValue) {
- error(elem, "Only 'field = value' elements are allowed in a map literal");
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
continue;
}
ast_node(fv, FieldValue, elem);
-
- if (key_is_typeid) {
- check_expr_or_type(c, o, fv->field, t->Map.key);
- } else {
- check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
}
- check_assignment(c, o, t->Map.key, str_lit("map literal"));
- if (o->mode == Addressing_Invalid) {
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ if (sel.entity == nullptr) {
+ error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
continue;
}
- if (value_is_typeid) {
- check_expr_or_type(c, o, fv->value, t->Map.value);
- } else {
- check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ isize index = sel.index[0];
+
+ if (fields_visited[index]) {
+ error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
+ continue;
}
- check_assignment(c, o, t->Map.value, str_lit("map literal"));
- }
- }
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
- add_package_dependency(c, "runtime", "__dynamic_map_reserve");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- }
- break;
- }
+ fields_visited[index] = true;
+ check_expr(c, o, fv->value);
- case Type_BitSet: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- Type *et = base_type(t->BitSet.elem);
- isize field_count = 0;
- if (et->kind == Type_Enum) {
- field_count = et->Enum.fields.count;
- }
+ // NOTE(bill): 'any' literals can never be constant
+ 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;
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
} else {
for_array(index, cl->elems) {
Ast *elem = cl->elems[index];
if (elem->kind == Ast_FieldValue) {
- error(elem, "'field = value' in a bit_set a literal is not allowed");
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
continue;
}
- check_expr_with_type_hint(c, o, elem, et);
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
+ check_expr(c, o, elem);
+ if (index >= field_count) {
+ error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
+ break;
}
- check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
- if (o->mode == Addressing_Constant) {
- i64 lower = t->BitSet.lower;
- i64 upper = t->BitSet.upper;
- i64 v = exact_value_to_i64(o->value);
- 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);
- continue;
- }
- }
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
+
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
+ if (cl->elems.count < field_count) {
+ error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
}
}
+ }
+
+ break;
+ }
+
+ case Type_Map: {
+ if (cl->elems.count == 0) {
break;
}
+ is_constant = false;
+ { // Checker values
+ bool key_is_typeid = is_type_typeid(t->Map.key);
+ bool value_is_typeid = is_type_typeid(t->Map.value);
- default: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Only 'field = value' elements are allowed in a map literal");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (key_is_typeid) {
+ check_expr_or_type(c, o, fv->field, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ }
+ check_assignment(c, o, t->Map.key, str_lit("map literal"));
+ if (o->mode == Addressing_Invalid) {
+ continue;
+ }
+
+ if (value_is_typeid) {
+ check_expr_or_type(c, o, fv->value, t->Map.value);
+ } else {
+ check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ }
+ check_assignment(c, o, t->Map.value, str_lit("map literal"));
}
+ }
- gbString str = type_to_string(type);
- error(node, "Invalid compound literal type '%s'", str);
- gb_string_free(str);
- return kind;
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ } else {
+ add_package_dependency(c, "runtime", "__dynamic_map_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
+ }
+ break;
+ }
+
+ case Type_BitSet: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
}
+ Type *et = base_type(t->BitSet.elem);
+ isize field_count = 0;
+ if (et->kind == Type_Enum) {
+ field_count = et->Enum.fields.count;
}
- if (is_constant) {
- o->mode = Addressing_Constant;
+ 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;
+ } else {
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'field = value' in a bit_set a literal is not allowed");
+ continue;
+ }
- if (is_type_bit_set(type)) {
- // NOTE(bill): Encode as an integer
+ check_expr_with_type_hint(c, o, elem, et);
- i64 lower = base_type(type)->BitSet.lower;
+ if (is_constant) {
+ is_constant = o->mode == Addressing_Constant;
+ }
- u64 bits = 0;
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- GB_ASSERT(elem->kind != Ast_FieldValue);
- TypeAndValue tav = elem->tav;
- ExactValue i = exact_value_to_integer(tav.value);
- if (i.kind != ExactValue_Integer) {
+ check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+ if (o->mode == Addressing_Constant) {
+ i64 lower = t->BitSet.lower;
+ i64 upper = t->BitSet.upper;
+ i64 v = exact_value_to_i64(o->value);
+ 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);
continue;
}
- i64 val = big_int_to_i64(&i.value_integer);
- val -= lower;
- u64 bit = u64(1ll<<val);
- bits |= bit;
- }
- o->value = exact_value_u64(bits);
- } else if (is_type_constant_type(type) && cl->elems.count == 0) {
- ExactValue value = exact_value_compound(node);
- Type *bt = core_type(type);
- if (bt->kind == Type_Basic) {
- if (bt->Basic.flags & BasicFlag_Boolean) {
- value = exact_value_bool(false);
- } else if (bt->Basic.flags & BasicFlag_Integer) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Unsigned) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Float) {
- value = exact_value_float(0);
- } else if (bt->Basic.flags & BasicFlag_Complex) {
- value = exact_value_complex(0, 0);
- } else if (bt->Basic.flags & BasicFlag_Quaternion) {
- value = exact_value_quaternion(0, 0, 0, 0);
- } else if (bt->Basic.flags & BasicFlag_Pointer) {
- value = exact_value_pointer(0);
- } else if (bt->Basic.flags & BasicFlag_String) {
- String empty_string = {};
- value = exact_value_string(empty_string);
- } else if (bt->Basic.flags & BasicFlag_Rune) {
- value = exact_value_i64(0);
- }
}
-
- o->value = value;
- } else {
- o->value = exact_value_compound(node);
}
+ }
+ break;
+ }
+
+ default: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+
+ gbString str = type_to_string(type);
+ error(node, "Invalid compound literal type '%s'", str);
+ gb_string_free(str);
+ return kind;
+ }
+ }
+
+ if (is_constant) {
+ o->mode = Addressing_Constant;
+
+ if (is_type_bit_set(type)) {
+ // NOTE(bill): Encode as an integer
+
+ i64 lower = base_type(type)->BitSet.lower;
+
+ u64 bits = 0;
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ GB_ASSERT(elem->kind != Ast_FieldValue);
+ TypeAndValue tav = elem->tav;
+ ExactValue i = exact_value_to_integer(tav.value);
+ if (i.kind != ExactValue_Integer) {
+ continue;
+ }
+ i64 val = big_int_to_i64(&i.value_integer);
+ val -= lower;
+ u64 bit = u64(1ll<<val);
+ bits |= bit;
+ }
+ o->value = exact_value_u64(bits);
+ } else if (is_type_constant_type(type) && cl->elems.count == 0) {
+ ExactValue value = exact_value_compound(node);
+ Type *bt = core_type(type);
+ if (bt->kind == Type_Basic) {
+ if (bt->Basic.flags & BasicFlag_Boolean) {
+ value = exact_value_bool(false);
+ } else if (bt->Basic.flags & BasicFlag_Integer) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Unsigned) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Float) {
+ value = exact_value_float(0);
+ } else if (bt->Basic.flags & BasicFlag_Complex) {
+ value = exact_value_complex(0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Quaternion) {
+ value = exact_value_quaternion(0, 0, 0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Pointer) {
+ value = exact_value_pointer(0);
+ } else if (bt->Basic.flags & BasicFlag_String) {
+ String empty_string = {};
+ value = exact_value_string(empty_string);
+ } else if (bt->Basic.flags & BasicFlag_Rune) {
+ value = exact_value_i64(0);
+ }
+ }
+
+ o->value = value;
} else {
- o->mode = Addressing_Value;
+ o->value = exact_value_compound(node);
}
- o->type = type;
- case_end;
+ } else {
+ o->mode = Addressing_Value;
+ }
+ o->type = type;
+ return kind;
+}
- case_ast_node(pe, ParenExpr, node);
- kind = check_expr_base(c, o, pe->expr, type_hint);
- node->viral_state_flags |= pe->expr->viral_state_flags;
+ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ta, TypeAssertion, node);
+ check_expr(c, o, ta->expr);
+ node->viral_state_flags |= ta->expr->viral_state_flags;
+
+ if (o->mode == Addressing_Invalid) {
o->expr = node;
- case_end;
+ return kind;
+ }
+ if (o->mode == Addressing_Constant) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
- case_ast_node(te, TagExpr, node);
- String name = te->name.string;
- error(node, "Unknown tag expression, #%.*s", LIT(name));
- if (te->expr) {
- kind = check_expr_base(c, o, te->expr, type_hint);
- node->viral_state_flags |= te->expr->viral_state_flags;
- }
+ if (is_type_untyped(o->type)) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
o->expr = node;
- case_end;
+ return kind;
+ }
- case_ast_node(ta, TypeAssertion, node);
- check_expr(c, o, ta->expr);
- node->viral_state_flags |= ta->expr->viral_state_flags;
+ Type *src = type_deref(o->type);
+ Type *bsrc = base_type(src);
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
- if (o->mode == Addressing_Constant) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
- gb_string_free(expr_str);
+
+ if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
+ if (!is_type_union(src)) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
+ gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
- if (is_type_untyped(o->type)) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
- gb_string_free(expr_str);
+ if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
+ bool allowed = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, type_hint)) {
+ allowed = true;
+ add_type_info_type(c, vt);
+ break;
+ }
+ }
+ if (allowed) {
+ add_type_info_type(c, o->type);
+ o->type = type_hint;
+ o->mode = Addressing_OptionalOk;
+ return kind;
+ }
+ }
+
+ if (bsrc->Union.variants.count != 1) {
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
- Type *src = type_deref(o->type);
- Type *bsrc = base_type(src);
-
-
- if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
- if (!is_type_union(src)) {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, bsrc->Union.variants[0]);
- if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
- bool allowed = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, type_hint)) {
- allowed = true;
- add_type_info_type(c, vt);
- break;
- }
- }
- if (allowed) {
- add_type_info_type(c, o->type);
- o->type = type_hint;
- o->mode = Addressing_OptionalOk;
- return kind;
+ o->type = bsrc->Union.variants[0];
+ o->mode = Addressing_OptionalOk;
+ } else {
+ Type *t = check_type(c, ta->type);
+ Type *dst = t;
+
+ if (is_type_union(src)) {
+ bool ok = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, dst)) {
+ ok = true;
+ break;
}
}
- if (bsrc->Union.variants.count != 1) {
- error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
+ if (!ok) {
+ gbString expr_str = expr_to_string(o->expr);
+ gbString dst_type_str = type_to_string(t);
+ defer (gb_string_free(expr_str));
+ defer (gb_string_free(dst_type_str));
+ if (bsrc->Union.variants.count == 0) {
+ error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
+ } else {
+ error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
+ }
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
add_type_info_type(c, o->type);
- add_type_info_type(c, bsrc->Union.variants[0]);
+ add_type_info_type(c, t);
- o->type = bsrc->Union.variants[0];
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+ } else if (is_type_any(src)) {
+ o->type = t;
o->mode = Addressing_OptionalOk;
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
} else {
- Type *t = check_type(c, ta->type);
- Type *dst = t;
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
- if (is_type_union(src)) {
- bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, dst)) {
- ok = true;
- break;
- }
- }
+ if ((c->state_flags & StateFlag_no_type_assert) == 0) {
+ add_package_dependency(c, "runtime", "type_assertion_check");
+ add_package_dependency(c, "runtime", "type_assertion_check2");
+ }
+ return kind;
+}
- if (!ok) {
- gbString expr_str = expr_to_string(o->expr);
- gbString dst_type_str = type_to_string(t);
- defer (gb_string_free(expr_str));
- defer (gb_string_free(dst_type_str));
- if (bsrc->Union.variants.count == 0) {
- error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
- } else {
- error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(se, SelectorCallExpr, node);
+ // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
+ // COM APIs is a great example of where this kind of thing is extremely useful
+ // General idea:
+ //
+ // x->y(123) == x.y(x, 123)
+ //
+ // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
+ // Just regenerating a new AST aids things
+ //
+ // TODO(bill): Is this a good hack or not?
+ //
+ // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
+
+
+ if (se->modified_call) {
+ // Prevent double evaluation
+ o->expr = node;
+ o->type = node->tav.type;
+ o->value = node->tav.value;
+ o->mode = node->tav.mode;
+ return Expr_Expr;
+ }
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
+ bool allow_arrow_right_selector_expr;
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ Operand x = {};
+ ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
- o->type = t;
- o->mode = Addressing_OptionalOk;
- } else if (is_type_any(src)) {
- o->type = t;
- o->mode = Addressing_OptionalOk;
+ if (x.mode == Addressing_Invalid || x.type == t_invalid) {
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return kind;
+ }
+ if (!is_type_proc(x.type)) {
+ gbString type_str = type_to_string(x.type);
+ error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
+ gb_string_free(type_str);
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
- } else {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- }
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
- add_package_dependency(c, "runtime", "type_assertion_check");
- add_package_dependency(c, "runtime", "type_assertion_check2");
- case_end;
+ ast_node(ce, CallExpr, se->call);
- case_ast_node(tc, TypeCast, node);
- check_expr_or_type(c, o, tc->type);
- if (o->mode != Addressing_Type) {
- gbString str = expr_to_string(tc->type);
- error(tc->type, "Expected a type, got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- }
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
+ GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
+
+ Ast *first_arg = x.expr->SelectorExpr.expr;
+ GB_ASSERT(first_arg != nullptr);
+
+ Type *pt = base_type(x.type);
+ GB_ASSERT(pt->kind == Type_Proc);
+ Type *first_type = nullptr;
+ String first_arg_name = {};
+ if (pt->Proc.param_count > 0) {
+ Entity *f = pt->Proc.params->Tuple.variables[0];
+ first_type = f->type;
+ first_arg_name = f->token.string;
+ }
+ if (first_arg_name.len == 0) {
+ first_arg_name = str_lit("_");
+ }
+
+ if (first_type == nullptr) {
+ error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
+
+ Operand y = {};
+ y.mode = first_arg->tav.mode;
+ y.type = first_arg->tav.type;
+ y.value = first_arg->tav.value;
+ if (check_is_assignable_to(c, &y, first_type)) {
+ // Do nothing, it's valid
+ } else {
+ Operand z = y;
+ z.type = type_deref(y.type);
+ if (check_is_assignable_to(c, &z, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_Pointer};
+ first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
+ } else if (y.mode == Addressing_Variable) {
+ Operand w = y;
+ w.type = alloc_type_pointer(y.type);
+ if (check_is_assignable_to(c, &w, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_And};
+ first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+ }
}
- Type *type = o->type;
- check_expr_base(c, o, tc->expr, type);
- node->viral_state_flags |= tc->expr->viral_state_flags;
+ }
- if (o->mode != Addressing_Invalid) {
- switch (tc->token.kind) {
- case Token_transmute:
- check_transmute(c, node, o, type);
- break;
- case Token_cast:
- check_cast(c, o, type);
- break;
- default:
- error(node, "Invalid AST: Invalid casting expression");
- o->mode = Addressing_Invalid;
+ if (ce->args.count > 0) {
+ bool fail = false;
+ bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+ for_array(i, ce->args) {
+ Ast *arg = ce->args[i];
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ fail = true;
break;
}
}
- return Expr_Expr;
- case_end;
+ if (!fail && first_is_field_value) {
+ Token op = {Token_Eq};
+ AstFile *f = first_arg->file();
+ first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
+ }
+ }
- case_ast_node(ac, AutoCast, node);
- check_expr_base(c, o, ac->expr, type_hint);
- node->viral_state_flags |= ac->expr->viral_state_flags;
- if (o->mode == Addressing_Invalid) {
+
+ auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
+ modified_args[0] = first_arg;
+ slice_copy(&modified_args, ce->args, 1);
+ ce->args = modified_args;
+ se->modified_call = true;
+
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ check_expr_base(c, o, se->call, type_hint);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
+ o->expr = node;
+ return Expr_Expr;
+}
+
+
+ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ie, IndexExpr, node);
+ check_expr(c, o, ie->expr);
+ node->viral_state_flags |= ie->expr->viral_state_flags;
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+
+ Type *t = base_type(type_deref(o->type));
+ bool is_ptr = is_type_pointer(o->type);
+ bool is_const = o->mode == Addressing_Constant;
+
+ if (is_type_map(t)) {
+ Operand key = {};
+ if (is_type_typeid(t->Map.key)) {
+ check_expr_or_type(c, &key, ie->index, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
+ }
+ check_assignment(c, &key, t->Map.key, str_lit("map index"));
+ if (key.mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
- if (type_hint) {
- Type *type = type_of_expr(ac->expr);
- check_cast(c, o, type_hint);
- if (is_type_typed(type) && are_types_identical(type, type_hint)) {
- if (build_context.vet_extra) {
- error(node, "Redundant 'auto_cast' applied to expression");
- }
- }
-
- }
+ o->mode = Addressing_MapIndex;
+ o->type = t->Map.value;
o->expr = node;
+
+ add_package_dependency(c, "runtime", "__dynamic_map_get");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
return Expr_Expr;
- case_end;
+ }
- case_ast_node(ue, UnaryExpr, node);
- Type *th = type_hint;
- if (ue->op.kind == Token_And) {
- th = type_deref(th);
+ i64 max_count = -1;
+ bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
+
+ if (is_const) {
+ if (is_type_array(t)) {
+ // OKay
+ } else if (is_type_slice(t)) {
+ // Okay
+ } else if (is_type_enumerated_array(t)) {
+ // Okay
+ } else if (is_type_string(t)) {
+ // Okay
+ } else if (is_type_relative_slice(t)) {
+ // Okay
+ } else if (is_type_matrix(t)) {
+ // Okay
+ } else {
+ valid = false;
}
- check_expr_base(c, o, ue->expr, th);
- node->viral_state_flags |= ue->expr->viral_state_flags;
+ }
- if (o->mode != Addressing_Invalid) {
- check_unary_expr(c, o, ue->op, node);
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ defer (gb_string_free(str));
+ defer (gb_string_free(type_str));
+ if (is_const) {
+ error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
+ } else {
+ error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
}
+ o->mode = Addressing_Invalid;
o->expr = node;
return kind;
- case_end;
+ }
+ if (ie->index == nullptr) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Missing index for '%s'", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
- case_ast_node(be, BinaryExpr, node);
- check_binary_expr(c, o, node, type_hint, true);
- if (o->mode == Addressing_Invalid) {
+ Type *index_type_hint = nullptr;
+ if (is_type_enumerated_array(t)) {
+ Type *bt = base_type(t);
+ GB_ASSERT(bt->kind == Type_EnumeratedArray);
+ index_type_hint = bt->EnumeratedArray.index;
+ }
+
+ i64 index = 0;
+ bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
+ if (is_const) {
+ if (index < 0) {
+ 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");
+ 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;
+ 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) {
+ 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");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
}
- case_end;
+ }
- case_ast_node(se, SelectorExpr, node);
- check_selector(c, o, node, type_hint);
- node->viral_state_flags |= se->expr->viral_state_flags;
- case_end;
+ if (type_hint != nullptr && is_type_matrix(t)) {
+ // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
+ // if a type hint exists
+ }
+ return kind;
+}
- case_ast_node(se, SelectorCallExpr, node);
- // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
- // COM APIs is a great example of where this kind of thing is extremely useful
- // General idea:
- //
- // x->y(123) == x.y(x, 123)
- //
- // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
- // Just regenerating a new AST aids things
- //
- // TODO(bill): Is this a good hack or not?
- //
- // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
-
-
- if (se->modified_call) {
- // Prevent double evaluation
- o->expr = node;
- o->type = node->tav.type;
- o->value = node->tav.value;
- o->mode = node->tav.mode;
- return Expr_Expr;
- }
+ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Stmt;
+ ast_node(se, SliceExpr, node);
+ check_expr(c, o, se->expr);
+ node->viral_state_flags |= se->expr->viral_state_flags;
- bool allow_arrow_right_selector_expr;
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- Operand x = {};
- ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+ if (o->mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
- if (x.mode == Addressing_Invalid || x.type == t_invalid) {
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return kind;
+ bool valid = false;
+ i64 max_count = -1;
+ Type *t = base_type(type_deref(o->type));
+ switch (t->kind) {
+ case Type_Basic:
+ if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+ valid = true;
+ if (o->mode == Addressing_Constant) {
+ max_count = o->value.value_string.len;
+ }
+ o->type = type_deref(o->type);
}
- if (!is_type_proc(x.type)) {
- gbString type_str = type_to_string(x.type);
- error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
- gb_string_free(type_str);
+ break;
+ case Type_Array:
+ valid = true;
+ max_count = t->Array.count;
+ if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot slice array '%s', value is not addressable", str);
+ gb_string_free(str);
o->mode = Addressing_Invalid;
- o->type = t_invalid;
o->expr = node;
- return Expr_Stmt;
+ return kind;
}
+ o->type = alloc_type_slice(t->Array.elem);
+ break;
- ast_node(ce, CallExpr, se->call);
+ case Type_MultiPointer:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
- GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
+ case Type_Slice:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
- Ast *first_arg = x.expr->SelectorExpr.expr;
- GB_ASSERT(first_arg != nullptr);
+ case Type_DynamicArray:
+ valid = true;
+ o->type = alloc_type_slice(t->DynamicArray.elem);
+ break;
- Type *pt = base_type(x.type);
- GB_ASSERT(pt->kind == Type_Proc);
- Type *first_type = nullptr;
- String first_arg_name = {};
- if (pt->Proc.param_count > 0) {
- Entity *f = pt->Proc.params->Tuple.variables[0];
- first_type = f->type;
- first_arg_name = f->token.string;
- }
- if (first_arg_name.len == 0) {
- first_arg_name = str_lit("_");
+ case Type_Struct:
+ if (is_type_soa_struct(t)) {
+ valid = true;
+ o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
}
+ break;
- if (first_type == nullptr) {
- error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ case Type_RelativeSlice:
+ valid = true;
+ o->type = t->RelativeSlice.slice_type;
+ if (o->mode != Addressing_Variable) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot relative slice '%s', value is not addressable", str);
+ gb_string_free(str);
o->mode = Addressing_Invalid;
- o->type = t_invalid;
o->expr = node;
- return Expr_Stmt;
+ return kind;
}
+ break;
+ }
- Operand y = {};
- y.mode = first_arg->tav.mode;
- y.type = first_arg->tav.type;
- y.value = first_arg->tav.value;
- if (check_is_assignable_to(c, &y, first_type)) {
- // Do nothing, it's valid
- } else {
- Operand z = y;
- z.type = type_deref(y.type);
- if (check_is_assignable_to(c, &z, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_Pointer};
- first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
- } else if (y.mode == Addressing_Variable) {
- Operand w = y;
- w.type = alloc_type_pointer(y.type);
- if (check_is_assignable_to(c, &w, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_And};
- first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
- }
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
+ gb_string_free(type_str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (se->low == nullptr && se->high != nullptr) {
+ // It is okay to continue as it will assume the 1st index is zero
+ }
+
+ i64 indices[2] = {};
+ Ast *nodes[2] = {se->low, se->high};
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ i64 index = max_count;
+ if (nodes[i] != nullptr) {
+ i64 capacity = -1;
+ if (max_count >= 0) {
+ capacity = max_count;
+ }
+ i64 j = 0;
+ if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
+ index = j;
+ }
+
+ node->viral_state_flags |= nodes[i]->viral_state_flags;
+ } else if (i == 0) {
+ index = 0;
+ }
+ indices[i] = index;
+ }
+
+ for (isize i = 0; i < gb_count_of(indices); i++) {
+ i64 a = indices[i];
+ for (isize j = i+1; j < gb_count_of(indices); j++) {
+ i64 b = indices[j];
+ if (a > b && b >= 0) {
+ error(se->close, "Invalid slice indices: [%td > %td]", a, b);
}
}
+ }
- if (ce->args.count > 0) {
- bool fail = false;
- bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- bool mix = false;
- if (first_is_field_value) {
- mix = arg->kind != Ast_FieldValue;
- } else {
- mix = arg->kind == Ast_FieldValue;
- }
- if (mix) {
- fail = true;
+ if (max_count < 0) {
+ if (o->mode == Addressing_Constant) {
+ gbString s = expr_to_string(se->expr);
+ error(se->expr, "Cannot slice constant value '%s'", s);
+ gb_string_free(s);
+ }
+ }
+
+ if (t->kind == Type_MultiPointer && se->high != nullptr) {
+ /*
+ x[:] -> [^]T
+ x[i:] -> [^]T
+ x[:n] -> []T
+ x[i:n] -> []T
+ */
+ o->type = alloc_type_slice(t->MultiPointer.elem);
+ }
+
+ o->mode = Addressing_Value;
+
+ if (is_type_string(t) && max_count >= 0) {
+ bool all_constant = true;
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ if (nodes[i] != nullptr) {
+ TypeAndValue tav = type_and_value_of_expr(nodes[i]);
+ if (tav.mode != Addressing_Constant) {
+ all_constant = false;
break;
}
}
- if (!fail && first_is_field_value) {
- Token op = {Token_Eq};
- AstFile *f = first_arg->file();
- first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
- }
+ }
+ if (!all_constant) {
+ 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");
+ gb_string_free(str);
+ o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
+ o->expr = node;
+ return kind;
}
+ String s = {};
+ if (o->value.kind == ExactValue_String) {
+ s = o->value.value_string;
+ }
+ o->mode = Addressing_Constant;
+ o->type = t;
+ o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ }
+ return kind;
+}
- auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
- modified_args[0] = first_arg;
- slice_copy(&modified_args, ce->args, 1);
- ce->args = modified_args;
- se->modified_call = true;
+ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ u32 prev_state_flags = c->state_flags;
+ defer (c->state_flags = prev_state_flags);
+ if (node->state_flags != 0) {
+ u32 in = node->state_flags;
+ u32 out = c->state_flags;
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- check_expr_base(c, o, se->call, type_hint);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+ if (in & StateFlag_no_bounds_check) {
+ out |= StateFlag_no_bounds_check;
+ out &= ~StateFlag_bounds_check;
+ } else if (in & StateFlag_bounds_check) {
+ out |= StateFlag_bounds_check;
+ out &= ~StateFlag_no_bounds_check;
+ }
- o->expr = node;
- return Expr_Expr;
- case_end;
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+ c->state_flags = out;
+ }
- case_ast_node(ise, ImplicitSelectorExpr, node);
- return check_implicit_selector_expr(c, o, node, type_hint);
- case_end;
+ ExprKind kind = Expr_Stmt;
- case_ast_node(ie, IndexExpr, node);
- check_expr(c, o, ie->expr);
- node->viral_state_flags |= ie->expr->viral_state_flags;
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
- Type *t = base_type(type_deref(o->type));
- bool is_ptr = is_type_pointer(o->type);
- bool is_const = o->mode == Addressing_Constant;
+ switch (node->kind) {
+ default:
+ return kind;
- if (is_type_map(t)) {
- Operand key = {};
- if (is_type_typeid(t->Map.key)) {
- check_expr_or_type(c, &key, ie->index, t->Map.key);
- } else {
- check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
- }
- check_assignment(c, &key, t->Map.key, str_lit("map index"));
- if (key.mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- o->mode = Addressing_MapIndex;
- o->type = t->Map.value;
- o->expr = node;
+ case_ast_node(be, BadExpr, node)
+ return kind;
+ case_end;
- add_package_dependency(c, "runtime", "__dynamic_map_get");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- return Expr_Expr;
- }
+ case_ast_node(i, Implicit, node)
+ switch (i->kind) {
+ 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);
+ return kind;
+ }
+ if (unparen_expr(c->assignment_lhs_hint) == node) {
+ c->scope->flags |= ScopeFlag_ContextDefined;
+ }
- i64 max_count = -1;
- bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
+ if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ error(node, "'context' has not been defined within this scope");
+ // Continue with value
+ }
- if (is_const) {
- if (is_type_array(t)) {
- // OKay
- } else if (is_type_slice(t)) {
- // Okay
- } else if (is_type_enumerated_array(t)) {
- // Okay
- } else if (is_type_string(t)) {
- // Okay
- } else if (is_type_relative_slice(t)) {
- // Okay
- } else if (is_type_matrix(t)) {
- // Okay
- } else {
- valid = false;
+ init_core_context(c->checker);
+ o->mode = Addressing_Context;
+ o->type = t_context;
}
- }
+ break;
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- defer (gb_string_free(str));
- defer (gb_string_free(type_str));
- if (is_const) {
- error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
- } else {
- error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
+ default:
+ error(node, "Illegal implicit name '%.*s'", LIT(i->string));
return kind;
}
+ case_end;
- if (ie->index == nullptr) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Missing index for '%s'", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ case_ast_node(i, Ident, node);
+ check_ident(c, o, node, nullptr, type_hint, false);
+ case_end;
- Type *index_type_hint = nullptr;
- if (is_type_enumerated_array(t)) {
- Type *bt = base_type(t);
- GB_ASSERT(bt->kind == Type_EnumeratedArray);
- index_type_hint = bt->EnumeratedArray.index;
- }
+ case_ast_node(u, Undef, node);
+ o->mode = Addressing_Value;
+ o->type = t_untyped_undef;
+ case_end;
- i64 index = 0;
- bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
- if (is_const) {
- if (index < 0) {
- 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");
- 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;
- 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) {
- 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");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+
+ case_ast_node(bl, BasicLit, node);
+ Type *t = t_invalid;
+ switch (node->tav.value.kind) {
+ case ExactValue_String: t = t_untyped_string; break;
+ case ExactValue_Float: t = t_untyped_float; break;
+ case ExactValue_Complex: t = t_untyped_complex; break;
+ case ExactValue_Quaternion: t = t_untyped_quaternion; break;
+ case ExactValue_Integer:
+ t = t_untyped_integer;
+ if (bl->token.kind == Token_Rune) {
+ t = t_untyped_rune;
}
+ break;
+ default:
+ GB_PANIC("Unhandled value type for basic literal");
+ break;
}
-
- if (type_hint != nullptr && is_type_matrix(t)) {
- // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
- // if a type hint exists
- }
-
+
+ o->mode = Addressing_Constant;
+ o->type = t;
+ o->value = node->tav.value;
case_end;
- case_ast_node(se, SliceExpr, node);
- check_expr(c, o, se->expr);
- node->viral_state_flags |= se->expr->viral_state_flags;
+ case_ast_node(bd, BasicDirective, node);
+ kind = check_basic_directive_expr(c, o, node, type_hint);
+ case_end;
- if (o->mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
+ case_ast_node(pg, ProcGroup, node);
+ error(node, "Illegal use of a procedure group");
+ o->mode = Addressing_Invalid;
+ case_end;
- bool valid = false;
- i64 max_count = -1;
- Type *t = base_type(type_deref(o->type));
- switch (t->kind) {
- case Type_Basic:
- if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
- valid = true;
- if (o->mode == Addressing_Constant) {
- max_count = o->value.value_string.len;
- }
- o->type = type_deref(o->type);
+ case_ast_node(pl, ProcLit, node);
+ CheckerContext ctx = *c;
+
+ DeclInfo *decl = nullptr;
+ Type *type = alloc_type(Type_Proc);
+ check_open_scope(&ctx, pl->type);
+ {
+ decl = make_decl_info(ctx.scope, ctx.decl);
+ decl->proc_lit = node;
+ ctx.decl = decl;
+ defer (ctx.decl = ctx.decl->parent);
+
+ if (pl->tags != 0) {
+ error(node, "A procedure literal cannot have tags");
+ pl->tags = 0; // TODO(bill): Should I zero this?!
}
- break;
- case Type_Array:
- valid = true;
- max_count = t->Array.count;
- if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ check_procedure_type(&ctx, type, pl->type);
+ if (!is_type_proc(type)) {
gbString str = expr_to_string(node);
- error(node, "Cannot slice array '%s', value is not addressable", str);
+ error(node, "Invalid procedure literal '%s'", str);
gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
+ check_close_scope(&ctx);
return kind;
}
- o->type = alloc_type_slice(t->Array.elem);
- break;
- case Type_MultiPointer:
- valid = true;
- o->type = type_deref(o->type);
- break;
+ if (pl->body == nullptr) {
+ error(node, "A procedure literal must have a body");
+ return kind;
+ }
- case Type_Slice:
- valid = true;
- o->type = type_deref(o->type);
- break;
+ pl->decl = decl;
+ check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags);
+ }
+ check_close_scope(&ctx);
- case Type_DynamicArray:
- valid = true;
- o->type = alloc_type_slice(t->DynamicArray.elem);
- break;
+ o->mode = Addressing_Value;
+ o->type = type;
+ case_end;
- case Type_Struct:
- if (is_type_soa_struct(t)) {
- valid = true;
- o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
- }
- break;
+ case_ast_node(te, TernaryIfExpr, node);
+ kind = check_ternary_if_expr(c, o, node, type_hint);
+ case_end;
- case Type_RelativeSlice:
- valid = true;
- o->type = t->RelativeSlice.slice_type;
- if (o->mode != Addressing_Variable) {
- gbString str = expr_to_string(node);
- error(node, "Cannot relative slice '%s', value is not addressable", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- break;
+ case_ast_node(te, TernaryWhenExpr, node);
+ kind = check_ternary_when_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(oe, OrElseExpr, node);
+ return check_or_else_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(re, OrReturnExpr, node);
+ return check_or_return_expr(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(cl, CompoundLit, node);
+ kind = check_compound_literal(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(pe, ParenExpr, node);
+ kind = check_expr_base(c, o, pe->expr, type_hint);
+ node->viral_state_flags |= pe->expr->viral_state_flags;
+ o->expr = node;
+ case_end;
+
+ case_ast_node(te, TagExpr, node);
+ String name = te->name.string;
+ error(node, "Unknown tag expression, #%.*s", LIT(name));
+ if (te->expr) {
+ kind = check_expr_base(c, o, te->expr, type_hint);
+ node->viral_state_flags |= te->expr->viral_state_flags;
}
+ o->expr = node;
+ case_end;
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
- gb_string_free(type_str);
+ case_ast_node(ta, TypeAssertion, node);
+ kind = check_type_assertion(c, o, node, type_hint);
+ case_end;
+
+ case_ast_node(tc, TypeCast, node);
+ check_expr_or_type(c, o, tc->type);
+ if (o->mode != Addressing_Type) {
+ gbString str = expr_to_string(tc->type);
+ error(tc->type, "Expected a type, got %s", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
+ }
+ if (o->mode == Addressing_Invalid) {
o->expr = node;
return kind;
}
+ Type *type = o->type;
+ check_expr_base(c, o, tc->expr, type);
+ node->viral_state_flags |= tc->expr->viral_state_flags;
- if (se->low == nullptr && se->high != nullptr) {
- // It is okay to continue as it will assume the 1st index is zero
+ if (o->mode != Addressing_Invalid) {
+ switch (tc->token.kind) {
+ case Token_transmute:
+ check_transmute(c, node, o, type);
+ break;
+ case Token_cast:
+ check_cast(c, o, type);
+ break;
+ default:
+ error(node, "Invalid AST: Invalid casting expression");
+ o->mode = Addressing_Invalid;
+ break;
+ }
}
+ return Expr_Expr;
+ case_end;
- i64 indices[2] = {};
- Ast *nodes[2] = {se->low, se->high};
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- i64 index = max_count;
- if (nodes[i] != nullptr) {
- i64 capacity = -1;
- if (max_count >= 0) {
- capacity = max_count;
- }
- i64 j = 0;
- if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
- index = j;
- }
+ case_ast_node(ac, AutoCast, node);
+ check_expr_base(c, o, ac->expr, type_hint);
+ node->viral_state_flags |= ac->expr->viral_state_flags;
- node->viral_state_flags |= nodes[i]->viral_state_flags;
- } else if (i == 0) {
- index = 0;
- }
- indices[i] = index;
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
}
-
- for (isize i = 0; i < gb_count_of(indices); i++) {
- i64 a = indices[i];
- for (isize j = i+1; j < gb_count_of(indices); j++) {
- i64 b = indices[j];
- if (a > b && b >= 0) {
- error(se->close, "Invalid slice indices: [%td > %td]", a, b);
+ if (type_hint) {
+ Type *type = type_of_expr(ac->expr);
+ check_cast(c, o, type_hint);
+ if (is_type_typed(type) && are_types_identical(type, type_hint)) {
+ if (build_context.vet_extra) {
+ error(node, "Redundant 'auto_cast' applied to expression");
}
}
+
}
+ o->expr = node;
+ return Expr_Expr;
+ case_end;
- if (max_count < 0) {
- if (o->mode == Addressing_Constant) {
- gbString s = expr_to_string(se->expr);
- error(se->expr, "Cannot slice constant value '%s'", s);
- gb_string_free(s);
- }
+ case_ast_node(ue, UnaryExpr, node);
+ Type *th = type_hint;
+ if (ue->op.kind == Token_And) {
+ th = type_deref(th);
+ }
+ check_expr_base(c, o, ue->expr, th);
+ node->viral_state_flags |= ue->expr->viral_state_flags;
+
+ if (o->mode != Addressing_Invalid) {
+ check_unary_expr(c, o, ue->op, node);
}
+ o->expr = node;
+ return kind;
+ case_end;
+
- if (t->kind == Type_MultiPointer && se->high != nullptr) {
- /*
- x[:] -> [^]T
- x[i:] -> [^]T
- x[:n] -> []T
- x[i:n] -> []T
- */
- o->type = alloc_type_slice(t->MultiPointer.elem);
+ case_ast_node(be, BinaryExpr, node);
+ check_binary_expr(c, o, node, type_hint, true);
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
}
+ case_end;
- o->mode = Addressing_Value;
+ case_ast_node(se, SelectorExpr, node);
+ check_selector(c, o, node, type_hint);
+ node->viral_state_flags |= se->expr->viral_state_flags;
+ case_end;
- if (is_type_string(t) && max_count >= 0) {
- bool all_constant = true;
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- if (nodes[i] != nullptr) {
- TypeAndValue tav = type_and_value_of_expr(nodes[i]);
- if (tav.mode != Addressing_Constant) {
- all_constant = false;
- break;
- }
- }
- }
- if (!all_constant) {
- 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");
- gb_string_free(str);
- o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
- o->expr = node;
- return kind;
- }
+ case_ast_node(se, SelectorCallExpr, node);
+ return check_selector_call_expr(c, o, node, type_hint);
+ case_end;
- String s = {};
- if (o->value.kind == ExactValue_String) {
- s = o->value.value_string;
- }
+ case_ast_node(ise, ImplicitSelectorExpr, node);
+ return check_implicit_selector_expr(c, o, node, type_hint);
+ case_end;
- o->mode = Addressing_Constant;
- o->type = t;
- o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
- }
+ case_ast_node(ie, IndexExpr, node);
+ kind = check_index_expr(c, o, node, type_hint);
+ case_end;
+ case_ast_node(se, SliceExpr, node);
+ kind = check_slice_expr(c, o, node, type_hint);
case_end;
case_ast_node(mie, MatrixIndexExpr, node);
@@ -8940,6 +9309,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
+
+
ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = check_expr_base_internal(c, o, node, type_hint);
if (o->type != nullptr && core_type(o->type) == nullptr) {
@@ -9114,18 +9485,7 @@ gbString string_append_string(gbString str, String string) {
gbString string_append_token(gbString str, Token token) {
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
str = string_append_string(str, token.string);
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
-
return str;
}
@@ -9352,6 +9712,13 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = gb_string_appendc(str, " = ");
str = write_expr_to_string(str, fv->value, shorthand);
case_end;
+ case_ast_node(fv, EnumFieldValue, node);
+ str = write_expr_to_string(str, fv->name, shorthand);
+ if (fv->value) {
+ str = gb_string_appendc(str, " = ");
+ str = write_expr_to_string(str, fv->value, shorthand);
+ }
+ case_end;
case_ast_node(ht, HelperType, node);
str = gb_string_appendc(str, "#type ");
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 94b7561c7..7cae1893f 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
ctx->state_flags = out;
}
@@ -689,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
return true;
}
-
-struct TypeAndToken {
- Type *type;
- Token token;
-};
-
-
-void add_constant_switch_case(CheckerContext *ctx, PtrMap<uintptr, TypeAndToken> *seen, Operand operand, bool use_expr = true) {
- if (operand.mode != Addressing_Constant) {
- return;
- }
- if (operand.value.kind == ExactValue_Invalid) {
- return;
- }
-
- uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
- isize count = multi_map_count(seen, key);
- TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
-
- multi_map_get_all(seen, key, taps);
- for (isize i = 0; i < count; i++) {
- TypeAndToken tap = taps[i];
- if (!are_types_identical(operand.type, tap.type)) {
- continue;
- }
-
- TokenPos pos = tap.token.pos;
- if (use_expr) {
- gbString expr_str = expr_to_string(operand.expr);
- error(operand.expr,
- "Duplicate case '%s'\n"
- "\tprevious case at %s",
- expr_str,
- token_pos_to_string(pos));
- gb_string_free(expr_str);
- } else {
- error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
- }
- return;
- }
- }
-
- TypeAndToken tap = {operand.type, ast_token(operand.expr)};
- multi_map_insert(seen, key, tap);
-}
-
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -961,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
- PtrMap<uintptr, TypeAndToken> seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
@@ -1001,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
- case Token_Ellipsis: upper_op = Token_GtEq; break;
- case Token_RangeFull: upper_op = Token_GtEq; break;
- case Token_RangeHalf: upper_op = Token_Gt; break;
+ case Token_Ellipsis: upper_op = Token_LtEq; break;
+ case Token_RangeFull: upper_op = Token_LtEq; break;
+ case Token_RangeHalf: upper_op = Token_Lt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -1024,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
- if (is_type_enum(x.type)) {
- // TODO(bill): Fix this logic so it's fast!!!
-
- i64 v0 = exact_value_to_i64(lhs.value);
- i64 v1 = exact_value_to_i64(rhs.value);
- Operand v = {};
- v.mode = Addressing_Constant;
- v.type = x.type;
- v.expr = x.expr;
-
- Type *bt = base_type(x.type);
- GB_ASSERT(bt->kind == Type_Enum);
- for (i64 vi = v0; vi <= v1; vi++) {
- if (upper_op != Token_GtEq && vi == v1) {
- break;
- }
-
- bool found = false;
- for_array(j, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[j];
- GB_ASSERT(f->kind == Entity_Constant);
-
- i64 fv = exact_value_to_i64(f->Constant.value);
- if (fv == vi) {
- found = true;
- break;
- }
- }
- if (found) {
- v.value = exact_value_i64(vi);
- add_constant_switch_case(ctx, &seen, v);
- }
- }
- } else {
- add_constant_switch_case(ctx, &seen, lhs);
- if (upper_op == Token_GtEq) {
- add_constant_switch_case(ctx, &seen, rhs);
- }
- }
+ add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1107,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
- add_constant_switch_case(ctx, &seen, y);
+ add_to_seen_map(ctx, &seen, y);
}
}
}
@@ -1143,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
- error_no_newline(node, "Unhandled switch cases: ");
+ error(node, "Unhandled switch cases:");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
diff --git a/src/check_type.cpp b/src/check_type.cpp
index a5a757f3e..6d3e32466 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -120,6 +120,8 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
ast_node(p, Field, param);
Ast *type_expr = p->type;
Type *type = nullptr;
+ CommentGroup *docs = p->docs;
+ CommentGroup *comment = p->comment;
if (type_expr != nullptr) {
type = check_type_expr(ctx, type_expr, nullptr);
@@ -156,6 +158,14 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
add_entity(ctx, ctx->scope, name, field);
field->Variable.field_group_index = field_group_index;
+
+ if (j == 0) {
+ field->Variable.docs = docs;
+ }
+ if (j+1 == p->names.count) {
+ field->Variable.comment = comment;
+ }
+
array_add(&fields_array, field);
String tag = p->tag.string;
if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
@@ -722,20 +732,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
Ast *ident = nullptr;
Ast *init = nullptr;
u32 entity_flags = 0;
- if (field->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, field);
- if (fv->field == nullptr || fv->field->kind != Ast_Ident) {
- error(field, "An enum field's name must be an identifier");
- continue;
- }
- ident = fv->field;
- init = fv->value;
- } else if (field->kind == Ast_Ident) {
- ident = field;
- } else {
+ if (field->kind != Ast_EnumFieldValue) {
error(field, "An enum field's name must be an identifier");
continue;
}
+ ident = field->EnumFieldValue.name;
+ init = field->EnumFieldValue.value;
+ if (ident == nullptr || ident->kind != Ast_Ident) {
+ error(field, "An enum field's name must be an identifier");
+ continue;
+ }
+ CommentGroup *docs = field->EnumFieldValue.docs;
+ CommentGroup *comment = field->EnumFieldValue.comment;
+
String name = ident->Ident.token.string;
if (init != nullptr) {
@@ -793,6 +802,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
e->flags |= EntityFlag_Visited;
e->state = EntityState_Resolved;
e->Constant.flags |= entity_flags;
+ e->Constant.docs = docs;
+ e->Constant.comment = comment;
if (scope_lookup_current(ctx->scope, name) != nullptr) {
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
@@ -2702,29 +2713,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid);
- bool is_partial = false;
+ bool is_sparse = false;
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name.string;
- if (name == "partial") {
- is_partial = true;
+ if (name == "sparse") {
+ is_sparse = true;
} else {
error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
}
}
- if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) {
+ if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
error(e, "Non-contiguous enumeration used as an index in an enumerated array");
long long ea_count = cast(long long)t->EnumeratedArray.count;
long long enum_count = cast(long long)bt->Enum.fields.count;
error_line("\tenumerated array length: %lld\n", ea_count);
error_line("\tenum field count: %lld\n", enum_count);
- error_line("\tSuggestion: prepend #partial to the enumerated array to allow for non-named elements\n");
+ error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
if (2*enum_count < ea_count) {
error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
- error_line("\t this warning will be removed if #partial is applied\n");
+ error_line("\t this warning will be removed if #sparse is applied\n");
}
}
+ t->EnumeratedArray.is_sparse = is_sparse;
*type = t;
diff --git a/src/checker.cpp b/src/checker.cpp
index 055d0d356..e1c827529 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -504,6 +504,7 @@ enum VettedEntityKind {
VettedEntity_Unused,
VettedEntity_Shadowed,
+ VettedEntity_Shadowed_And_Unused,
};
struct VettedEntity {
VettedEntityKind kind;
@@ -625,12 +626,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *e = scope->elements.entries[i].value;
if (e == nullptr) continue;
- VettedEntity ve = {};
- if (vet_unused && check_vet_unused(c, e, &ve)) {
- array_add(&vetted_entities, ve);
- }
- if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
- array_add(&vetted_entities, ve);
+ VettedEntity ve_unused = {};
+ VettedEntity ve_shadowed = {};
+ bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
+ if (is_unused && is_shadowed) {
+ VettedEntity ve_both = ve_shadowed;
+ ve_both.kind = VettedEntity_Shadowed_And_Unused;
+ array_add(&vetted_entities, ve_both);
+ } else if (is_unused) {
+ array_add(&vetted_entities, ve_unused);
+ } else if (is_shadowed) {
+ array_add(&vetted_entities, ve_shadowed);
}
}
@@ -642,16 +649,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
Entity *other = ve.other;
String name = e->token.string;
- if (build_context.vet) {
+ if (ve.kind == VettedEntity_Shadowed_And_Unused) {
+ error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
+ } else if (build_context.vet) {
switch (ve.kind) {
case VettedEntity_Unused:
error(e->token, "'%.*s' declared but not used", LIT(name));
break;
case VettedEntity_Shadowed:
if (e->flags&EntityFlag_Using) {
- error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
} else {
- error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
}
break;
default:
@@ -688,12 +697,17 @@ void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
ptr_set_add(&d->deps, e);
mutex_unlock(&info->deps_mutex);
}
-void add_type_info_dependency(DeclInfo *d, Type *type) {
+void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type, bool require_mutex) {
if (d == nullptr) {
return;
}
- // NOTE(bill): no mutex is required here because the only procedure calling it is wrapped in a mutex already
+ if (require_mutex) {
+ mutex_lock(&info->deps_mutex);
+ }
ptr_set_add(&d->type_info_deps, type);
+ if (require_mutex) {
+ mutex_unlock(&info->deps_mutex);
+ }
}
AstPackage *get_core_package(CheckerInfo *info, String name) {
@@ -919,6 +933,16 @@ void init_universal(void) {
add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
}
+ {
+ GlobalEnumValue values[ErrorPosStyle_COUNT] = {
+ {"Default", ErrorPosStyle_Default},
+ {"Unix", ErrorPosStyle_Unix},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
+ }
+
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
@@ -1620,7 +1644,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
return;
}
- add_type_info_dependency(c->decl, t);
+ add_type_info_dependency(c->info, c->decl, t, false);
auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
@@ -1749,6 +1773,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
} else {
add_type_info_type_internal(c, t_type_info_ptr);
}
+ add_type_info_type_internal(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_type_info_type_internal(c, bt->Union.variants[i]);
}
@@ -1772,6 +1797,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
}
}
}
+ add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_type_info_type_internal(c, f->type);
@@ -1965,6 +1991,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
} else {
add_min_dep_type_info(c, t_type_info_ptr);
}
+ add_min_dep_type_info(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_min_dep_type_info(c, bt->Union.variants[i]);
}
@@ -1988,6 +2015,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
}
}
}
+ add_min_dep_type_info(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_min_dep_type_info(c, f->type);
@@ -3479,9 +3507,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (entity_visibility_kind == EntityVisiblity_Public &&
(c->scope->flags&ScopeFlag_File) &&
- c->scope->file &&
- (c->scope->file->flags & AstFile_IsPrivate)) {
- entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+ c->scope->file) {
+ if (c->scope->file->flags & AstFile_IsPrivateFile) {
+ entity_visibility_kind = EntityVisiblity_PrivateToFile;
+ } else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
+ entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+ }
}
if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {
@@ -3572,9 +3603,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (is_ast_type(init)) {
e = alloc_entity_type_name(d->scope, token, nullptr);
- // if (vd->type != nullptr) {
- // error(name, "A type declaration cannot have an type parameter");
- // }
} else if (init->kind == Ast_ProcLit) {
if (c->scope->flags&ScopeFlag_Type) {
error(name, "Procedure declarations are not allowed within a struct");
@@ -3677,6 +3705,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
check_collect_entities(&c, block->stmts);
}
+bool correct_single_type_alias(CheckerContext *c, Entity *e) {
+ if (e->kind == Entity_Constant) {
+ DeclInfo *d = e->decl_info;
+ if (d != nullptr && d->init_expr != nullptr) {
+ Ast *init = d->init_expr;
+ Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
+ if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
+ e->kind = Entity_TypeName;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = n-1; i >= 0; i--) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = 0; i < n; i++) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+
+
+void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
+ // NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
+ // of type aliases being "confused" as constants
+ //
+ // A :: C
+ // B :: A
+ // C :: struct {b: ^B}
+ //
+ // See @TypeAliasingProblem for more information
+ for (;;) {
+ bool corrections = false;
+ corrections |= correct_type_alias_in_scope_backwards(c, s);
+ corrections |= correct_type_alias_in_scope_forwards(c, s);
+ if (!corrections) {
+ return;
+ }
+ }
+}
+
+
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
AstFile *curr_file = nullptr;
@@ -3748,6 +3829,7 @@ void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
}
}
+ // correct_type_aliases(c);
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
@@ -4393,10 +4475,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
for_array(i, decls) {
if (collect_file_decl(ctx, decls[i])) {
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return true;
}
}
-
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return false;
}
@@ -4666,6 +4749,15 @@ void check_import_entities(Checker *c) {
}
add_untyped_expressions(ctx.info, &untyped);
}
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = false;
+
+ correct_type_aliases_in_scope(&ctx, pkg->scope);
+ }
+
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
@@ -4887,6 +4979,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
+ bool type_assert = (pi->tags & ProcTag_type_assert) != 0;
+ bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0;
+
if (bounds_check) {
ctx.state_flags |= StateFlag_bounds_check;
ctx.state_flags &= ~StateFlag_no_bounds_check;
@@ -4894,6 +4989,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.state_flags |= StateFlag_no_bounds_check;
ctx.state_flags &= ~StateFlag_bounds_check;
}
+
+ if (type_assert) {
+ ctx.state_flags |= StateFlag_type_assert;
+ ctx.state_flags &= ~StateFlag_no_type_assert;
+ } else if (no_type_assert) {
+ ctx.state_flags |= StateFlag_no_type_assert;
+ ctx.state_flags &= ~StateFlag_type_assert;
+ }
+
if (pi->body != nullptr && e != nullptr) {
GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0);
}
@@ -5307,12 +5411,18 @@ void check_unique_package_names(Checker *c) {
string_map_set(&pkgs, key, pkg);
continue;
}
+ auto *curr = pkg->files[0]->pkg_decl;
+ auto *prev = (*found)->files[0]->pkg_decl;
+ if (curr == prev) {
+ // NOTE(bill): A false positive was found, ignore it
+ continue;
+ }
- error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name));
+ error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
error_line("\tA package name must be unique\n"
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
- error((*found)->files[0]->pkg_decl, "found at previous location");
+ error(prev, "found at previous location");
}
}
@@ -5504,9 +5614,6 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
- TIME_SECTION("generate minimum dependency set");
- generate_minimum_dependency_set(c, c->info.entry_point);
-
TIME_SECTION("check test procedures");
check_test_procedures(c);
@@ -5517,6 +5624,9 @@ void check_parsed_files(Checker *c) {
add_type_info_for_type_definitions(c);
check_merge_queues_into_arrays(c);
+ TIME_SECTION("generate minimum dependency set");
+ generate_minimum_dependency_set(c, c->info.entry_point);
+
TIME_SECTION("check entry point");
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
Scope *s = c->info.init_scope;
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index e8f5174c0..d833a055f 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -213,8 +213,6 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_union,
BuiltinProc_type_is_enum,
BuiltinProc_type_is_proc,
- BuiltinProc_type_is_bit_field,
- BuiltinProc_type_is_bit_field_value,
BuiltinProc_type_is_bit_set,
BuiltinProc_type_is_simd_vector,
BuiltinProc_type_is_matrix,
@@ -466,8 +464,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index f47fd0945..7ce93d2bf 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -15,7 +15,7 @@ struct OdinDocVersionType {
#define OdinDocVersionType_Major 0
#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 3
+#define OdinDocVersionType_Patch 4
struct OdinDocHeaderBase {
u8 magic[8];
@@ -154,6 +154,7 @@ enum OdinDocEntityKind : u32 {
OdinDocEntity_ProcGroup = 5,
OdinDocEntity_ImportName = 6,
OdinDocEntity_LibraryName = 7,
+ OdinDocEntity_Builtin = 8,
};
enum OdinDocEntityFlag : u64 {
@@ -170,6 +171,9 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Type_Alias = 1ull<<20,
+ OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30,
+ OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31,
+
OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
OdinDocEntityFlag_Var_Static = 1ull<<41,
@@ -185,8 +189,8 @@ struct OdinDocEntity {
OdinDocTypeIndex type;
OdinDocString init_string;
u32 reserved_for_init;
- OdinDocString comment;
- OdinDocString docs;
+ OdinDocString comment; // line comment
+ OdinDocString docs; // preceding comment
i32 field_group_index;
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
@@ -201,15 +205,21 @@ enum OdinDocPkgFlags : u32 {
OdinDocPkgFlag_Init = 1<<2,
};
+struct OdinDocScopeEntry {
+ OdinDocString name;
+ OdinDocEntityIndex entity;
+};
+
struct OdinDocPkg {
OdinDocString fullpath;
OdinDocString name;
u32 flags;
OdinDocString docs;
- OdinDocArray<OdinDocFileIndex> files;
- OdinDocArray<OdinDocEntityIndex> entities;
+ OdinDocArray<OdinDocFileIndex> files;
+ OdinDocArray<OdinDocScopeEntry> entries;
};
+
struct OdinDocHeader {
OdinDocHeaderBase base;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 0a990cc37..0474ce8ff 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -811,8 +811,17 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
comment = e->decl_info->comment;
docs = e->decl_info->docs;
}
+ if (e->kind == Entity_Variable) {
+ if (!comment) { comment = e->Variable.comment; }
+ if (!docs) { docs = e->Variable.docs; }
+ } else if (e->kind == Entity_Constant) {
+ if (!comment) { comment = e->Constant.comment; }
+ if (!docs) { docs = e->Constant.docs; }
+ }
+ String name = e->token.string;
String link_name = {};
+ TokenPos pos = e->token.pos;
OdinDocEntityKind kind = OdinDocEntity_Invalid;
u64 flags = 0;
@@ -827,6 +836,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
+ case Entity_Builtin: kind = OdinDocEntity_Builtin; break;
}
switch (e->kind) {
@@ -856,6 +866,23 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
link_name = e->Procedure.link_name;
break;
+ case Entity_Builtin:
+ {
+ auto bp = builtin_procs[e->Builtin.id];
+ pos = {};
+ name = bp.name;
+ switch (bp.pkg) {
+ case BuiltinProcPkg_builtin:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
+ break;
+ case BuiltinProcPkg_intrinsics:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
+ break;
+ default:
+ GB_PANIC("Unhandled BuiltinProcPkg");
+ }
+ }
+ break;
}
if (e->flags & EntityFlag_Param) {
@@ -891,8 +918,8 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
doc_entity.kind = kind;
doc_entity.flags = flags;
- doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
- doc_entity.name = odin_doc_write_string(w, e->token.string);
+ doc_entity.pos = odin_doc_token_pos_cast(w, pos);
+ doc_entity.name = odin_doc_write_string(w, name);
doc_entity.type = 0; // Set later
doc_entity.init_string = init_string;
doc_entity.comment = odin_doc_comment_group_string(w, comment);
@@ -969,7 +996,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
-OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
+OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
if (pkg->scope == nullptr) {
return {};
}
@@ -977,14 +1004,14 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
return {};
}
- auto entities = array_make<Entity *>(heap_allocator(), 0, pkg->scope->elements.entries.count);
- defer (array_free(&entities));
+ auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.entries.count);
+ defer (array_free(&entries));
for_array(i, pkg->scope->elements.entries) {
+ String name = pkg->scope->elements.entries[i].key.string;
Entity *e = pkg->scope->elements.entries[i].value;
switch (e->kind) {
case Entity_Invalid:
- case Entity_Builtin:
case Entity_Nil:
case Entity_Label:
continue;
@@ -995,34 +1022,27 @@ OdinDocArray<OdinDocEntityIndex> odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
case Entity_ProcGroup:
case Entity_ImportName:
case Entity_LibraryName:
+ case Entity_Builtin:
// Fine
break;
}
- array_add(&entities, e);
- }
- gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
-
- auto entity_indices = array_make<OdinDocEntityIndex>(heap_allocator(), 0, w->entity_cache.entries.count);
- defer (array_free(&entity_indices));
-
- for_array(i, entities) {
- Entity *e = entities[i];
if (e->pkg != pkg) {
continue;
}
- if (!is_entity_exported(e)) {
+ if (!is_entity_exported(e, true)) {
continue;
}
if (e->token.string.len == 0) {
continue;
}
- OdinDocEntityIndex doc_entity_index = 0;
- doc_entity_index = odin_doc_add_entity(w, e);
- array_add(&entity_indices, doc_entity_index);
+ OdinDocScopeEntry entry = {};
+ entry.name = odin_doc_write_string(w, name);
+ entry.entity = odin_doc_add_entity(w, e);
+ array_add(&entries, entry);
}
- return odin_write_slice(w, entity_indices.data, entity_indices.count);
+ return odin_write_slice(w, entries.data, entries.count);
}
@@ -1090,7 +1110,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
}
doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
- doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
+ doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
if (dst) {
*dst = doc_pkg;
diff --git a/src/entity.cpp b/src/entity.cpp
index 05ee9a33e..8327a517e 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -161,6 +161,8 @@ struct Entity {
ParameterValue param_value;
u32 flags;
i32 field_group_index;
+ CommentGroup *docs;
+ CommentGroup *comment;
} Constant;
struct {
Ast *init_expr; // only used for some variables within procedure bodies
@@ -175,6 +177,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_section;
+ CommentGroup *docs;
+ CommentGroup *comment;
bool is_foreign;
bool is_export;
} Variable;
@@ -241,7 +245,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
if (e->flags & EntityFlag_NotExported) {
return false;
}
- if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
+ if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) {
return false;
}
diff --git a/src/error.cpp b/src/error.cpp
index 1496b4775..faf4d11fb 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -33,6 +33,10 @@ void init_global_error_collector(void) {
}
+// temporary
+// defined in build_settings.cpp
+char *token_pos_to_string(TokenPos const &pos);
+
bool set_file_path_string(i32 index, String const &path) {
bool ok = false;
GB_ASSERT(index >= 0);
@@ -403,6 +407,7 @@ void compiler_error(char const *fmt, ...) {
gb_printf_err("Internal Compiler Error: %s\n",
gb_bprintf_va(fmt, va));
va_end(va);
+ GB_DEBUG_TRAP();
gb_exit(1);
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 49f675a49..d7093bc63 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -232,6 +232,7 @@ struct lbTargetList {
enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
+ lbProcedureFlag_DebugAllocaCopy = 1<<1,
};
struct lbCopyElisionHint {
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 5862a7add..8f17a1cfb 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
- GB_ASSERT(is_type_pointer(value.type));
- GB_ASSERT(is_type_pointer(t));
+ GB_ASSERT(is_type_internally_pointer_like(value.type));
+ GB_ASSERT(is_type_internally_pointer_like(t));
GB_ASSERT(lb_is_const(value));
lbValue res = {};
@@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
}
LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
- GB_ASSERT(is_type_pointer(data.type));
+ GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
GB_ASSERT(are_types_identical(len.type, t_int));
LLVMValueRef vals[2] = {
data.value,
@@ -568,7 +568,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
case ExactValue_Integer:
- if (is_type_pointer(type)) {
+ if (is_type_pointer(type) || is_type_multi_pointer(type)) {
LLVMTypeRef t = lb_type(m, original_type);
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
res.value = LLVMConstIntToPtr(i, t);
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index 7a2b00fe9..f60096aad 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -965,6 +965,77 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
}
+
+void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ if (type == nullptr) {
+ return;
+ }
+ if (type == t_invalid) {
+ return;
+ }
+ if (p->body == nullptr) {
+ return;
+ }
+
+ lbModule *m = p->module;
+ String const &name = token.string;
+ if (name == "" || name == "_") {
+ return;
+ }
+
+ if (lb_get_llvm_metadata(m, ptr) != nullptr) {
+ // Already been set
+ return;
+ }
+
+
+ AstFile *file = p->body->file();
+
+ LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
+ LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+ GB_ASSERT(llvm_scope != nullptr);
+ if (llvm_file == nullptr) {
+ llvm_file = LLVMDIScopeGetFile(llvm_scope);
+ }
+
+ if (llvm_file == nullptr) {
+ return;
+ }
+
+ LLVMDIFlags flags = LLVMDIFlagZero;
+ LLVMBool always_preserve = build_context.optimization_level == 0;
+
+ LLVMMetadataRef debug_type = lb_debug_type(m, type);
+
+ LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable(
+ m->debug_builder, llvm_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ arg_number,
+ llvm_file, token.pos.line,
+ debug_type,
+ always_preserve, flags
+ );
+
+ LLVMValueRef storage = ptr;
+ LLVMValueRef instr = ptr;
+ LLVMBasicBlockRef block = p->decl_block->block;
+ LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
+ LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
+ lb_set_llvm_metadata(m, ptr, llvm_expr);
+ if (LLVMIsAAllocaInst(instr)) {
+ LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
+ } else {
+ // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
+ // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
+ // instruction "before" it.
+ LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
+ }
+}
+
+
void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
if (!p->debug_info || !p->body) {
return;
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 1f0ed6434..29a86d116 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -580,6 +580,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
if (is_type_array(m.type)) {
+ i32 rank = type_math_rank(m.type);
+ if (rank == 2) {
+ lbAddr addr = lb_add_local_generated(p, type, false);
+ lbValue dst = addr.addr;
+ lbValue src = m;
+ i32 n = cast(i32)get_array_type_count(m.type);
+ i32 m = cast(i32)get_array_type_count(type);
+ // m.type == [n][m]T
+ // type == [m][n]T
+
+ for (i32 j = 0; j < m; j++) {
+ lbValue dst_col = lb_emit_struct_ep(p, dst, j);
+ for (i32 i = 0; i < n; i++) {
+ lbValue dst_row = lb_emit_struct_ep(p, dst_col, i);
+ lbValue src_col = lb_emit_struct_ev(p, src, i);
+ lbValue src_row = lb_emit_struct_ev(p, src_col, j);
+ lb_emit_store(p, dst_row, src_row);
+ }
+ }
+ return lb_addr_load(p, addr);
+ }
// no-op
m.type = type;
return m;
@@ -1834,6 +1855,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_addr_load(p, parent);
}
}
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ if (internal_check_is_assignable_to(src, vt)) {
+ value = lb_emit_conv(p, value, vt);
+ lbAddr parent = lb_add_local_generated(p, t, true);
+ lb_emit_store_union_variant(p, parent.addr, value, vt);
+ return lb_addr_load(p, parent);
+ }
+ }
}
// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
@@ -2768,27 +2798,29 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *src_type = type_deref(v.type);
Type *dst_type = type;
- lbValue src_tag = {};
- lbValue dst_tag = {};
- if (is_type_union_maybe_pointer(src_type)) {
- src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
- dst_tag = lb_const_bool(p->module, t_bool, true);
- } else {
- src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
- dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
- }
- lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
- auto args = array_make<lbValue>(permanent_allocator(), 6);
- args[0] = ok;
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ lbValue src_tag = {};
+ lbValue dst_tag = {};
+ if (is_type_union_maybe_pointer(src_type)) {
+ src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+ dst_tag = lb_const_bool(p->module, t_bool, true);
+ } else {
+ src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+ dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+ }
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
+ args[0] = ok;
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
- args[4] = lb_typeid(p->module, src_type);
- args[5] = lb_typeid(p->module, dst_type);
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[4] = lb_typeid(p->module, src_type);
+ args[5] = lb_typeid(p->module, dst_type);
+ lb_emit_runtime_call(p, "type_assertion_check", args);
+ }
lbValue data_ptr = v;
return lb_emit_conv(p, data_ptr, tv.type);
@@ -2797,23 +2829,23 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
if (is_type_pointer(v.type)) {
v = lb_emit_load(p, v);
}
-
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
- lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue id = lb_typeid(p->module, type);
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ lbValue any_id = lb_emit_struct_ev(p, v, 1);
+ lbValue id = lb_typeid(p->module, type);
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+ auto args = array_make<lbValue>(permanent_allocator(), 6);
+ args[0] = ok;
- lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
- auto args = array_make<lbValue>(permanent_allocator(), 6);
- args[0] = ok;
-
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
- args[4] = any_id;
- args[5] = id;
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[4] = any_id;
+ args[5] = id;
+ lb_emit_runtime_call(p, "type_assertion_check", args);
+ }
return lb_emit_conv(p, data_ptr, tv.type);
} else {
@@ -2843,6 +2875,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ } else if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ }
+
p->state_flags = out;
}
@@ -3460,7 +3500,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) {
- lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+ lbAddr map_addr = lb_build_addr(p, ie->expr);
+ lbValue map_val = lb_addr_load(p, map_addr);
if (deref) {
map_val = lb_emit_load(p, map_val);
}
@@ -3469,7 +3510,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
key = lb_emit_conv(p, key, t->Map.key);
Type *result_type = type_of_expr(expr);
- return lb_addr_map(map_val, key, t, result_type);
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
+ return lb_addr_map(map_ptr, key, t, result_type);
}
switch (t->kind) {
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 17eeb0bea..2fc21b534 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -271,6 +271,10 @@ lbAddr lb_addr(lbValue addr) {
lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
+ GB_ASSERT(is_type_pointer(addr.type));
+ Type *mt = type_deref(addr.type);
+ GB_ASSERT(is_type_map(mt));
+
lbAddr v = {lbAddr_Map, addr};
v.map.key = map_key;
v.map.type = map_type;
@@ -1598,8 +1602,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return llvm_type;
}
llvm_type = LLVMStructCreateNamed(ctx, name);
+ LLVMTypeRef found_val = *found;
map_set(&m->types, type, llvm_type);
- lb_clone_struct_type(llvm_type, *found);
+ lb_clone_struct_type(llvm_type, found_val);
return llvm_type;
}
}
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 2a6eb6bb3..7a6fac603 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -61,7 +61,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
if (!entity->Procedure.is_foreign) {
- GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked);
+ GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
}
String link_name = {};
@@ -473,6 +473,8 @@ void lb_begin_procedure_body(lbProcedure *p) {
}
lbArgType *arg_type = &ft->args[param_index];
+ defer (param_index += 1);
+
if (arg_type->kind == lbArg_Ignore) {
continue;
} else if (arg_type->kind == lbArg_Direct) {
@@ -487,18 +489,19 @@ void lb_begin_procedure_body(lbProcedure *p) {
param.type = e->type;
lbValue ptr = lb_address_from_load_or_generate_local(p, param);
+ GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
lb_add_entity(p->module, e, ptr);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1);
}
} else if (arg_type->kind == lbArg_Indirect) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
lbValue ptr = {};
ptr.value = LLVMGetParam(p->value, param_offset+param_index);
ptr.type = alloc_type_pointer(e->type);
-
lb_add_entity(p->module, e, ptr);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1);
}
}
- param_index += 1;
}
}
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 3375ceda9..916c0433e 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1991,6 +1991,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
out |= StateFlag_no_bounds_check;
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
p->state_flags = out;
}
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index decb57702..1d6297164 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -1,11 +1,10 @@
isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
- isize index = type_info_index(info, type, false);
+ auto *set = &info->minimum_dependency_type_info_set;
+ isize index = type_info_index(info, type, err_on_not_found);
if (index >= 0) {
- auto *set = &info->minimum_dependency_type_info_set;
- for_array(i, set->entries) {
- if (set->entries[i].ptr == index) {
- return i+1;
- }
+ isize i = ptr_entry_index(set, index);
+ if (i >= 0) {
+ return i+1;
}
}
if (err_on_not_found) {
@@ -455,7 +454,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_EnumeratedArray: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
- LLVMValueRef vals[6] = {
+ LLVMValueRef vals[7] = {
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
@@ -464,6 +463,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// Unions
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
+
+ lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
};
lbValue res = {};
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 5b1b11b44..7e2bd7daa 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -626,6 +626,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lbValue value_ = lb_address_from_load_or_generate_local(p, value);
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type));
+ return lb_emit_load(p, ptr);
+ }
+
lbValue tag = {};
lbValue dst_tag = {};
lbValue cond = {};
@@ -666,23 +672,22 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lb_start_block(p, end_block);
if (!is_tuple) {
- {
- // NOTE(bill): Panic on invalid conversion
- Type *dst_type = tuple->Tuple.variables[0]->type;
+ GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+ // NOTE(bill): Panic on invalid conversion
+ Type *dst_type = tuple->Tuple.variables[0]->type;
- lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make<lbValue>(permanent_allocator(), 7);
- args[0] = ok;
+ lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+ auto args = array_make<lbValue>(permanent_allocator(), 7);
+ args[0] = ok;
- args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(m, t_i32, pos.line);
- args[3] = lb_const_int(m, t_i32, pos.column);
+ args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(m, t_i32, pos.line);
+ args[3] = lb_const_int(m, t_i32, pos.column);
- args[4] = lb_typeid(m, src_type);
- args[5] = lb_typeid(m, dst_type);
- args[6] = lb_emit_conv(p, value_, t_rawptr);
- lb_emit_runtime_call(p, "type_assertion_check2", args);
- }
+ args[4] = lb_typeid(m, src_type);
+ args[5] = lb_typeid(m, dst_type);
+ args[6] = lb_emit_conv(p, value_, t_rawptr);
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
}
@@ -706,6 +711,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
}
Type *dst_type = tuple->Tuple.variables[0]->type;
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_struct_ev(p, value, 0);
+ ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type));
+ return lb_addr(ptr);
+ }
+
lbAddr v = lb_add_local_generated(p, tuple, true);
lbValue dst_typeid = lb_typeid(m, dst_type);
@@ -731,7 +743,6 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
if (!is_tuple) {
// NOTE(bill): Panic on invalid conversion
-
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
auto args = array_make<lbValue>(permanent_allocator(), 7);
args[0] = ok;
diff --git a/src/main.cpp b/src/main.cpp
index fe56d451f..efb0f584e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -485,7 +485,7 @@ i32 linker_stage(lbGenerator *gen) {
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the 'mtriple' param passed to 'opt'
#if defined(GB_CPU_ARM)
- " -mmacosx-version-min=11.0.0 "
+ " -mmacosx-version-min=12.0.0 "
#else
" -mmacosx-version-min=10.8.0 "
#endif
@@ -1851,6 +1851,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
} else if (command == "run") {
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(1, " append an empty flag and then the args, '-- <args>', to specify args for the output.");
} else if (command == "check") {
print_usage_line(1, "check parse and type check .odin file(s)");
} else if (command == "test") {
diff --git a/src/parser.cpp b/src/parser.cpp
index 7e7146244..0914c77ca 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -183,6 +183,11 @@ Ast *clone_ast(Ast *node) {
n->FieldValue.value = clone_ast(n->FieldValue.value);
break;
+ case Ast_EnumFieldValue:
+ n->EnumFieldValue.name = clone_ast(n->EnumFieldValue.name);
+ n->EnumFieldValue.value = clone_ast(n->EnumFieldValue.value);
+ break;
+
case Ast_TernaryIfExpr:
n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x);
n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond);
@@ -693,6 +698,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
return result;
}
+
+Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) {
+ Ast *result = alloc_ast_node(f, Ast_EnumFieldValue);
+ result->EnumFieldValue.name = name;
+ result->EnumFieldValue.value = value;
+ result->EnumFieldValue.docs = docs;
+ result->EnumFieldValue.comment = comment;
+ return result;
+}
+
Ast *ast_compound_lit(AstFile *f, Ast *type, Array<Ast *> const &elems, Token open, Token close) {
Ast *result = alloc_ast_node(f, Ast_CompoundLit);
result->CompoundLit.type = type;
@@ -944,7 +959,7 @@ Ast *ast_field(AstFile *f, Array<Ast *> const &names, Ast *type, Ast *default_va
result->Field.default_value = default_value;
result->Field.flags = flags;
result->Field.tag = tag;
- result->Field.docs = docs;
+ result->Field.docs = docs;
result->Field.comment = comment;
return result;
}
@@ -1234,7 +1249,7 @@ CommentGroup *consume_comment_group(AstFile *f, isize n, isize *end_line_) {
return comments;
}
-void comsume_comment_groups(AstFile *f, Token prev) {
+void consume_comment_groups(AstFile *f, Token prev) {
if (f->curr_token.kind == Token_Comment) {
CommentGroup *comment = nullptr;
isize end_line = 0;
@@ -1278,7 +1293,7 @@ Token advance_token(AstFile *f) {
if (ok) {
switch (f->curr_token.kind) {
case Token_Comment:
- comsume_comment_groups(f, prev);
+ consume_comment_groups(f, prev);
break;
case Token_Semicolon:
if (ignore_newlines(f) && f->curr_token.string == "\n") {
@@ -1689,6 +1704,46 @@ Array<Ast *> parse_element_list(AstFile *f) {
return elems;
}
+CommentGroup *consume_line_comment(AstFile *f) {
+ CommentGroup *comment = f->line_comment;
+ if (f->line_comment == f->lead_comment) {
+ f->lead_comment = nullptr;
+ }
+ f->line_comment = nullptr;
+ return comment;
+
+}
+
+Array<Ast *> parse_enum_field_list(AstFile *f) {
+ auto elems = array_make<Ast *>(heap_allocator());
+
+ while (f->curr_token.kind != Token_CloseBrace &&
+ f->curr_token.kind != Token_EOF) {
+ CommentGroup *docs = f->lead_comment;
+ CommentGroup *comment = nullptr;
+ Ast *name = parse_value(f);
+ Ast *value = nullptr;
+ if (f->curr_token.kind == Token_Eq) {
+ Token eq = expect_token(f, Token_Eq);
+ value = parse_value(f);
+ }
+
+ comment = consume_line_comment(f);
+
+ Ast *elem = ast_enum_field_value(f, name, value, docs, comment);
+ array_add(&elems, elem);
+
+ if (!allow_token(f, Token_Comma)) {
+ break;
+ }
+
+ if (!elem->EnumFieldValue.comment) {
+ elem->EnumFieldValue.comment = consume_line_comment(f);
+ }
+ }
+
+ return elems;
+}
Ast *parse_literal_value(AstFile *f, Ast *type) {
Array<Ast *> elems = {};
@@ -1793,6 +1848,8 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
ELSE_IF_ADD_TAG(require_results)
ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check)
+ ELSE_IF_ADD_TAG(type_assert)
+ ELSE_IF_ADD_TAG(no_type_assert)
else {
syntax_error(tag_expr, "Unknown procedure type tag #%.*s", LIT(tag_name));
}
@@ -1803,6 +1860,10 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) {
syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure");
}
+
+ if ((*tags & ProcTag_type_assert) && (*tags & ProcTag_no_type_assert)) {
+ syntax_error(f->curr_token, "You cannot apply both #type_assert and #no_type_assert to a procedure");
+ }
}
@@ -1950,11 +2011,23 @@ Ast *parse_check_directive_for_statement(Ast *s, Token const &tag_token, u16 sta
syntax_error(tag_token, "#bounds_check and #no_bounds_check cannot be applied together");
}
break;
+ case StateFlag_type_assert:
+ if ((s->state_flags & StateFlag_no_type_assert) != 0) {
+ syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together");
+ }
+ break;
+ case StateFlag_no_type_assert:
+ if ((s->state_flags & StateFlag_type_assert) != 0) {
+ syntax_error(tag_token, "#type_assert and #no_type_assert cannot be applied together");
+ }
+ break;
}
switch (state_flag) {
case StateFlag_bounds_check:
case StateFlag_no_bounds_check:
+ case StateFlag_type_assert:
+ case StateFlag_no_type_assert:
switch (s->kind) {
case Ast_BlockStmt:
case Ast_IfStmt:
@@ -2063,6 +2136,22 @@ Ast *parse_operand(AstFile *f, bool lhs) {
return original_type;
} else if (name.string == "partial") {
Ast *tag = ast_basic_directive(f, token, name);
+ Ast *original_expr = parse_expr(f, lhs);
+ Ast *expr = unparen_expr(original_expr);
+ switch (expr->kind) {
+ case Ast_ArrayType:
+ syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types");
+ break;
+ case Ast_CompoundLit:
+ expr->CompoundLit.tag = tag;
+ break;
+ default:
+ syntax_error(expr, "Expected a compound literal after #%.*s, got %.*s", LIT(name.string), LIT(ast_strings[expr->kind]));
+ break;
+ }
+ return original_expr;
+ } else if (name.string == "sparse") {
+ Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type);
switch (type->kind) {
@@ -2078,6 +2167,12 @@ Ast *parse_operand(AstFile *f, bool lhs) {
} else if (name.string == "no_bounds_check") {
Ast *operand = parse_expr(f, lhs);
return parse_check_directive_for_statement(operand, name, StateFlag_no_bounds_check);
+ } else if (name.string == "type_assert") {
+ Ast *operand = parse_expr(f, lhs);
+ return parse_check_directive_for_statement(operand, name, StateFlag_type_assert);
+ } else if (name.string == "no_type_assert") {
+ Ast *operand = parse_expr(f, lhs);
+ return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert);
} else if (name.string == "relative") {
Ast *tag = ast_basic_directive(f, token, name);
tag = parse_call_expr(f, tag);
@@ -2174,6 +2269,12 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (tags & ProcTag_bounds_check) {
body->state_flags |= StateFlag_bounds_check;
}
+ if (tags & ProcTag_no_type_assert) {
+ body->state_flags |= StateFlag_no_type_assert;
+ }
+ if (tags & ProcTag_type_assert) {
+ body->state_flags |= StateFlag_type_assert;
+ }
return ast_proc_lit(f, type, body, tags, where_token, where_clauses);
} else if (allow_token(f, Token_do)) {
@@ -2449,7 +2550,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
skip_possible_newline_for_literal(f);
Token open = expect_token(f, Token_OpenBrace);
- Array<Ast *> values = parse_element_list(f);
+ Array<Ast *> values = parse_enum_field_list(f);
Token close = expect_closing_brace_of_field_list(f);
return ast_enum_type(f, token, base_type, values);
@@ -4561,6 +4662,12 @@ Ast *parse_stmt(AstFile *f) {
} else if (tag == "no_bounds_check") {
s = parse_stmt(f);
return parse_check_directive_for_statement(s, name, StateFlag_no_bounds_check);
+ } else if (tag == "type_assert") {
+ s = parse_stmt(f);
+ return parse_check_directive_for_statement(s, name, StateFlag_type_assert);
+ } else if (tag == "no_type_assert") {
+ s = parse_stmt(f);
+ return parse_check_directive_for_statement(s, name, StateFlag_no_type_assert);
} else if (tag == "partial") {
s = parse_stmt(f);
switch (s->kind) {
@@ -5398,7 +5505,7 @@ bool parse_file(Parser *p, AstFile *f) {
String filepath = f->tokenizer.fullpath;
String base_dir = dir_from_path(filepath);
if (f->curr_token.kind == Token_Comment) {
- comsume_comment_groups(f, f->prev_token);
+ consume_comment_groups(f, f->prev_token);
}
CommentGroup *docs = f->lead_comment;
@@ -5444,8 +5551,17 @@ bool parse_file(Parser *p, AstFile *f) {
if (!parse_build_tag(tok, lc)) {
return false;
}
- } else if (lc == "+private") {
- f->flags |= AstFile_IsPrivate;
+ } else if (string_starts_with(lc, str_lit("+private"))) {
+ f->flags |= AstFile_IsPrivatePkg;
+ String command = string_trim_starts_with(lc, str_lit("+private "));
+ command = string_trim_whitespace(command);
+ if (lc == "+private") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "package") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "file") {
+ f->flags |= AstFile_IsPrivateFile;
+ }
} else if (lc == "+lazy") {
if (build_context.ignore_lazy) {
// Ignore
diff --git a/src/parser.hpp b/src/parser.hpp
index b83822cbf..ff0df0382 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -78,9 +78,11 @@ struct ImportedFile {
};
enum AstFileFlag : u32 {
- AstFile_IsPrivate = 1<<0,
- AstFile_IsTest = 1<<1,
- AstFile_IsLazy = 1<<2,
+ AstFile_IsPrivatePkg = 1<<0,
+ AstFile_IsPrivateFile = 1<<1,
+
+ AstFile_IsTest = 1<<3,
+ AstFile_IsLazy = 1<<4,
};
enum AstDelayQueueKind {
@@ -226,6 +228,8 @@ enum ProcInlining {
enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
+ ProcTag_type_assert = 1<<2,
+ ProcTag_no_type_assert = 1<<3,
ProcTag_require_results = 1<<4,
ProcTag_optional_ok = 1<<5,
@@ -258,6 +262,8 @@ ProcCallingConvention default_calling_convention(void) {
enum StateFlag : u8 {
StateFlag_bounds_check = 1<<0,
StateFlag_no_bounds_check = 1<<1,
+ StateFlag_type_assert = 1<<2,
+ StateFlag_no_type_assert = 1<<3,
StateFlag_BeenHandled = 1<<7,
};
@@ -344,6 +350,7 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
Slice<Ast *> elems; \
Token open, close; \
i64 max_count; \
+ Ast *tag; \
}) \
AST_KIND(_ExprBegin, "", bool) \
AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
@@ -383,6 +390,12 @@ AST_KIND(_ExprBegin, "", bool) \
void *sce_temp_data; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
+ AST_KIND(EnumFieldValue, "enum field value", struct { \
+ Ast *name; \
+ Ast *value; \
+ CommentGroup *docs; \
+ CommentGroup *comment; \
+ }) \
AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(OrElseExpr, "or_else expression", struct { Ast *x; Token token; Ast *y; }) \
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index 6ef0db215..54c3ec1f1 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -39,6 +39,7 @@ Token ast_token(Ast *node) {
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
+ case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
@@ -178,6 +179,11 @@ Token ast_end_token(Ast *node) {
}
return node->Ellipsis.token;
case Ast_FieldValue: return ast_end_token(node->FieldValue.value);
+ case Ast_EnumFieldValue:
+ if (node->EnumFieldValue.value) {
+ return ast_end_token(node->EnumFieldValue.value);
+ }
+ return ast_end_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y);
case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y);
diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp
index ca7df3b53..b45997916 100644
--- a/src/ptr_set.cpp
+++ b/src/ptr_set.cpp
@@ -138,6 +138,15 @@ gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
return index != MAP_SENTINEL;
}
+template <typename T>
+gb_inline isize ptr_entry_index(PtrSet<T> *s, T ptr) {
+ isize index = ptr_set__find(s, ptr).entry_index;
+ if (index != MAP_SENTINEL) {
+ return index;
+ }
+ return -1;
+}
+
// Returns true if it already exists
template <typename T>
T ptr_set_add(PtrSet<T> *s, T ptr) {
diff --git a/src/string.cpp b/src/string.cpp
index 800378689..eb6058f78 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -195,8 +195,6 @@ template <isize N> bool operator > (String const &a, char const (&b)[N]) { retu
template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
-
-
gb_inline bool string_starts_with(String const &s, String const &prefix) {
if (prefix.len > s.len) {
return false;
@@ -230,6 +228,16 @@ gb_inline bool string_ends_with(String const &s, u8 suffix) {
return s[s.len-1] == suffix;
}
+
+
+gb_inline String string_trim_starts_with(String const &s, String const &prefix) {
+ if (string_starts_with(s, prefix)) {
+ return substring(s, prefix.len, s.len);
+ }
+ return s;
+}
+
+
gb_inline isize string_extension_position(String const &str) {
isize dot_pos = -1;
isize i = str.len;
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 20815fd16..40bc5c220 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -201,14 +201,6 @@ struct TokenPos {
i32 column; // starting at 1
};
-// temporary
-char *token_pos_to_string(TokenPos const &pos) {
- gbString s = gb_string_make_reserve(temporary_allocator(), 128);
- String file = get_file_path_string(pos.file_id);
- s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
- return s;
-}
-
i32 token_pos_cmp(TokenPos const &a, TokenPos const &b) {
if (a.offset != b.offset) {
return (a.offset < b.offset) ? -1 : +1;
diff --git a/src/types.cpp b/src/types.cpp
index 07951196a..9ee6ba359 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -221,6 +221,7 @@ struct TypeProc {
ExactValue *max_value; \
i64 count; \
TokenKind op; \
+ bool is_sparse; \
}) \
TYPE_KIND(Slice, struct { Type *elem; }) \
TYPE_KIND(DynamicArray, struct { Type *elem; }) \
@@ -362,6 +363,7 @@ enum TypeInfoFlag : u32 {
enum : int {
MATRIX_ELEMENT_COUNT_MIN = 1,
MATRIX_ELEMENT_COUNT_MAX = 16,
+ MATRIX_ELEMENT_MAX_SIZE = MATRIX_ELEMENT_COUNT_MAX * (2 * 8), // complex128
};
@@ -1582,6 +1584,24 @@ Type *core_array_type(Type *t) {
}
}
+i32 type_math_rank(Type *t) {
+ i32 rank = 0;
+ for (;;) {
+ t = base_type(t);
+ switch (t->kind) {
+ case Type_Array:
+ rank += 1;
+ t = t->Array.elem;
+ break;
+ case Type_Matrix:
+ rank += 2;
+ t = t->Matrix.elem;
+ break;
+ default:
+ return rank;
+ }
+ }
+}
Type *base_complex_elem_type(Type *t) {
@@ -3830,6 +3850,9 @@ gbString write_type_to_string(gbString str, Type *type) {
break;
case Type_EnumeratedArray:
+ if (type->EnumeratedArray.is_sparse) {
+ str = gb_string_appendc(str, "#sparse");
+ }
str = gb_string_append_rune(str, '[');
str = write_type_to_string(str, type->EnumeratedArray.index);
str = gb_string_append_rune(str, ']');