mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-10 21:31:37 -07:00
Merge branch 'master' into odin-global-constants-as-enums
This commit is contained in:
+52
-1
@@ -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;
|
||||
|
||||
|
||||
+39
-4
@@ -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:
|
||||
|
||||
+89
-2
@@ -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) {
|
||||
|
||||
+2083
-1716
File diff suppressed because it is too large
Load Diff
+15
-93
@@ -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));
|
||||
|
||||
+29
-17
@@ -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;
|
||||
|
||||
|
||||
+134
-24
@@ -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;
|
||||
|
||||
@@ -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},
|
||||
|
||||
+15
-5
@@ -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;
|
||||
|
||||
|
||||
+41
-21
@@ -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;
|
||||
|
||||
+5
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ struct lbTargetList {
|
||||
|
||||
enum lbProcedureFlag : u32 {
|
||||
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
|
||||
lbProcedureFlag_DebugAllocaCopy = 1<<1,
|
||||
};
|
||||
|
||||
struct lbCopyElisionHint {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
+77
-35
@@ -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,28 +2798,30 @@ 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);
|
||||
|
||||
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[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 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[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);
|
||||
} else if (is_type_any(t)) {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
+2
-1
@@ -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") {
|
||||
|
||||
+123
-7
@@ -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:
|
||||
@@ -2062,6 +2135,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);
|
||||
@@ -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
|
||||
|
||||
+16
-3
@@ -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; }) \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+10
-2
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ']');
|
||||
|
||||
Reference in New Issue
Block a user