mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-24 06:35:00 -07:00
Merge branch 'master' into new-temp-allocator
This commit is contained in:
@@ -288,7 +288,8 @@ struct BuildContext {
|
||||
|
||||
bool ignore_warnings;
|
||||
bool warnings_as_errors;
|
||||
bool show_error_line;
|
||||
bool hide_error_line;
|
||||
bool has_ansi_terminal_colours;
|
||||
|
||||
bool ignore_lazy;
|
||||
bool ignore_llvm_build;
|
||||
@@ -1033,7 +1034,10 @@ gb_internal String get_fullpath_core(gbAllocator a, String path) {
|
||||
}
|
||||
|
||||
gb_internal bool show_error_line(void) {
|
||||
return build_context.show_error_line;
|
||||
return !build_context.hide_error_line;
|
||||
}
|
||||
gb_internal bool has_ansi_terminal_colours(void) {
|
||||
return build_context.has_ansi_terminal_colours;
|
||||
}
|
||||
|
||||
gb_internal bool has_asm_extension(String const &path) {
|
||||
|
||||
+7
-2
@@ -578,7 +578,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
|
||||
if (operand.mode == Addressing_Invalid ||
|
||||
base_type(operand.type) == t_invalid) {
|
||||
gbString str = expr_to_string(init);
|
||||
error(e->token, "Invalid declaration type '%s'", str);
|
||||
error(init, "Invalid declaration value '%s'", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
|
||||
@@ -816,9 +816,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
if (ac.test) {
|
||||
e->flags |= EntityFlag_Test;
|
||||
}
|
||||
if (ac.init) {
|
||||
if (ac.init && ac.fini) {
|
||||
error(e->token, "A procedure cannot be both declared as @(init) and @(fini)");
|
||||
} else if (ac.init) {
|
||||
e->flags |= EntityFlag_Init;
|
||||
} else if (ac.fini) {
|
||||
e->flags |= EntityFlag_Fini;
|
||||
}
|
||||
|
||||
if (ac.set_cold) {
|
||||
e->flags |= EntityFlag_Cold;
|
||||
}
|
||||
|
||||
+265
-126
@@ -1679,9 +1679,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
|
||||
|
||||
case Token_Not:
|
||||
if (!is_type_boolean(type)) {
|
||||
ERROR_BLOCK();
|
||||
str = expr_to_string(o->expr);
|
||||
error(op, "Operator '%.*s' is only allowed on boolean expression", LIT(op.string));
|
||||
error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
|
||||
gb_string_free(str);
|
||||
if (is_type_integer(type)) {
|
||||
error_line("\tSuggestion: Did you mean to use the bitwise not operator '~'?\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2019,6 +2023,47 @@ gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue i
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
|
||||
if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
|
||||
gbString b = type_to_string(type);
|
||||
|
||||
i64 sz = type_size_of(type);
|
||||
BigInt *bi = &o->value.value_integer;
|
||||
if (is_type_unsigned(type)) {
|
||||
if (big_int_is_neg(bi)) {
|
||||
error_line("\tA negative value cannot be represented by the unsigned integer type '%s'\n", b);
|
||||
} else {
|
||||
BigInt one = big_int_make_u64(1);
|
||||
BigInt max_size = big_int_make_u64(1);
|
||||
BigInt bits = big_int_make_i64(8*sz);
|
||||
big_int_shl_eq(&max_size, &bits);
|
||||
big_int_sub_eq(&max_size, &one);
|
||||
String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
|
||||
error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
|
||||
}
|
||||
} else {
|
||||
BigInt zero = big_int_make_u64(0);
|
||||
BigInt one = big_int_make_u64(1);
|
||||
BigInt max_size = big_int_make_u64(1);
|
||||
BigInt bits = big_int_make_i64(8*sz - 1);
|
||||
big_int_shl_eq(&max_size, &bits);
|
||||
if (big_int_is_neg(bi)) {
|
||||
big_int_neg(&max_size, &max_size);
|
||||
String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
|
||||
error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
|
||||
} else {
|
||||
big_int_sub_eq(&max_size, &one);
|
||||
String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
|
||||
error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
|
||||
}
|
||||
}
|
||||
|
||||
gb_string_free(b);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
|
||||
gbString a = expr_to_string(o->expr);
|
||||
gbString b = type_to_string(type);
|
||||
@@ -2050,6 +2095,8 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
|
||||
error_line("\t whereas slices in general are assumed to be mutable.\n");
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
|
||||
error_line("\tSuggestion: the expression may be casted to %s\n", b);
|
||||
} else if (check_integer_exceed_suggestion(c, o, type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2089,8 +2136,8 @@ gb_internal void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type
|
||||
}
|
||||
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
|
||||
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
|
||||
error_line("\tSuggestion: the expression may be casted to %s\n", b);
|
||||
} else if (check_integer_exceed_suggestion(c, o, type)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2116,16 +2163,22 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ
|
||||
o->mode = Addressing_Invalid;
|
||||
);
|
||||
|
||||
ERROR_BLOCK();
|
||||
|
||||
|
||||
if (is_type_numeric(o->type) && is_type_numeric(type)) {
|
||||
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
||||
error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s);
|
||||
if (are_types_identical(o->type, type)) {
|
||||
error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b);
|
||||
} else {
|
||||
error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c);
|
||||
}
|
||||
|
||||
check_assignment_error_suggestion(ctx, o, type);
|
||||
}
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
|
||||
check_assignment_error_suggestion(ctx, o, type);
|
||||
}
|
||||
@@ -2344,7 +2397,7 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t
|
||||
}
|
||||
|
||||
|
||||
gb_internal void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
|
||||
gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Operand *y, TokenKind op) {
|
||||
if (x->mode == Addressing_Type && y->mode == Addressing_Type) {
|
||||
bool comp = are_types_identical(x->type, y->type);
|
||||
switch (op) {
|
||||
@@ -2432,7 +2485,7 @@ gb_internal void check_comparison(CheckerContext *c, Operand *x, Operand *y, Tok
|
||||
}
|
||||
|
||||
if (err_str != nullptr) {
|
||||
error(x->expr, "Cannot compare expression, %s", err_str);
|
||||
error(node, "Cannot compare expression, %s", err_str);
|
||||
x->type = t_untyped_bool;
|
||||
} else {
|
||||
if (x->mode == Addressing_Constant &&
|
||||
@@ -2597,10 +2650,12 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
|
||||
x->type = t_untyped_integer;
|
||||
}
|
||||
|
||||
x->expr = node;
|
||||
x->value = exact_value_shift(be->op.kind, x_val, y_val);
|
||||
|
||||
|
||||
if (is_type_typed(x->type)) {
|
||||
check_is_expressible(c, x, base_type(x->type));
|
||||
check_is_expressible(c, x, x->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2915,17 +2970,38 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
|
||||
bool can_convert = check_cast_internal(c, x, type);
|
||||
|
||||
if (!can_convert) {
|
||||
gbString expr_str = expr_to_string(x->expr);
|
||||
gbString to_type = type_to_string(type);
|
||||
gbString from_type = type_to_string(x->type);
|
||||
error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type);
|
||||
gb_string_free(from_type);
|
||||
gb_string_free(to_type);
|
||||
gb_string_free(expr_str);
|
||||
|
||||
check_cast_error_suggestion(c, x, type);
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
gbString expr_str = expr_to_string(x->expr, temporary_allocator());
|
||||
gbString to_type = type_to_string(type, temporary_allocator());
|
||||
gbString from_type = type_to_string(x->type, temporary_allocator());
|
||||
|
||||
x->mode = Addressing_Invalid;
|
||||
|
||||
begin_error_block();
|
||||
error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type);
|
||||
if (is_const_expr) {
|
||||
gbString val_str = exact_value_to_string(x->value);
|
||||
if (is_type_float(x->type) && is_type_integer(type)) {
|
||||
error_line("\t%s cannot be represented without truncation/rounding as the type '%s'\n", val_str, to_type);
|
||||
|
||||
// NOTE(bill): keep the mode and modify the type to minimize errors further on
|
||||
x->mode = Addressing_Constant;
|
||||
x->type = type;
|
||||
} else {
|
||||
error_line("\t'%s' cannot be represented as the type '%s'\n", val_str, to_type);
|
||||
if (is_type_numeric(type)) {
|
||||
// NOTE(bill): keep the mode and modify the type to minimize errors further on
|
||||
x->mode = Addressing_Constant;
|
||||
x->type = type;
|
||||
}
|
||||
}
|
||||
gb_string_free(val_str);
|
||||
|
||||
}
|
||||
check_cast_error_suggestion(c, x, type);
|
||||
|
||||
end_error_block();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3422,7 +3498,7 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
|
||||
|
||||
|
||||
if (token_is_comparison(op.kind)) {
|
||||
check_comparison(c, x, y, op.kind);
|
||||
check_comparison(c, node, x, y, op.kind);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4539,7 +4615,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
entity = scope_lookup_current(import_scope, entity_name);
|
||||
bool allow_builtin = false;
|
||||
if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
|
||||
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
|
||||
error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->expr = node;
|
||||
|
||||
@@ -4559,7 +4635,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
|
||||
if (!is_entity_exported(entity, allow_builtin)) {
|
||||
gbString sel_str = expr_to_string(selector);
|
||||
error(op_expr, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
|
||||
error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
|
||||
gb_string_free(sel_str);
|
||||
// NOTE(bill): make the state valid still, even if it's "invalid"
|
||||
// operand->mode = Addressing_Invalid;
|
||||
@@ -4730,20 +4806,29 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
gbString op_str = expr_to_string(op_expr);
|
||||
gbString type_str = type_to_string_shorthand(operand->type);
|
||||
gbString sel_str = expr_to_string(selector);
|
||||
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
|
||||
|
||||
if (operand->type != nullptr && selector->kind == Ast_Ident) {
|
||||
String const &name = selector->Ident.token.string;
|
||||
Type *bt = base_type(operand->type);
|
||||
if (operand->type->kind == Type_Named &&
|
||||
operand->type->Named.type_name &&
|
||||
operand->type->Named.type_name->kind == Entity_TypeName &&
|
||||
operand->type->Named.type_name->TypeName.objc_metadata) {
|
||||
check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
|
||||
} else if (bt->kind == Type_Struct) {
|
||||
check_did_you_mean_type(name, bt->Struct.fields);
|
||||
} else if (bt->kind == Type_Enum) {
|
||||
check_did_you_mean_type(name, bt->Enum.fields);
|
||||
if (operand->mode == Addressing_Type) {
|
||||
if (is_type_polymorphic(operand->type, true)) {
|
||||
error(op_expr, "Type '%s' has no field nor polymorphic parameter '%s'", op_str, sel_str);
|
||||
} else {
|
||||
error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str);
|
||||
}
|
||||
} else {
|
||||
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
|
||||
|
||||
if (operand->type != nullptr && selector->kind == Ast_Ident) {
|
||||
String const &name = selector->Ident.token.string;
|
||||
Type *bt = base_type(operand->type);
|
||||
if (operand->type->kind == Type_Named &&
|
||||
operand->type->Named.type_name &&
|
||||
operand->type->Named.type_name->kind == Entity_TypeName &&
|
||||
operand->type->Named.type_name->TypeName.objc_metadata) {
|
||||
check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
|
||||
} else if (bt->kind == Type_Struct) {
|
||||
check_did_you_mean_type(name, bt->Struct.fields);
|
||||
} else if (bt->kind == Type_Enum) {
|
||||
check_did_you_mean_type(name, bt->Enum.fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5418,7 +5503,18 @@ gb_internal CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
data->score = score;
|
||||
data->result_type = final_proc_type->Proc.results;
|
||||
data->gen_entity = gen_entity;
|
||||
add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
|
||||
|
||||
|
||||
Ast *proc_lit = nullptr;
|
||||
if (ce->proc->tav.value.kind == ExactValue_Procedure) {
|
||||
Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure);
|
||||
if (vp && vp->kind == Ast_ProcLit) {
|
||||
proc_lit = vp;
|
||||
}
|
||||
}
|
||||
if (proc_lit == nullptr) {
|
||||
add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -7033,7 +7129,7 @@ gb_internal bool ternary_compare_types(Type *x, Type *y) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) {
|
||||
gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) {
|
||||
if (!is_ast_range(node)) {
|
||||
return false;
|
||||
}
|
||||
@@ -7082,9 +7178,17 @@ gb_internal bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *
|
||||
}
|
||||
|
||||
Type *type = x->type;
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
return false;
|
||||
|
||||
if (is_for_loop) {
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_enum(type)) {
|
||||
error(ie->op, "Only numerical types are allowed within interval expressions");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (x->mode == Addressing_Constant &&
|
||||
@@ -7790,6 +7894,124 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
|
||||
gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<Ast *> const &elems, Operand *o, Type *type, bool &is_constant) {
|
||||
Type *bt = base_type(type);
|
||||
|
||||
StringSet fields_visited = {};
|
||||
defer (string_set_destroy(&fields_visited));
|
||||
|
||||
StringMap<String> fields_visited_through_raw_union = {};
|
||||
defer (string_map_destroy(&fields_visited_through_raw_union));
|
||||
|
||||
for (Ast *elem : elems) {
|
||||
if (elem->kind != Ast_FieldValue) {
|
||||
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
|
||||
continue;
|
||||
}
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (fv->field->kind != Ast_Ident) {
|
||||
gbString expr_str = expr_to_string(fv->field);
|
||||
error(elem, "Invalid field name '%s' in structure literal", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
continue;
|
||||
}
|
||||
String name = fv->field->Ident.token.string;
|
||||
|
||||
Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
|
||||
bool is_unknown = sel.entity == nullptr;
|
||||
if (is_unknown) {
|
||||
error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity *field = bt->Struct.fields[sel.index[0]];
|
||||
add_entity_use(c, fv->field, field);
|
||||
if (string_set_update(&fields_visited, name)) {
|
||||
if (sel.index.count > 1) {
|
||||
if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
|
||||
error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
|
||||
} else {
|
||||
error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string));
|
||||
}
|
||||
} else {
|
||||
error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
|
||||
}
|
||||
continue;
|
||||
} else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
|
||||
error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
|
||||
continue;
|
||||
}
|
||||
if (sel.indirect) {
|
||||
error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sel.index.count > 1) {
|
||||
if (is_constant) {
|
||||
Type *ft = type;
|
||||
for (i32 index : sel.index) {
|
||||
Type *bt = base_type(ft);
|
||||
switch (bt->kind) {
|
||||
case Type_Struct:
|
||||
if (bt->Struct.is_raw_union) {
|
||||
is_constant = false;
|
||||
break;
|
||||
}
|
||||
ft = bt->Struct.fields[index]->type;
|
||||
break;
|
||||
case Type_Array:
|
||||
ft = bt->Array.elem;
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("invalid type: %s", type_to_string(ft));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_constant &&
|
||||
(is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
|
||||
is_constant = false;
|
||||
}
|
||||
}
|
||||
|
||||
Type *nested_ft = bt;
|
||||
for (i32 index : sel.index) {
|
||||
Type *bt = base_type(nested_ft);
|
||||
switch (bt->kind) {
|
||||
case Type_Struct:
|
||||
if (bt->Struct.is_raw_union) {
|
||||
for (Entity *re : bt->Struct.fields) {
|
||||
string_map_set(&fields_visited_through_raw_union, re->token.string, sel.entity->token.string);
|
||||
}
|
||||
}
|
||||
nested_ft = bt->Struct.fields[index]->type;
|
||||
break;
|
||||
case Type_Array:
|
||||
nested_ft = bt->Array.elem;
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("invalid type %s", type_to_string(nested_ft));
|
||||
break;
|
||||
}
|
||||
}
|
||||
field = sel.entity;
|
||||
}
|
||||
|
||||
|
||||
Operand o = {};
|
||||
check_expr_or_type(c, &o, fv->value, field->type);
|
||||
|
||||
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
|
||||
is_constant = false;
|
||||
}
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o);
|
||||
}
|
||||
|
||||
check_assignment(c, &o, field->type, str_lit("structure literal"));
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
|
||||
ExprKind kind = Expr_Expr;
|
||||
ast_node(cl, CompoundLit, node);
|
||||
@@ -7877,45 +8099,13 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
|
||||
gb_string_free(type_str);
|
||||
} else {
|
||||
Ast *elem = cl->elems[0];
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (fv->field->kind != Ast_Ident) {
|
||||
gbString expr_str = expr_to_string(fv->field);
|
||||
error(elem, "Invalid field name '%s' in structure literal", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
break;
|
||||
}
|
||||
|
||||
String name = fv->field->Ident.token.string;
|
||||
|
||||
Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
|
||||
bool is_unknown = sel.entity == nullptr;
|
||||
if (is_unknown) {
|
||||
error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
|
||||
break;
|
||||
}
|
||||
|
||||
if (sel.index.count > 1) {
|
||||
error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
|
||||
break;
|
||||
}
|
||||
|
||||
Entity *field = t->Struct.fields[sel.index[0]];
|
||||
add_entity_use(c, fv->field, field);
|
||||
|
||||
Operand o = {};
|
||||
check_expr_or_type(c, &o, fv->value, field->type);
|
||||
|
||||
|
||||
check_assignment(c, &o, field->type, str_lit("structure literal"));
|
||||
check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
isize field_count = t->Struct.fields.count;
|
||||
isize min_field_count = t->Struct.fields.count;
|
||||
for (isize i = min_field_count-1; i >= 0; i--) {
|
||||
@@ -7929,58 +8119,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
}
|
||||
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
|
||||
|
||||
for (Ast *elem : cl->elems) {
|
||||
if (elem->kind != Ast_FieldValue) {
|
||||
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
|
||||
continue;
|
||||
}
|
||||
ast_node(fv, FieldValue, elem);
|
||||
if (fv->field->kind != Ast_Ident) {
|
||||
gbString expr_str = expr_to_string(fv->field);
|
||||
error(elem, "Invalid field name '%s' in structure literal", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
continue;
|
||||
}
|
||||
String name = fv->field->Ident.token.string;
|
||||
|
||||
Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
|
||||
bool is_unknown = sel.entity == nullptr;
|
||||
if (is_unknown) {
|
||||
error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sel.index.count > 1) {
|
||||
error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity *field = t->Struct.fields[sel.index[0]];
|
||||
add_entity_use(c, fv->field, field);
|
||||
|
||||
if (fields_visited[sel.index[0]]) {
|
||||
error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
|
||||
continue;
|
||||
}
|
||||
|
||||
fields_visited[sel.index[0]] = true;
|
||||
|
||||
Operand o = {};
|
||||
check_expr_or_type(c, &o, fv->value, field->type);
|
||||
|
||||
if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
|
||||
is_constant = false;
|
||||
}
|
||||
if (is_constant) {
|
||||
is_constant = check_is_operand_compound_lit_constant(c, &o);
|
||||
}
|
||||
|
||||
check_assignment(c, &o, field->type, str_lit("structure literal"));
|
||||
}
|
||||
check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
|
||||
} else {
|
||||
bool seen_field_value = false;
|
||||
|
||||
@@ -8097,7 +8236,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
bool ok = check_range(c, fv->field, &x, &y, nullptr);
|
||||
bool ok = check_range(c, fv->field, false, &x, &y, nullptr);
|
||||
if (!ok) {
|
||||
continue;
|
||||
}
|
||||
@@ -8313,7 +8452,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
|
||||
bool ok = check_range(c, fv->field, false, &x, &y, nullptr, index_type);
|
||||
if (!ok) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+7
-8
@@ -725,7 +725,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
|
||||
bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth);
|
||||
bool ok = check_range(ctx, expr, true, &x, &y, &inline_for_depth);
|
||||
if (!ok) {
|
||||
goto skip_expr;
|
||||
}
|
||||
@@ -978,19 +978,19 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
|
||||
|
||||
Operand a = lhs;
|
||||
Operand b = rhs;
|
||||
check_comparison(ctx, &a, &x, Token_LtEq);
|
||||
check_comparison(ctx, expr, &a, &x, Token_LtEq);
|
||||
if (a.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
check_comparison(ctx, &b, &x, upper_op);
|
||||
check_comparison(ctx, expr, &b, &x, upper_op);
|
||||
if (b.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Operand a1 = lhs;
|
||||
Operand b1 = rhs;
|
||||
check_comparison(ctx, &a1, &b1, Token_LtEq);
|
||||
check_comparison(ctx, expr, &a1, &b1, Token_LtEq);
|
||||
|
||||
add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
|
||||
|
||||
@@ -1029,7 +1029,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
|
||||
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(ctx, &z, &x, Token_CmpEq);
|
||||
check_comparison(ctx, expr, &z, &x, Token_CmpEq);
|
||||
if (z.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
@@ -1293,7 +1293,6 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
|
||||
for (Type *t : variants) {
|
||||
if (!type_ptr_set_exists(&seen, t)) {
|
||||
array_add(&unhandled, t);
|
||||
gb_printf_err("HERE: %p %s\n", t, type_to_string(t));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1439,7 +1438,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
|
||||
bool ok = check_range(ctx, expr, &x, &y, nullptr);
|
||||
bool ok = check_range(ctx, expr, true, &x, &y, nullptr);
|
||||
if (!ok) {
|
||||
goto skip_expr_range_stmt;
|
||||
}
|
||||
@@ -1921,7 +1920,7 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
|
||||
case Addressing_Type:
|
||||
{
|
||||
gbString str = type_to_string(operand.type);
|
||||
error(node, "'%s' is not an expression", str);
|
||||
error(node, "'%s' is not an expression but a type and cannot be used as a statement", str);
|
||||
gb_string_free(str);
|
||||
break;
|
||||
}
|
||||
|
||||
+10
-4
@@ -1309,6 +1309,8 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
|
||||
init_core_source_code_location(ctx->checker);
|
||||
param_value.kind = ParameterValue_Location;
|
||||
o.type = t_source_code_location;
|
||||
o.mode = Addressing_Value;
|
||||
o.expr = expr;
|
||||
|
||||
if (in_type) {
|
||||
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
|
||||
@@ -1666,17 +1668,21 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (is_poly_name) {
|
||||
bool valid = false;
|
||||
if (is_type_proc(op.type)) {
|
||||
Entity *proc_entity = entity_from_expr(op.expr);
|
||||
valid = (proc_entity != nullptr) && (op.value.kind == ExactValue_Procedure);
|
||||
if (valid) {
|
||||
Ast *expr = unparen_expr(op.expr);
|
||||
Entity *proc_entity = entity_from_expr(expr);
|
||||
if (proc_entity) {
|
||||
poly_const = exact_value_procedure(proc_entity->identifier.load() ? proc_entity->identifier.load() : op.expr);
|
||||
valid = true;
|
||||
} else if (expr->kind == Ast_ProcLit) {
|
||||
poly_const = exact_value_procedure(expr);
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
if (!valid) {
|
||||
if (op.mode == Addressing_Constant) {
|
||||
poly_const = op.value;
|
||||
} else {
|
||||
error(op.expr, "Expected a constant value for this polymorphic name parameter");
|
||||
error(op.expr, "Expected a constant value for this polymorphic name parameter, got %s", expr_to_string(op.expr));
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
+49
-8
@@ -1155,6 +1155,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
|
||||
array_init(&i->variable_init_order, a);
|
||||
array_init(&i->testing_procedures, a, 0, 0);
|
||||
array_init(&i->init_procedures, a, 0, 0);
|
||||
array_init(&i->fini_procedures, a, 0, 0);
|
||||
array_init(&i->required_foreign_imports_through_force, a, 0, 0);
|
||||
|
||||
map_init(&i->objc_msgSend_types);
|
||||
@@ -1422,7 +1423,7 @@ gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_f
|
||||
}
|
||||
|
||||
|
||||
gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue value) {
|
||||
gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
|
||||
if (expr == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -1439,7 +1440,7 @@ gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode,
|
||||
check_set_expr_info(c, expr, mode, type, value);
|
||||
}
|
||||
|
||||
gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue value) {
|
||||
gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
|
||||
if (expr == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -1546,7 +1547,7 @@ gb_internal void add_entity_flags_from_file(CheckerContext *c, Entity *e, Scope
|
||||
AstPackage *pkg = c->file->pkg;
|
||||
if (pkg->kind == Package_Init && e->kind == Entity_Procedure && e->token.string == "main") {
|
||||
// Do nothing
|
||||
} else if (e->flags & (EntityFlag_Test|EntityFlag_Init)) {
|
||||
} else if (e->flags & (EntityFlag_Test|EntityFlag_Init|EntityFlag_Fini)) {
|
||||
// Do nothing
|
||||
} else {
|
||||
e->flags |= EntityFlag_Lazy;
|
||||
@@ -1614,7 +1615,7 @@ gb_internal bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e->flags & (EntityFlag_Test|EntityFlag_Init)) {
|
||||
if (e->flags & (EntityFlag_Test|EntityFlag_Init|EntityFlag_Fini)) {
|
||||
return false;
|
||||
} else if (e->kind == Entity_Variable && e->Variable.is_export) {
|
||||
return false;
|
||||
@@ -2423,6 +2424,28 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
|
||||
add_dependency_to_set(c, e);
|
||||
array_add(&c->info.init_procedures, e);
|
||||
}
|
||||
} else if (e->flags & EntityFlag_Fini) {
|
||||
Type *t = base_type(e->type);
|
||||
GB_ASSERT(t->kind == Type_Proc);
|
||||
|
||||
bool is_fini = true;
|
||||
|
||||
if (t->Proc.param_count != 0 || t->Proc.result_count != 0) {
|
||||
gbString str = type_to_string(t);
|
||||
error(e->token, "@(fini) procedures must have a signature type with no parameters nor results, got %s", str);
|
||||
gb_string_free(str);
|
||||
is_fini = false;
|
||||
}
|
||||
|
||||
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
|
||||
error(e->token, "@(fini) procedures must be declared at the file scope");
|
||||
is_fini = false;
|
||||
}
|
||||
|
||||
if (is_fini) {
|
||||
add_dependency_to_set(c, e);
|
||||
array_add(&c->info.fini_procedures, e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2974,6 +2997,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
}
|
||||
ac->init = true;
|
||||
return true;
|
||||
} else if (name == "fini") {
|
||||
if (value != nullptr) {
|
||||
error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
|
||||
}
|
||||
ac->fini = true;
|
||||
return true;
|
||||
} else if (name == "deferred") {
|
||||
if (value != nullptr) {
|
||||
Operand o = {};
|
||||
@@ -3615,6 +3644,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
EntityVisiblityKind entity_visibility_kind = c->foreign_context.visibility_kind;
|
||||
bool is_test = false;
|
||||
bool is_init = false;
|
||||
bool is_fini = false;
|
||||
|
||||
for_array(i, vd->attributes) {
|
||||
Ast *attr = vd->attributes[i];
|
||||
@@ -3674,6 +3704,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
is_test = true;
|
||||
} else if (name == "init") {
|
||||
is_init = true;
|
||||
} else if (name == "fini") {
|
||||
is_fini = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3807,8 +3839,12 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
if (is_test) {
|
||||
e->flags |= EntityFlag_Test;
|
||||
}
|
||||
if (is_init) {
|
||||
if (is_init && is_fini) {
|
||||
error(name, "A procedure cannot be both declared as @(init) and @(fini)");
|
||||
} else if (is_init) {
|
||||
e->flags |= EntityFlag_Init;
|
||||
} else if (is_fini) {
|
||||
e->flags |= EntityFlag_Fini;
|
||||
}
|
||||
} else if (init->kind == Ast_ProcGroup) {
|
||||
ast_node(pg, ProcGroup, init);
|
||||
@@ -5634,9 +5670,14 @@ gb_internal GB_COMPARE_PROC(init_procedures_cmp) {
|
||||
return i32_cmp(x->token.pos.offset, y->token.pos.offset);
|
||||
}
|
||||
|
||||
gb_internal GB_COMPARE_PROC(fini_procedures_cmp) {
|
||||
return init_procedures_cmp(b, a);
|
||||
}
|
||||
|
||||
gb_internal void check_sort_init_procedures(Checker *c) {
|
||||
|
||||
gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
|
||||
gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp);
|
||||
gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp);
|
||||
}
|
||||
|
||||
gb_internal void add_type_info_for_type_definitions(Checker *c) {
|
||||
@@ -5846,8 +5887,8 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
add_type_and_value(&c->builtin_ctx, u.expr, u.info->mode, u.info->type, u.info->value);
|
||||
}
|
||||
|
||||
TIME_SECTION("sort init procedures");
|
||||
check_sort_init_procedures(c);
|
||||
TIME_SECTION("sort init and fini procedures");
|
||||
check_sort_init_and_fini_procedures(c);
|
||||
|
||||
if (c->info.intrinsics_entry_point_usage.count > 0) {
|
||||
TIME_SECTION("check intrinsics.__entry_point usage");
|
||||
|
||||
+4
-2
@@ -117,6 +117,7 @@ struct AttributeContext {
|
||||
bool disabled_proc : 1;
|
||||
bool test : 1;
|
||||
bool init : 1;
|
||||
bool fini : 1;
|
||||
bool set_cold : 1;
|
||||
u32 optimization_mode; // ProcedureOptimizationMode
|
||||
i64 foreign_import_priority_index;
|
||||
@@ -351,6 +352,7 @@ struct CheckerInfo {
|
||||
|
||||
Array<Entity *> testing_procedures;
|
||||
Array<Entity *> init_procedures;
|
||||
Array<Entity *> fini_procedures;
|
||||
|
||||
Array<Entity *> definitions;
|
||||
Array<Entity *> entities;
|
||||
@@ -483,9 +485,9 @@ gb_internal void scope_lookup_parent (Scope *s, String const &name, Scope **s
|
||||
gb_internal Entity *scope_insert (Scope *s, Entity *entity);
|
||||
|
||||
|
||||
gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
|
||||
gb_internal void add_type_and_value (CheckerContext *c, Ast *expression, AddressingMode mode, Type *type, ExactValue const &value);
|
||||
gb_internal ExprInfo *check_get_expr_info (CheckerContext *c, Ast *expr);
|
||||
gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue value);
|
||||
gb_internal void add_untyped (CheckerContext *c, Ast *expression, AddressingMode mode, Type *basic_type, ExactValue const &value);
|
||||
gb_internal void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
|
||||
gb_internal void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e);
|
||||
gb_internal void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported=true);
|
||||
|
||||
@@ -53,6 +53,11 @@ struct TypeIsPointer<T *> {
|
||||
enum {value = true};
|
||||
};
|
||||
|
||||
template <typename T> struct TypeIsPtrSizedInteger { enum {value = false}; };
|
||||
template <> struct TypeIsPtrSizedInteger<isize> { enum {value = true}; };
|
||||
template <> struct TypeIsPtrSizedInteger<usize> { enum {value = true}; };
|
||||
|
||||
|
||||
#include "unicode.cpp"
|
||||
#include "array.cpp"
|
||||
#include "threading.cpp"
|
||||
|
||||
@@ -75,6 +75,7 @@ enum EntityFlag : u64 {
|
||||
EntityFlag_Test = 1ull<<30,
|
||||
EntityFlag_Init = 1ull<<31,
|
||||
EntityFlag_Subtype = 1ull<<32,
|
||||
EntityFlag_Fini = 1ull<<33,
|
||||
|
||||
EntityFlag_CustomLinkName = 1ull<<40,
|
||||
EntityFlag_CustomLinkage_Internal = 1ull<<41,
|
||||
|
||||
+171
-51
@@ -97,15 +97,57 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) {
|
||||
|
||||
|
||||
|
||||
// NOTE: defined in build_settings.cpp
|
||||
gb_internal bool global_warnings_as_errors(void);
|
||||
gb_internal bool global_ignore_warnings(void);
|
||||
gb_internal bool show_error_line(void);
|
||||
gb_internal bool has_ansi_terminal_colours(void);
|
||||
gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
|
||||
|
||||
gb_internal void warning(Token const &token, char const *fmt, ...);
|
||||
gb_internal void error(Token const &token, char const *fmt, ...);
|
||||
gb_internal void error(TokenPos pos, char const *fmt, ...);
|
||||
gb_internal void error_line(char const *fmt, ...);
|
||||
gb_internal void syntax_error(Token const &token, char const *fmt, ...);
|
||||
gb_internal void syntax_error(TokenPos pos, char const *fmt, ...);
|
||||
gb_internal void syntax_warning(Token const &token, char const *fmt, ...);
|
||||
gb_internal void compiler_error(char const *fmt, ...);
|
||||
|
||||
gb_internal void begin_error_block(void) {
|
||||
mutex_lock(&global_error_collector.block_mutex);
|
||||
global_error_collector.in_block.store(true);
|
||||
}
|
||||
|
||||
gb_internal void end_error_block(void) {
|
||||
if (global_error_collector.error_buffer.count > 0) {
|
||||
isize n = global_error_collector.error_buffer.count;
|
||||
u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
|
||||
isize n = global_error_collector.error_buffer.count;
|
||||
if (n > 0) {
|
||||
u8 *text = global_error_collector.error_buffer.data;
|
||||
|
||||
bool add_extra_newline = false;
|
||||
|
||||
if (show_error_line()) {
|
||||
if (n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) {
|
||||
add_extra_newline = true;
|
||||
}
|
||||
} else {
|
||||
isize newline_count = 0;
|
||||
for (isize i = 0; i < n; i++) {
|
||||
if (text[i] == '\n') {
|
||||
newline_count += 1;
|
||||
}
|
||||
}
|
||||
if (newline_count > 1) {
|
||||
add_extra_newline = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_extra_newline) {
|
||||
// add an extra new line as padding when the error line is being shown
|
||||
error_line("\n");
|
||||
}
|
||||
|
||||
n = global_error_collector.error_buffer.count;
|
||||
text = gb_alloc_array(permanent_allocator(), u8, n+1);
|
||||
gb_memmove(text, global_error_collector.error_buffer.data, n);
|
||||
text[n] = 0;
|
||||
String s = {text, n};
|
||||
@@ -149,15 +191,8 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) {
|
||||
gb_file_write(f, buf, n);
|
||||
}
|
||||
|
||||
|
||||
gb_global ErrorOutProc *error_out_va = default_error_out_va;
|
||||
|
||||
// NOTE: defined in build_settings.cpp
|
||||
gb_internal bool global_warnings_as_errors(void);
|
||||
gb_internal bool global_ignore_warnings(void);
|
||||
gb_internal bool show_error_line(void);
|
||||
gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
|
||||
|
||||
gb_internal void error_out(char const *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
@@ -165,6 +200,49 @@ gb_internal void error_out(char const *fmt, ...) {
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
enum TerminalStyle {
|
||||
TerminalStyle_Normal,
|
||||
TerminalStyle_Bold,
|
||||
TerminalStyle_Underline,
|
||||
};
|
||||
|
||||
enum TerminalColour {
|
||||
TerminalColour_White,
|
||||
TerminalColour_Red,
|
||||
TerminalColour_Yellow,
|
||||
TerminalColour_Green,
|
||||
TerminalColour_Cyan,
|
||||
TerminalColour_Blue,
|
||||
TerminalColour_Purple,
|
||||
TerminalColour_Black,
|
||||
};
|
||||
|
||||
gb_internal void terminal_set_colours(TerminalStyle style, TerminalColour foreground) {
|
||||
if (has_ansi_terminal_colours()) {
|
||||
char const *ss = "0";
|
||||
switch (style) {
|
||||
case TerminalStyle_Normal: ss = "0"; break;
|
||||
case TerminalStyle_Bold: ss = "1"; break;
|
||||
case TerminalStyle_Underline: ss = "4"; break;
|
||||
}
|
||||
switch (foreground) {
|
||||
case TerminalColour_White: error_out("\x1b[%s;37m", ss); break;
|
||||
case TerminalColour_Red: error_out("\x1b[%s;31m", ss); break;
|
||||
case TerminalColour_Yellow: error_out("\x1b[%s;33m", ss); break;
|
||||
case TerminalColour_Green: error_out("\x1b[%s;32m", ss); break;
|
||||
case TerminalColour_Cyan: error_out("\x1b[%s;36m", ss); break;
|
||||
case TerminalColour_Blue: error_out("\x1b[%s;34m", ss); break;
|
||||
case TerminalColour_Purple: error_out("\x1b[%s;35m", ss); break;
|
||||
case TerminalColour_Black: error_out("\x1b[%s;30m", ss); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
gb_internal void terminal_reset_colours(void) {
|
||||
if (has_ansi_terminal_colours()) {
|
||||
error_out("\x1b[0m");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
|
||||
if (!show_error_line()) {
|
||||
@@ -181,26 +259,33 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
|
||||
// TODO(bill): This assumes ASCII
|
||||
|
||||
enum {
|
||||
MAX_LINE_LENGTH = 76,
|
||||
MAX_LINE_LENGTH = 80,
|
||||
MAX_TAB_WIDTH = 8,
|
||||
ELLIPSIS_PADDING = 8
|
||||
ELLIPSIS_PADDING = 8, // `... ...`
|
||||
MAX_LINE_LENGTH_PADDED = MAX_LINE_LENGTH-MAX_TAB_WIDTH-ELLIPSIS_PADDING,
|
||||
};
|
||||
|
||||
error_out("\n\t");
|
||||
if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
|
||||
i32 const half_width = MAX_LINE_LENGTH/2;
|
||||
i32 left = cast(i32)(offset);
|
||||
i32 right = cast(i32)(line.len - offset);
|
||||
left = gb_min(left, half_width);
|
||||
right = gb_min(right, half_width);
|
||||
error_out("\t");
|
||||
|
||||
terminal_set_colours(TerminalStyle_Bold, TerminalColour_White);
|
||||
|
||||
|
||||
i32 error_length = gb_max(end.offset - pos.offset, 1);
|
||||
|
||||
isize squiggle_extra = 0;
|
||||
|
||||
if (line.len > MAX_LINE_LENGTH_PADDED) {
|
||||
i32 left = MAX_TAB_WIDTH;
|
||||
line.text += offset-left;
|
||||
line.len -= offset+right-left;
|
||||
|
||||
line = string_trim_whitespace(line);
|
||||
|
||||
offset = left + ELLIPSIS_PADDING/2;
|
||||
|
||||
line.len -= offset-left;
|
||||
offset = left+MAX_TAB_WIDTH/2;
|
||||
if (line.len > MAX_LINE_LENGTH_PADDED) {
|
||||
line.len = MAX_LINE_LENGTH_PADDED;
|
||||
if (error_length > line.len-left) {
|
||||
error_length = cast(i32)line.len - left;
|
||||
squiggle_extra = 1;
|
||||
}
|
||||
}
|
||||
error_out("... %.*s ...", LIT(line));
|
||||
} else {
|
||||
error_out("%.*s", LIT(line));
|
||||
@@ -210,6 +295,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
|
||||
for (i32 i = 0; i < offset; i++) {
|
||||
error_out(" ");
|
||||
}
|
||||
|
||||
terminal_set_colours(TerminalStyle_Bold, TerminalColour_Green);
|
||||
|
||||
error_out("^");
|
||||
if (end.file_id == pos.file_id) {
|
||||
if (end.line > pos.line) {
|
||||
@@ -217,34 +305,54 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
|
||||
error_out("~");
|
||||
}
|
||||
} else if (end.line == pos.line && end.column > pos.column) {
|
||||
i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
|
||||
for (i32 i = 1; i < length-1; i++) {
|
||||
for (i32 i = 1; i < error_length-1+squiggle_extra; i++) {
|
||||
error_out("~");
|
||||
}
|
||||
if (length > 1) {
|
||||
if (error_length > 1 && squiggle_extra == 0) {
|
||||
error_out("^");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_out("\n\n");
|
||||
terminal_reset_colours();
|
||||
|
||||
error_out("\n");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void error_out_pos(TokenPos pos) {
|
||||
terminal_set_colours(TerminalStyle_Bold, TerminalColour_White);
|
||||
error_out("%s ", token_pos_to_string(pos));
|
||||
terminal_reset_colours();
|
||||
}
|
||||
|
||||
gb_internal void error_out_coloured(char const *str, TerminalStyle style, TerminalColour foreground) {
|
||||
terminal_set_colours(style, foreground);
|
||||
error_out(str);
|
||||
terminal_reset_colours();
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
|
||||
global_error_collector.count.fetch_add(1);
|
||||
|
||||
mutex_lock(&global_error_collector.mutex);
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (pos.line == 0) {
|
||||
error_out("Error: %s\n", gb_bprintf_va(fmt, va));
|
||||
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
} else if (global_error_collector.prev != pos) {
|
||||
global_error_collector.prev = pos;
|
||||
error_out("%s %s\n",
|
||||
token_pos_to_string(pos),
|
||||
gb_bprintf_va(fmt, va));
|
||||
error_out_pos(pos);
|
||||
if (has_ansi_terminal_colours()) {
|
||||
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
}
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
show_error_on_line(pos, end);
|
||||
}
|
||||
mutex_unlock(&global_error_collector.mutex);
|
||||
@@ -263,12 +371,15 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt,
|
||||
if (!global_ignore_warnings()) {
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (pos.line == 0) {
|
||||
error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
|
||||
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
} else if (global_error_collector.prev != pos) {
|
||||
global_error_collector.prev = pos;
|
||||
error_out("%s Warning: %s\n",
|
||||
token_pos_to_string(pos),
|
||||
gb_bprintf_va(fmt, va));
|
||||
error_out_pos(pos);
|
||||
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
show_error_on_line(pos, end);
|
||||
}
|
||||
}
|
||||
@@ -285,12 +396,15 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (pos.line == 0) {
|
||||
error_out("Error: %s", gb_bprintf_va(fmt, va));
|
||||
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
error_out_va(fmt, va);
|
||||
} else if (global_error_collector.prev != pos) {
|
||||
global_error_collector.prev = pos;
|
||||
error_out("%s %s",
|
||||
token_pos_to_string(pos),
|
||||
gb_bprintf_va(fmt, va));
|
||||
error_out_pos(pos);
|
||||
if (has_ansi_terminal_colours()) {
|
||||
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
}
|
||||
error_out_va(fmt, va);
|
||||
}
|
||||
mutex_unlock(&global_error_collector.mutex);
|
||||
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
|
||||
@@ -305,12 +419,15 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (global_error_collector.prev != pos) {
|
||||
global_error_collector.prev = pos;
|
||||
error_out("%s Syntax Error: %s\n",
|
||||
token_pos_to_string(pos),
|
||||
gb_bprintf_va(fmt, va));
|
||||
show_error_on_line(pos, end);
|
||||
error_out_pos(pos);
|
||||
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
// show_error_on_line(pos, end);
|
||||
} else if (pos.line == 0) {
|
||||
error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
|
||||
error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&global_error_collector.mutex);
|
||||
@@ -330,12 +447,15 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (global_error_collector.prev != pos) {
|
||||
global_error_collector.prev = pos;
|
||||
error_out("%s Syntax Warning: %s\n",
|
||||
token_pos_to_string(pos),
|
||||
gb_bprintf_va(fmt, va));
|
||||
show_error_on_line(pos, end);
|
||||
error_out_pos(pos);
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
// show_error_on_line(pos, end);
|
||||
} else if (pos.line == 0) {
|
||||
error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
}
|
||||
}
|
||||
mutex_unlock(&global_error_collector.mutex);
|
||||
|
||||
+1
-3
@@ -578,9 +578,7 @@ gb_internal ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i3
|
||||
}
|
||||
}
|
||||
|
||||
failure:
|
||||
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
|
||||
|
||||
failure:;
|
||||
ExactValue error_value = {};
|
||||
return error_value;
|
||||
}
|
||||
|
||||
+37
-4
@@ -1161,6 +1161,34 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
return p;
|
||||
}
|
||||
|
||||
gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // Cleanup Runtime
|
||||
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit(LB_CLEANUP_RUNTIME_PROC_NAME), proc_type);
|
||||
p->is_startup = true;
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
CheckerInfo *info = main_module->gen->info;
|
||||
|
||||
for (Entity *e : info->fini_procedures) {
|
||||
lbValue value = lb_find_procedure_value_from_entity(main_module, e);
|
||||
lb_emit_call(p, value, {}, ProcInlining_none);
|
||||
}
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
|
||||
if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
|
||||
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
|
||||
LLVMDumpValue(p->value);
|
||||
gb_printf_err("\n\n\n\n");
|
||||
LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
|
||||
lbModule *m = cast(lbModule *)data;
|
||||
for (Entity *e : m->global_procedures_and_types_to_create) {
|
||||
@@ -1328,6 +1356,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
|
||||
if (m == &m->gen->default_module) {
|
||||
lb_llvm_function_pass_per_function_internal(m, m->gen->startup_type_info);
|
||||
lb_llvm_function_pass_per_function_internal(m, m->gen->startup_runtime);
|
||||
lb_llvm_function_pass_per_function_internal(m, m->gen->cleanup_runtime);
|
||||
lb_llvm_function_pass_per_function_internal(m, m->gen->objc_names);
|
||||
}
|
||||
|
||||
@@ -1674,7 +1703,7 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading)
|
||||
|
||||
|
||||
|
||||
gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime) {
|
||||
gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime, lbProcedure *cleanup_runtime) {
|
||||
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
|
||||
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
|
||||
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
|
||||
@@ -1793,7 +1822,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
||||
|
||||
|
||||
if (call_cleanup) {
|
||||
lbValue cleanup_runtime_value = lb_find_runtime_value(m, str_lit("_cleanup_runtime"));
|
||||
lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
|
||||
lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
|
||||
}
|
||||
|
||||
@@ -2330,9 +2359,13 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
gen->startup_type_info = lb_create_startup_type_info(default_module);
|
||||
gen->objc_names = lb_create_objc_names(default_module);
|
||||
|
||||
TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
|
||||
TIME_SECTION("LLVM Runtime Startup Creation (Global Variables & @(init))");
|
||||
gen->startup_runtime = lb_create_startup_runtime(default_module, gen->startup_type_info, gen->objc_names, global_variables);
|
||||
|
||||
TIME_SECTION("LLVM Runtime Cleanup Creation & @(fini)");
|
||||
gen->cleanup_runtime = lb_create_cleanup_runtime(default_module);
|
||||
|
||||
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
for (auto const &entry : builtin_pkg->scope->elements) {
|
||||
Entity *e = entry.value;
|
||||
@@ -2352,7 +2385,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
|
||||
if (build_context.command_kind == Command_test && !already_has_entry_point) {
|
||||
TIME_SECTION("LLVM main");
|
||||
lb_create_main_procedure(default_module, gen->startup_runtime);
|
||||
lb_create_main_procedure(default_module, gen->startup_runtime, gen->cleanup_runtime);
|
||||
}
|
||||
|
||||
TIME_SECTION("LLVM Procedure Generation (missing)");
|
||||
|
||||
@@ -182,6 +182,8 @@ struct lbModule {
|
||||
PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<Type *, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
|
||||
PtrMap<Ast *, lbAddr> exact_value_compound_literal_addr_map; // Key: Ast_CompoundLit
|
||||
|
||||
LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
|
||||
};
|
||||
|
||||
@@ -208,6 +210,7 @@ struct lbGenerator {
|
||||
|
||||
lbProcedure *startup_type_info;
|
||||
lbProcedure *startup_runtime;
|
||||
lbProcedure *cleanup_runtime;
|
||||
lbProcedure *objc_names;
|
||||
};
|
||||
|
||||
@@ -540,6 +543,7 @@ gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
|
||||
gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
|
||||
|
||||
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||
#define LB_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime"
|
||||
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
|
||||
#define LB_TYPE_INFO_DATA_NAME "__$type_info_data"
|
||||
#define LB_TYPE_INFO_TYPES_NAME "__$type_info_types_data"
|
||||
|
||||
@@ -386,6 +386,31 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
|
||||
return value;
|
||||
}
|
||||
|
||||
gb_internal bool lb_is_nested_possibly_constant(Type *ft, Selection const &sel, Ast *elem) {
|
||||
GB_ASSERT(!sel.indirect);
|
||||
for (i32 index : sel.index) {
|
||||
Type *bt = base_type(ft);
|
||||
switch (bt->kind) {
|
||||
case Type_Struct:
|
||||
if (bt->Struct.is_raw_union) {
|
||||
return false;
|
||||
}
|
||||
ft = bt->Struct.fields[index]->type;
|
||||
break;
|
||||
case Type_Array:
|
||||
ft = bt->Array.elem;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_type_raw_union(ft) || is_type_typeid(ft)) {
|
||||
return false;
|
||||
}
|
||||
return lb_is_elem_const(elem, ft);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_local) {
|
||||
LLVMContextRef ctx = m->ctx;
|
||||
@@ -411,7 +436,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
Ast *expr = unparen_expr(value.value_procedure);
|
||||
if (expr->kind == Ast_ProcLit) {
|
||||
res = lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr);
|
||||
|
||||
} else {
|
||||
Entity *e = entity_from_expr(expr);
|
||||
res = lb_find_procedure_value_from_entity(m, e);
|
||||
@@ -461,6 +485,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, llvm_type, array_data, indices, 2, "");
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), count, true);
|
||||
lbAddr slice = lb_add_local_generated(p, type, false);
|
||||
map_set(&m->exact_value_compound_literal_addr_map, value.value_compound, slice);
|
||||
|
||||
lb_fill_slice(p, slice, {ptr, alloc_type_pointer(elem)}, {len, t_int});
|
||||
return lb_addr_load(p, slice);
|
||||
}
|
||||
@@ -978,12 +1004,58 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
|
||||
Selection sel = lookup_field(type, name, false);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
|
||||
Entity *f = type->Struct.fields[sel.index[0]];
|
||||
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
visited[index] = true;
|
||||
if (sel.index.count == 1) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
visited[index] = true;
|
||||
} else {
|
||||
if (!visited[index]) {
|
||||
values[index] = lb_const_value(m, f->type, {}, false).value;
|
||||
visited[index] = true;
|
||||
}
|
||||
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
|
||||
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
|
||||
|
||||
if (lb_is_nested_possibly_constant(type, sel, fv->value)) {
|
||||
bool is_constant = true;
|
||||
Type *cv_type = f->type;
|
||||
for (isize j = 1; j < sel.index.count; j++) {
|
||||
i32 index = sel.index[j];
|
||||
Type *cvt = base_type(cv_type);
|
||||
|
||||
if (cvt->kind == Type_Struct) {
|
||||
if (cvt->Struct.is_raw_union) {
|
||||
// sanity check which should have been caught by `lb_is_nested_possibly_constant`
|
||||
is_constant = false;
|
||||
break;
|
||||
}
|
||||
cv_type = cvt->Struct.fields[index]->type;
|
||||
|
||||
if (is_type_struct(cv_type)) {
|
||||
auto cv_field_remapping = lb_get_struct_remapping(m, cv_type);
|
||||
idx_list[j-1] = cast(unsigned)cv_field_remapping[index];
|
||||
} else {
|
||||
idx_list[j-1] = cast(unsigned)index;
|
||||
}
|
||||
} else if (cvt->kind == Type_Array) {
|
||||
cv_type = cvt->Array.elem;
|
||||
|
||||
idx_list[j-1] = cast(unsigned)index;
|
||||
} else {
|
||||
GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type));
|
||||
}
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
|
||||
GB_ASSERT(LLVMIsConstant(elem_value));
|
||||
values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1043,6 +1115,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
|
||||
GB_ASSERT(is_local);
|
||||
lbProcedure *p = m->curr_procedure;
|
||||
lbAddr v = lb_add_local_generated(p, res.type, true);
|
||||
map_set(&m->exact_value_compound_literal_addr_map, value.value_compound, v);
|
||||
|
||||
LLVMBuildStore(p->builder, constant_value, v.addr.value);
|
||||
for (isize i = 0; i < value_count; i++) {
|
||||
LLVMValueRef val = old_values[i];
|
||||
|
||||
@@ -2210,6 +2210,15 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu
|
||||
lbValue left_ptr = lb_address_from_load_or_generate_local(p, left);
|
||||
lbValue right_ptr = lb_address_from_load_or_generate_local(p, right);
|
||||
lbValue res = {};
|
||||
if (type_size_of(type) == 0) {
|
||||
switch (op_kind) {
|
||||
case Token_CmpEq:
|
||||
return lb_const_bool(p->module, t_bool, true);
|
||||
case Token_NotEq:
|
||||
return lb_const_bool(p->module, t_bool, false);
|
||||
}
|
||||
GB_PANIC("invalid operator");
|
||||
}
|
||||
if (is_type_simple_compare(type)) {
|
||||
// TODO(bill): Test to see if this is actually faster!!!!
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
@@ -3138,7 +3147,7 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
Entity *e = entity_from_expr(expr);
|
||||
e = strip_entity_wrapping(e);
|
||||
|
||||
GB_ASSERT_MSG(e != nullptr, "%s", expr_to_string(expr));
|
||||
GB_ASSERT_MSG(e != nullptr, "%s in %.*s %p", expr_to_string(expr), LIT(p->name), expr);
|
||||
if (e->kind == Entity_Builtin) {
|
||||
Token token = ast_token(expr);
|
||||
GB_PANIC("TODO(bill): lb_build_expr Entity_Builtin '%.*s'\n"
|
||||
@@ -4035,7 +4044,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
|
||||
@@ -4084,12 +4092,25 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
ast_node(fv, FieldValue, elem);
|
||||
String name = fv->field->Ident.token.string;
|
||||
Selection sel = lookup_field(bt, name, false);
|
||||
index = sel.index[0];
|
||||
GB_ASSERT(!sel.indirect);
|
||||
|
||||
elem = fv->value;
|
||||
TypeAndValue tav = type_and_value_of_expr(elem);
|
||||
if (sel.index.count > 1) {
|
||||
if (lb_is_nested_possibly_constant(type, sel, elem)) {
|
||||
continue;
|
||||
}
|
||||
lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel);
|
||||
field_expr = lb_build_expr(p, elem);
|
||||
field_expr = lb_emit_conv(p, field_expr, sel.entity->type);
|
||||
lb_emit_store(p, dst, field_expr);
|
||||
continue;
|
||||
}
|
||||
|
||||
index = sel.index[0];
|
||||
} else {
|
||||
TypeAndValue tav = type_and_value_of_expr(elem);
|
||||
Selection sel = lookup_field_from_index(bt, st->fields[field_index]->Variable.field_index);
|
||||
GB_ASSERT(sel.index.count == 1);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
index = sel.index[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
|
||||
map_init(&m->map_info_map, 0);
|
||||
map_init(&m->map_cell_info_map, 0);
|
||||
map_init(&m->exact_value_compound_literal_addr_map, 1024);
|
||||
|
||||
}
|
||||
|
||||
@@ -1586,6 +1587,10 @@ gb_internal LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *t
|
||||
if (params_by_ptr[i]) {
|
||||
// NOTE(bill): The parameter needs to be passed "indirectly", override it
|
||||
ft->args[i].kind = lbArg_Indirect;
|
||||
ft->args[i].attribute = nullptr;
|
||||
ft->args[i].align_attribute = nullptr;
|
||||
ft->args[i].byval_alignment = 0;
|
||||
ft->args[i].is_byval = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1045,9 +1045,25 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
|
||||
} else if (is_odin_cc) {
|
||||
// NOTE(bill): Odin parameters are immutable so the original value can be passed if possible
|
||||
// i.e. `T const &` in C++
|
||||
ptr = lb_address_from_load_or_generate_local(p, x);
|
||||
if (LLVMIsConstant(x.value)) {
|
||||
// NOTE(bill): if the value is already constant, then just it as a global variable
|
||||
// and pass it by pointer
|
||||
lbAddr addr = lb_add_global_generated(p->module, original_type, x);
|
||||
lb_make_global_private_const(addr);
|
||||
ptr = addr.addr;
|
||||
} else {
|
||||
ptr = lb_address_from_load_or_generate_local(p, x);
|
||||
}
|
||||
} else {
|
||||
ptr = lb_copy_value_to_ptr(p, x, original_type, 16);
|
||||
if (LLVMIsConstant(x.value)) {
|
||||
// NOTE(bill): if the value is already constant, then just it as a global variable
|
||||
// and pass it by pointer
|
||||
lbAddr addr = lb_add_global_generated(p->module, original_type, x);
|
||||
lb_make_global_private_const(addr);
|
||||
ptr = addr.addr;
|
||||
} else {
|
||||
ptr = lb_copy_value_to_ptr(p, x, original_type, 16);
|
||||
}
|
||||
}
|
||||
array_add(&processed_args, ptr);
|
||||
}
|
||||
@@ -2288,7 +2304,15 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
break;
|
||||
case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
|
||||
case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
|
||||
case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
|
||||
case BuiltinProc_atomic_store_explicit:
|
||||
{
|
||||
auto ordering = llvm_atomic_ordering_from_odin(ce->args[2]);
|
||||
LLVMSetOrdering(instr, ordering);
|
||||
if (ordering == LLVMAtomicOrderingUnordered) {
|
||||
LLVMSetVolatile(instr, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
|
||||
@@ -2314,7 +2338,15 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
break;
|
||||
case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
|
||||
case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
|
||||
case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
|
||||
case BuiltinProc_atomic_load_explicit:
|
||||
{
|
||||
auto ordering = llvm_atomic_ordering_from_odin(ce->args[1]);
|
||||
LLVMSetOrdering(instr, ordering);
|
||||
if (ordering == LLVMAtomicOrderingUnordered) {
|
||||
LLVMSetVolatile(instr, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
|
||||
|
||||
@@ -2384,6 +2416,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildAtomicRMW(p->builder, op, dst.value, val.value, ordering, false);
|
||||
res.type = tv.type;
|
||||
if (ordering == LLVMAtomicOrderingUnordered) {
|
||||
LLVMSetVolatile(res.value, true);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2409,7 +2444,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break;
|
||||
}
|
||||
|
||||
// TODO(bill): Figure out how to make it weak
|
||||
LLVMBool single_threaded = false;
|
||||
|
||||
LLVMValueRef value = LLVMBuildAtomicCmpXchg(
|
||||
@@ -2420,6 +2454,9 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
single_threaded
|
||||
);
|
||||
LLVMSetWeak(value, weak);
|
||||
if (success_ordering == LLVMAtomicOrderingUnordered || failure_ordering == LLVMAtomicOrderingUnordered) {
|
||||
LLVMSetVolatile(value, true);
|
||||
}
|
||||
|
||||
if (is_type_tuple(tv.type)) {
|
||||
Type *fix_typed = alloc_type_tuple();
|
||||
|
||||
+104
-8
@@ -1397,6 +1397,52 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
|
||||
switch_instr = LLVMBuildSwitch(p->builder, tag.value, else_block->block, cast(unsigned)num_cases);
|
||||
}
|
||||
|
||||
bool all_by_reference = false;
|
||||
for (Ast *clause : body->stmts) {
|
||||
ast_node(cc, CaseClause, clause);
|
||||
if (cc->list.count != 1) {
|
||||
continue;
|
||||
}
|
||||
Entity *case_entity = implicit_entity_of_node(clause);
|
||||
all_by_reference |= (case_entity->flags & EntityFlag_Value) == 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(bill, 2023-02-17): In the case of a pass by value, the value does need to be copied
|
||||
// to prevent errors such as these:
|
||||
//
|
||||
// switch v in some_union {
|
||||
// case i32:
|
||||
// fmt.println(v) // 'i32'
|
||||
// some_union = f32(123)
|
||||
// fmt.println(v) // if `v` is an implicit reference, then the data is now completely corrupted
|
||||
// case f32:
|
||||
// fmt.println(v)
|
||||
// }
|
||||
//
|
||||
lbAddr backing_data = {};
|
||||
if (!all_by_reference) {
|
||||
bool variants_found = false;
|
||||
i64 max_size = 0;
|
||||
i64 max_align = 1;
|
||||
for (Ast *clause : body->stmts) {
|
||||
ast_node(cc, CaseClause, clause);
|
||||
if (cc->list.count != 1) {
|
||||
continue;
|
||||
}
|
||||
Entity *case_entity = implicit_entity_of_node(clause);
|
||||
max_size = gb_max(max_size, type_size_of(case_entity->type));
|
||||
max_align = gb_max(max_align, type_align_of(case_entity->type));
|
||||
variants_found = true;
|
||||
}
|
||||
if (variants_found) {
|
||||
Type *t = alloc_type_array(t_u8, max_size);
|
||||
backing_data = lb_add_local(p, t, nullptr, false, true);
|
||||
GB_ASSERT(lb_try_update_alignment(backing_data.addr, cast(unsigned)max_align));
|
||||
}
|
||||
}
|
||||
lbValue backing_ptr = backing_data.addr;
|
||||
|
||||
for (Ast *clause : body->stmts) {
|
||||
ast_node(cc, CaseClause, clause);
|
||||
lb_open_scope(p, cc->scope);
|
||||
@@ -1427,8 +1473,6 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
|
||||
|
||||
Entity *case_entity = implicit_entity_of_node(clause);
|
||||
|
||||
lbValue value = parent_value;
|
||||
|
||||
lb_start_block(p, body);
|
||||
|
||||
bool by_reference = (case_entity->flags & EntityFlag_Value) == 0;
|
||||
@@ -1444,13 +1488,29 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
|
||||
Type *ct = case_entity->type;
|
||||
Type *ct_ptr = alloc_type_pointer(ct);
|
||||
|
||||
value = lb_emit_conv(p, data, ct_ptr);
|
||||
if (!by_reference) {
|
||||
value = lb_emit_load(p, value);
|
||||
lbValue ptr = {};
|
||||
|
||||
if (backing_data.addr.value) { // by value
|
||||
GB_ASSERT(!by_reference);
|
||||
// make a copy of the case value
|
||||
lb_mem_copy_non_overlapping(p,
|
||||
backing_ptr, // dst
|
||||
data, // src
|
||||
lb_const_int(p->module, t_int, type_size_of(case_entity->type)));
|
||||
ptr = lb_emit_conv(p, backing_ptr, ct_ptr);
|
||||
|
||||
} else { // by reference
|
||||
GB_ASSERT(by_reference);
|
||||
ptr = lb_emit_conv(p, data, ct_ptr);
|
||||
}
|
||||
GB_ASSERT(are_types_identical(case_entity->type, type_deref(ptr.type)));
|
||||
lb_add_entity(p->module, case_entity, ptr);
|
||||
lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
|
||||
} else {
|
||||
// TODO(bill): is the correct expected behaviour?
|
||||
lb_store_type_case_implicit(p, clause, parent_value);
|
||||
}
|
||||
|
||||
lb_store_type_case_implicit(p, clause, value);
|
||||
lb_type_case_body(p, ss->label, clause, body, done);
|
||||
}
|
||||
|
||||
@@ -1523,7 +1583,8 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
|
||||
lb_add_member(p->module, mangled_name, global_val);
|
||||
}
|
||||
}
|
||||
gb_internal void lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_values, lbValue src_value) {
|
||||
gb_internal isize lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_values, lbValue src_value) {
|
||||
isize init_count = dst_values->count;
|
||||
Type *t = src_value.type;
|
||||
if (t->kind == Type_Tuple) {
|
||||
lbTupleFix *tf = map_get(&p->tuple_fix_map, src_value.value);
|
||||
@@ -1540,6 +1601,7 @@ gb_internal void lb_append_tuple_values(lbProcedure *p, Array<lbValue> *dst_valu
|
||||
} else {
|
||||
array_add(dst_values, src_value);
|
||||
}
|
||||
return dst_values->count - init_count;
|
||||
}
|
||||
|
||||
|
||||
@@ -2218,7 +2280,41 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
|
||||
}
|
||||
array_add(&lvals, lval);
|
||||
}
|
||||
lb_build_assignment(p, lvals, vd->values);
|
||||
|
||||
auto const &values = vd->values;
|
||||
if (values.count > 0) {
|
||||
auto inits = array_make<lbValue>(permanent_allocator(), 0, lvals.count);
|
||||
|
||||
isize lval_index = 0;
|
||||
for (Ast *rhs : values) {
|
||||
rhs = unparen_expr(rhs);
|
||||
lbValue init = lb_build_expr(p, rhs);
|
||||
#if 1
|
||||
// NOTE(bill, 2023-02-17): lb_const_value might produce a stack local variable for the
|
||||
// compound literal, so reusing that variable should minimize the stack wastage
|
||||
if (rhs->kind == Ast_CompoundLit) {
|
||||
lbAddr *comp_lit_addr = map_get(&p->module->exact_value_compound_literal_addr_map, rhs);
|
||||
if (comp_lit_addr) {
|
||||
Entity *e = entity_of_node(vd->names[lval_index]);
|
||||
if (e) {
|
||||
lb_add_entity(p->module, e, comp_lit_addr->addr);
|
||||
lvals[lval_index] = {}; // do nothing so that nothing will assign to it
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
lval_index += lb_append_tuple_values(p, &inits, init);
|
||||
}
|
||||
GB_ASSERT(lval_index == lvals.count);
|
||||
|
||||
GB_ASSERT(lvals.count == inits.count);
|
||||
for_array(i, inits) {
|
||||
lbAddr lval = lvals[i];
|
||||
lbValue init = inits[i];
|
||||
lb_addr_store(p, lval, init);
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(as, AssignStmt, node);
|
||||
|
||||
@@ -915,7 +915,7 @@ gb_internal lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t)
|
||||
if (field_remapping == nullptr) {
|
||||
field_remapping = map_get(&m->struct_field_remapping, cast(void *)t);
|
||||
}
|
||||
GB_ASSERT(field_remapping != nullptr);
|
||||
GB_ASSERT_MSG(field_remapping != nullptr, "%s", type_to_string(t));
|
||||
return *field_remapping;
|
||||
}
|
||||
|
||||
|
||||
+44
-1
@@ -659,6 +659,7 @@ enum BuildFlagKind {
|
||||
|
||||
BuildFlag_IgnoreWarnings,
|
||||
BuildFlag_WarningsAsErrors,
|
||||
BuildFlag_TerseErrors,
|
||||
BuildFlag_VerboseErrors,
|
||||
BuildFlag_ErrorPosStyle,
|
||||
|
||||
@@ -832,6 +833,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
|
||||
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_TerseErrors, str_lit("terse-errors"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
|
||||
add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all);
|
||||
|
||||
@@ -1462,8 +1464,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case BuildFlag_TerseErrors:
|
||||
build_context.hide_error_line = true;
|
||||
break;
|
||||
case BuildFlag_VerboseErrors:
|
||||
build_context.show_error_line = true;
|
||||
gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n");
|
||||
build_context.hide_error_line = false;
|
||||
break;
|
||||
|
||||
case BuildFlag_ErrorPosStyle:
|
||||
@@ -1835,6 +1842,17 @@ gb_internal void show_timings(Checker *c, Timings *t) {
|
||||
gb_internal void remove_temp_files(lbGenerator *gen) {
|
||||
if (build_context.keep_temp_files) return;
|
||||
|
||||
switch (build_context.build_mode) {
|
||||
case BuildMode_Executable:
|
||||
case BuildMode_DynamicLibrary:
|
||||
break;
|
||||
|
||||
case BuildMode_Object:
|
||||
case BuildMode_Assembly:
|
||||
case BuildMode_LLVM_IR:
|
||||
return;
|
||||
}
|
||||
|
||||
TIME_SECTION("remove keep temp files");
|
||||
|
||||
for (String const &path : gen->output_temp_paths) {
|
||||
@@ -2476,6 +2494,30 @@ gb_internal int strip_semicolons(Parser *parser) {
|
||||
return cast(int)failed;
|
||||
}
|
||||
|
||||
gb_internal void init_terminal(void) {
|
||||
build_context.has_ansi_terminal_colours = false;
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
HANDLE hnd = GetStdHandle(STD_ERROR_HANDLE);
|
||||
DWORD mode = 0;
|
||||
if (GetConsoleMode(hnd, &mode)) {
|
||||
enum {FLAG_ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004};
|
||||
if (SetConsoleMode(hnd, mode|FLAG_ENABLE_VIRTUAL_TERMINAL_PROCESSING)) {
|
||||
build_context.has_ansi_terminal_colours = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!build_context.has_ansi_terminal_colours) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char const *odin_terminal_ = gb_get_env("ODIN_TERMINAL", a);
|
||||
defer (gb_free(a, cast(void *)odin_terminal_));
|
||||
String odin_terminal = make_string_c(odin_terminal_);
|
||||
if (str_eq_ignore_case(odin_terminal, str_lit("ansi"))) {
|
||||
build_context.has_ansi_terminal_colours = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int arg_count, char const **arg_ptr) {
|
||||
if (arg_count < 2) {
|
||||
usage(make_string_c(arg_ptr[0]));
|
||||
@@ -2491,6 +2533,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
init_string_interner();
|
||||
init_global_error_collector();
|
||||
init_keyword_hash_table();
|
||||
init_terminal();
|
||||
|
||||
if (!check_env()) {
|
||||
return 1;
|
||||
|
||||
@@ -20,6 +20,9 @@ gb_internal Token ast_token(Ast *node) {
|
||||
case Ast_ParenExpr: return node->ParenExpr.open;
|
||||
case Ast_CallExpr: return ast_token(node->CallExpr.proc);
|
||||
case Ast_SelectorExpr:
|
||||
if (node->SelectorExpr.expr != nullptr) {
|
||||
return ast_token(node->SelectorExpr.expr);
|
||||
}
|
||||
if (node->SelectorExpr.selector != nullptr) {
|
||||
return ast_token(node->SelectorExpr.selector);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
template <typename T>
|
||||
struct PtrSet {
|
||||
static_assert(TypeIsPointer<T>::value, "PtrSet::T must be a pointer");
|
||||
static_assert(TypeIsPointer<T>::value || TypeIsPtrSizedInteger<T>::value, "PtrSet::T must be a pointer");
|
||||
static constexpr uintptr TOMBSTONE = ~(uintptr)(0ull);
|
||||
|
||||
T * keys;
|
||||
|
||||
@@ -430,7 +430,6 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
|
||||
|
||||
@@ -2313,9 +2312,6 @@ gb_internal bool is_type_comparable(Type *t) {
|
||||
return true;
|
||||
|
||||
case Type_Struct:
|
||||
if (type_size_of(t) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (t->Struct.soa_kind != StructSoa_None) {
|
||||
return false;
|
||||
}
|
||||
@@ -2331,9 +2327,6 @@ gb_internal bool is_type_comparable(Type *t) {
|
||||
return true;
|
||||
|
||||
case Type_Union:
|
||||
if (type_size_of(t) == 0) {
|
||||
return false;
|
||||
}
|
||||
for_array(i, t->Union.variants) {
|
||||
Type *v = t->Union.variants[i];
|
||||
if (!is_type_comparable(v)) {
|
||||
|
||||
Reference in New Issue
Block a user