Merge remote-tracking branch 'offical/master'

This commit is contained in:
2024-10-19 15:06:25 -04:00
548 changed files with 21207 additions and 4272 deletions
+3
View File
@@ -919,6 +919,9 @@ gb_internal void report_os_info() {
{"23F79", {23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
{"23G80", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
{"23G93", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
{"23H124", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
{"24A335", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
{"24A348", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
};
+17 -14
View File
@@ -285,6 +285,7 @@ enum VetFlags : u64 {
VetFlag_Deprecated = 1u<<7,
VetFlag_Cast = 1u<<8,
VetFlag_Tabs = 1u<<9,
VetFlag_UnusedProcedures = 1u<<10,
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
@@ -316,6 +317,8 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_Cast;
} else if (name == "tabs") {
return VetFlag_Tabs;
} else if (name == "unused-procedures") {
return VetFlag_UnusedProcedures;
}
return VetFlag_NONE;
}
@@ -383,6 +386,7 @@ struct BuildContext {
u64 vet_flags;
u32 sanitizer_flags;
StringSet vet_packages;
bool has_resource;
String link_flags;
@@ -411,6 +415,7 @@ struct BuildContext {
bool no_dynamic_literals;
bool no_output_files;
bool no_crt;
bool no_rpath;
bool no_entry_point;
bool no_thread_local;
bool use_lld;
@@ -1461,8 +1466,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->thread_count = gb_max(bc->affinity.thread_count, 1);
}
string_set_init(&bc->custom_attributes);
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
@@ -2046,19 +2049,19 @@ gb_internal bool init_build_paths(String init_filename) {
return false;
}
gbFile output_file_test;
const char* output_file_name = (const char*)output_file.text;
gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name);
// gbFile output_file_test;
// const char* output_file_name = (const char*)output_file.text;
// gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name);
if (output_test_err == 0) {
gb_file_close(&output_file_test);
gb_file_remove(output_file_name);
} else {
String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
defer (gb_free(ha, output_file.text));
gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
return false;
}
// if (output_test_err == 0) {
// gb_file_close(&output_file_test);
// gb_file_remove(output_file_name);
// } else {
// String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
// defer (gb_free(ha, output_file.text));
// gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
// return false;
// }
if (build_context.sanitizer_flags & SanitizerFlag_Address) {
switch (build_context.metrics.os) {
+31 -20
View File
@@ -1632,6 +1632,22 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->type = t_source_code_location;
operand->mode = Addressing_Value;
} else if (name == "caller_expression") {
if (ce->args.count > 1) {
error(ce->args[0], "'#caller_expression' expects either 0 or 1 arguments, got %td", ce->args.count);
}
if (ce->args.count > 0) {
Ast *arg = ce->args[0];
Operand o = {};
Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
error(ce->args[0], "'#caller_expression' expected a valid earlier parameter name");
}
arg->Ident.entity = e;
}
operand->type = t_string;
operand->mode = Addressing_Value;
} else if (name == "exists") {
if (ce->args.count != 1) {
error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count);
@@ -3096,7 +3112,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// Okay
} else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(original_type);
error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str);
error(call, "Expected an ordered numeric type to 'min', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3182,7 +3198,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
gbString type_str = type_to_string(b.type);
error(call,
"Expected a ordered numeric type to 'min', got '%s'",
"Expected an ordered numeric type to 'min', got '%s'",
type_str);
gb_string_free(type_str);
return false;
@@ -3265,7 +3281,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// Okay
} else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(original_type);
error(call, "Expected a ordered numeric type to 'max', got '%s'", type_str);
error(call, "Expected an ordered numeric type to 'max', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3356,7 +3372,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
gbString type_str = type_to_string(b.type);
error(arg,
"Expected a ordered numeric type to 'max', got '%s'",
"Expected an ordered numeric type to 'max', got '%s'",
type_str);
gb_string_free(type_str);
return false;
@@ -3486,7 +3502,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
Type *type = operand->type;
if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(operand->type);
error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str);
error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3503,7 +3519,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
if (!is_type_ordered(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) {
gbString type_str = type_to_string(y.type);
error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str);
error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3514,7 +3530,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
if (!is_type_ordered(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) {
gbString type_str = type_to_string(z.type);
error(call, "Expected a ordered numeric or string type to 'clamp', got '%s'", type_str);
error(call, "Expected an ordered numeric or string type to 'clamp', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -4967,16 +4983,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_assignment(c, &x, elem, builtin_name);
Type *t = type_deref(operand->type);
switch (id) {
case BuiltinProc_atomic_add:
case BuiltinProc_atomic_sub:
if (!is_type_numeric(t)) {
if (id != BuiltinProc_atomic_exchange) {
if (!is_type_integer_like(t)) {
gbString str = type_to_string(t);
error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
error(operand->expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
} else if (is_type_different_to_arch_endianness(t)) {
gbString str = type_to_string(t);
error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
error(operand->expr, "Expected an integer type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
}
}
@@ -5012,19 +5026,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
Type *t = type_deref(operand->type);
switch (id) {
case BuiltinProc_atomic_add_explicit:
case BuiltinProc_atomic_sub_explicit:
if (!is_type_numeric(t)) {
if (id != BuiltinProc_atomic_exchange_explicit) {
if (!is_type_integer_like(t)) {
gbString str = type_to_string(t);
error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
error(operand->expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
} else if (is_type_different_to_arch_endianness(t)) {
gbString str = type_to_string(t);
error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
error(operand->expr, "Expected an integer type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
}
break;
}
operand->type = elem;
+4
View File
@@ -232,6 +232,10 @@ gb_internal bool check_override_as_type_due_to_aliasing(CheckerContext *ctx, Ent
// until there is a proper delaying system to try declaration again if they
// have failed.
if (e->type != nullptr && is_type_typed(e->type)) {
return false;
}
e->kind = Entity_TypeName;
check_type_decl(ctx, e, init, named_type);
return true;
+42 -27
View File
@@ -285,7 +285,7 @@ gb_internal void error_operand_no_value(Operand *o) {
if (o->mode == Addressing_NoValue) {
Ast *x = unparen_expr(o->expr);
if (x->kind == Ast_CallExpr) {
if (x != nullptr && x->kind == Ast_CallExpr) {
Ast *p = unparen_expr(x->CallExpr.proc);
if (p->kind == Ast_BasicDirective) {
String tag = p->BasicDirective.name.string;
@@ -297,7 +297,7 @@ gb_internal void error_operand_no_value(Operand *o) {
}
gbString err = expr_to_string(o->expr);
if (x->kind == Ast_CallExpr) {
if (x != nullptr && x->kind == Ast_CallExpr) {
error(o->expr, "'%s' call does not return a value and cannot be used as a value", err);
} else {
error(o->expr, "'%s' used as a value", err);
@@ -3612,10 +3612,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
if (are_types_identical(src_bt, dst_bt)) {
return true;
}
if (is_type_integer(src_t) && is_type_integer(dst_t)) {
if ((is_type_integer(src_t) && is_type_integer(dst_t)) ||
is_type_integer(src_t) && is_type_bit_set(dst_t)) {
if (types_have_same_internal_endian(src_t, dst_t)) {
ExactValue src_v = exact_value_to_integer(o->value);
GB_ASSERT(src_v.kind == ExactValue_Integer);
GB_ASSERT(src_v.kind == ExactValue_Integer || src_v.kind == ExactValue_Invalid);
BigInt v = src_v.value_integer;
BigInt smax = {};
@@ -3900,6 +3901,12 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// IMPORTANT NOTE(bill): This uses right-left evaluation in type checking only no in
check_expr(c, y, be->right);
Type *rhs_type = type_deref(y->type);
if (rhs_type == nullptr) {
error(y->expr, "Cannot use '%.*s' on an expression with no value", LIT(op.string));
x->mode = Addressing_Invalid;
x->expr = node;
return;
}
if (is_type_bit_set(rhs_type)) {
Type *elem = base_type(rhs_type)->BitSet.elem;
@@ -4602,7 +4609,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
(operand->value.kind == ExactValue_Integer ||
operand->value.kind == ExactValue_Float)) {
operand->mode = Addressing_Value;
target_type = t_untyped_nil;
// target_type = t_untyped_nil;
operand->value = empty_exact_value;
update_untyped_expr_value(c, operand->expr, operand->value);
break;
@@ -6203,22 +6210,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
o.type = vt->type;
// NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
if (c->decl) {
bool found = false;
for (auto &vr : c->decl->variadic_reuses) {
if (are_types_identical(vt->type, vr.slice_type)) {
vr.max_count = gb_max(vr.max_count, variadic_operands.count);
found = true;
break;
}
}
if (!found) {
array_add(&c->decl->variadic_reuses, VariadicReuseData{vt->type, variadic_operands.count});
}
}
} else {
dummy_argument_count += 1;
o.type = t_untyped_nil;
@@ -6412,6 +6403,23 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
score += eval_param_and_score(c, o, t, err, true, var_entity, show_error);
}
if (!vari_expand && variadic_operands.count != 0) {
// NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
if (c->decl) {
bool found = false;
for (auto &vr : c->decl->variadic_reuses) {
if (are_types_identical(slice, vr.slice_type)) {
vr.max_count = gb_max(vr.max_count, variadic_operands.count);
found = true;
break;
}
}
if (!found) {
array_add(&c->decl->variadic_reuses, VariadicReuseData{slice, variadic_operands.count});
}
}
}
}
if (data) {
@@ -7807,7 +7815,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
name == "load" ||
name == "load_directory" ||
name == "load_hash" ||
name == "hash"
name == "hash" ||
name == "caller_expression"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -8087,7 +8096,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
ERROR_BLOCK();
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
error_line("\tSuggested Example: @(enable_target_feature=\"%.*s\")\n", LIT(invalid));
}
}
}
@@ -8722,6 +8734,10 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
error(node, "#caller_location may only be used as a default argument parameter");
o->type = t_source_code_location;
o->mode = Addressing_Value;
} else if (name == "caller_expression") {
error(node, "#caller_expression may only be used as a default argument parameter");
o->type = t_string;
o->mode = Addressing_Value;
} else {
if (name == "location") {
init_core_source_code_location(c->checker);
@@ -8787,11 +8803,6 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n
return kind;
}
if (x.type == nullptr || x.type == t_invalid ||
y.type == nullptr || y.type == t_invalid) {
return kind;
}
bool use_type_hint = type_hint != nullptr && (is_operand_nil(x) || is_operand_nil(y));
convert_to_typed(c, &x, use_type_hint ? type_hint : y.type);
@@ -9123,6 +9134,10 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
}
if (label != nullptr) {
if (c->in_defer) {
error(label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(name));
return Expr_Expr;
}
if (label->kind != Ast_Ident) {
error(label, "A branch statement's label name must be an identifier");
return Expr_Expr;
+14 -4
View File
@@ -1641,6 +1641,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
Ast *expr = unparen_expr(rs->expr);
Operand rhs_operand = {};
bool is_range = false;
bool is_possibly_addressable = true;
isize max_val_count = 2;
@@ -1698,7 +1700,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
}
}
bool is_ptr = is_type_pointer(type_deref(operand.type));
bool is_ptr = is_type_pointer(operand.type);
Type *t = base_type(type_deref(operand.type));
switch (t->kind) {
@@ -1750,16 +1752,19 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
break;
case Type_DynamicArray:
is_possibly_addressable = true;
array_add(&vals, t->DynamicArray.elem);
array_add(&vals, t_int);
break;
case Type_Slice:
is_possibly_addressable = true;
array_add(&vals, t->Slice.elem);
array_add(&vals, t_int);
break;
case Type_Map:
is_possibly_addressable = true;
is_map = true;
array_add(&vals, t->Map.key);
array_add(&vals, t->Map.value);
@@ -1781,6 +1786,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
case Type_Tuple:
{
is_possibly_addressable = false;
isize count = t->Tuple.variables.count;
if (count < 1) {
ERROR_BLOCK();
@@ -1810,8 +1817,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
array_add(&vals, e->type);
}
is_possibly_addressable = false;
bool do_break = false;
for (isize i = rs->vals.count-1; i >= 0; i--) {
if (rs->vals[i] != nullptr && count < i+2) {
@@ -1831,6 +1836,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
case Type_Struct:
if (t->Struct.soa_kind != StructSoa_None) {
if (t->Struct.soa_kind == StructSoa_Fixed) {
is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
} else {
is_possibly_addressable = true;
}
is_soa = true;
array_add(&vals, t->Struct.soa_elem);
array_add(&vals, t_int);
@@ -1907,7 +1917,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
if (is_possibly_addressable && i == addressable_index) {
entity->flags &= ~EntityFlag_Value;
} else {
char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index";
char const *idx_name = is_map ? "key" : (is_bit_set || i == 0) ? "element" : "index";
error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str));
}
}
+57 -6
View File
@@ -673,7 +673,7 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
#define ST_ALIGN(_name) if (st->_name != nullptr) { \
if (st->is_packed) { \
syntax_error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \
error(st->_name, "'#%s' cannot be applied with '#packed'", #_name); \
return; \
} \
i64 align = 1; \
@@ -682,12 +682,31 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
} \
}
ST_ALIGN(field_align);
ST_ALIGN(min_field_align);
ST_ALIGN(max_field_align);
ST_ALIGN(align);
if (struct_type->Struct.custom_align < struct_type->Struct.custom_field_align) {
warning(st->align, "#align(%lld) is defined to be less than #field_name(%lld)",
cast(long long)struct_type->Struct.custom_align,
cast(long long)struct_type->Struct.custom_field_align);
if (struct_type->Struct.custom_align < struct_type->Struct.custom_min_field_align) {
error(st->align, "#align(%lld) is defined to be less than #min_field_align(%lld)",
cast(long long)struct_type->Struct.custom_align,
cast(long long)struct_type->Struct.custom_min_field_align);
}
if (struct_type->Struct.custom_max_field_align != 0 &&
struct_type->Struct.custom_align > struct_type->Struct.custom_max_field_align) {
error(st->align, "#align(%lld) is defined to be greater than #max_field_align(%lld)",
cast(long long)struct_type->Struct.custom_align,
cast(long long)struct_type->Struct.custom_max_field_align);
}
if (struct_type->Struct.custom_max_field_align != 0 &&
struct_type->Struct.custom_min_field_align > struct_type->Struct.custom_max_field_align) {
error(st->align, "#min_field_align(%lld) is defined to be greater than #max_field_align(%lld)",
cast(long long)struct_type->Struct.custom_min_field_align,
cast(long long)struct_type->Struct.custom_max_field_align);
i64 a = gb_min(struct_type->Struct.custom_min_field_align, struct_type->Struct.custom_max_field_align);
i64 b = gb_max(struct_type->Struct.custom_min_field_align, struct_type->Struct.custom_max_field_align);
// NOTE(bill): sort them to keep code consistent
struct_type->Struct.custom_min_field_align = a;
struct_type->Struct.custom_max_field_align = b;
}
#undef ST_ALIGN
@@ -1605,6 +1624,25 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
return false;
}
gb_internal bool is_caller_expression(Ast *expr) {
if (expr->kind == Ast_BasicDirective && expr->BasicDirective.name.string == "caller_expression") {
return true;
}
Ast *call = unparen_expr(expr);
if (call->kind != Ast_CallExpr) {
return false;
}
ast_node(ce, CallExpr, call);
if (ce->proc->kind != Ast_BasicDirective) {
return false;
}
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
return name == "caller_expression";
}
gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) {
ParameterValue param_value = {};
@@ -1626,7 +1664,19 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
} else if (is_caller_expression(expr)) {
if (expr->kind != Ast_BasicDirective) {
check_builtin_procedure_directive(ctx, &o, expr, t_string);
}
param_value.kind = ParameterValue_Expression;
o.type = t_string;
o.mode = Addressing_Value;
o.expr = expr;
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
} else {
if (in_type) {
check_expr_with_type_hint(ctx, &o, expr, in_type);
@@ -1858,6 +1908,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
case ParameterValue_Nil:
break;
case ParameterValue_Location:
case ParameterValue_Expression:
case ParameterValue_Value:
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
+63 -27
View File
@@ -533,18 +533,13 @@ gb_internal u64 check_vet_flags(CheckerContext *c) {
c->curr_proc_decl->proc_lit) {
file = c->curr_proc_decl->proc_lit->file();
}
if (file && file->vet_flags_set) {
return file->vet_flags;
}
return build_context.vet_flags;
return ast_file_vet_flags(file);
}
gb_internal u64 check_vet_flags(Ast *node) {
AstFile *file = node->file();
if (file && file->vet_flags_set) {
return file->vet_flags;
}
return build_context.vet_flags;
return ast_file_vet_flags(file);
}
enum VettedEntityKind {
@@ -681,20 +676,48 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
return false;
}
gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) {
u64 original_vet_flags = vet_flags;
Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
defer (array_free(&vetted_entities));
rw_mutex_shared_lock(&scope->mutex);
for (auto const &entry : scope->elements) {
Entity *e = entry.value;
if (e == nullptr) continue;
vet_flags = original_vet_flags;
if (per_entity) {
vet_flags = ast_file_vet_flags(e->file);
}
bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0;
if (vet_unused_procedures && e->pkg && e->pkg->kind == Package_Runtime) {
vet_unused_procedures = false;
}
VettedEntity ve_unused = {};
VettedEntity ve_shadowed = {};
bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
bool is_unused = false;
if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
is_unused = true;
} else if (vet_unused_procedures &&
e->kind == Entity_Procedure) {
if (e->flags&EntityFlag_Used) {
is_unused = false;
} else if (e->flags & EntityFlag_Require) {
is_unused = false;
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
is_unused = false;
} else {
is_unused = true;
ve_unused.kind = VettedEntity_Unused;
ve_unused.entity = e;
}
}
bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
if (is_unused && is_shadowed) {
VettedEntity ve_both = ve_shadowed;
@@ -717,13 +740,18 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
}
rw_mutex_shared_unlock(&scope->mutex);
gb_sort(vetted_entities.data, vetted_entities.count, gb_size_of(VettedEntity), vetted_entity_variable_pos_cmp);
array_sort(vetted_entities, vetted_entity_variable_pos_cmp);
for (auto const &ve : vetted_entities) {
Entity *e = ve.entity;
Entity *other = ve.other;
String name = e->token.string;
vet_flags = original_vet_flags;
if (per_entity) {
vet_flags = ast_file_vet_flags(e->file);
}
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 (vet_flags) {
@@ -732,6 +760,9 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
if (e->kind == Entity_Procedure && (vet_flags & VetFlag_UnusedProcedures) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
@@ -749,7 +780,11 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
}
}
array_free(&vetted_entities);
}
gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
check_scope_usage_internal(c, scope, vet_flags, false);
for (Scope *child = scope->head_child; child != nullptr; child = child->next) {
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
@@ -6174,7 +6209,7 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
if ((src_params == nullptr && dst_params != nullptr) ||
(src_params != nullptr && dst_params == nullptr)) {
error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string));
continue;
}
@@ -6187,8 +6222,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(src_params);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)",
LIT(src->token.string), LIT(dst->token.string),
s, d
LIT(dst->token.string), LIT(src->token.string),
d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -6204,7 +6239,7 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
if ((src_results == nullptr && dst_params != nullptr) ||
(src_results != nullptr && dst_params == nullptr)) {
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string));
continue;
}
@@ -6217,8 +6252,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(src_results);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
LIT(src->token.string), LIT(dst->token.string),
s, d
LIT(dst->token.string), LIT(src->token.string),
d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -6270,8 +6305,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(tsrc);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
LIT(src->token.string), LIT(dst->token.string),
s, d
LIT(dst->token.string), LIT(src->token.string),
d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -6497,12 +6532,13 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check scope usage");
for (auto const &entry : c->info.files) {
AstFile *f = entry.value;
u64 vet_flags = build_context.vet_flags;
if (f->vet_flags_set) {
vet_flags = f->vet_flags;
}
u64 vet_flags = ast_file_vet_flags(f);
check_scope_usage(c, f->scope, vet_flags);
}
for (auto const &entry : c->info.packages) {
AstPackage *pkg = entry.value;
check_scope_usage_internal(c, pkg->scope, 0, true);
}
TIME_SECTION("add basic type information");
// Add "Basic" type information
+1
View File
@@ -104,6 +104,7 @@ enum ParameterValueKind {
ParameterValue_Constant,
ParameterValue_Nil,
ParameterValue_Location,
ParameterValue_Expression,
ParameterValue_Value,
};
+10 -2
View File
@@ -3195,11 +3195,11 @@ void gb_affinity_init(gbAffinity *a) {
a->core_count = 1;
a->threads_per_core = 1;
if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) {
if (sysctlbyname("kern.smp.cpus", &count, &count_size, NULL, 0) == 0) {
if (count > 0) {
a->thread_count = count;
// Get # of physical cores
if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) {
if (sysctlbyname("kern.smp.cores", &count, &count_size, NULL, 0) == 0) {
if (count > 0) {
a->core_count = count;
a->threads_per_core = a->thread_count / count;
@@ -3210,6 +3210,14 @@ void gb_affinity_init(gbAffinity *a) {
}
}
}
} else if (sysctlbyname("hw.ncpu", &count, &count_size, NULL, 0) == 0) {
// SMP disabled or unavailable.
if (count > 0) {
a->is_accurate = true;
a->thread_count = count;
a->core_count = count;
a->threads_per_core = 1;
}
}
}
+11 -7
View File
@@ -548,14 +548,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
// static libs and object files, absolute full path relative to the file in which the lib was imported from
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
} else if (string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
// dynamic lib, relative path to executable
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
// at runtime to the executable
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
@@ -643,6 +637,16 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
}
if (!build_context.no_rpath) {
// Set the rpath to the $ORIGIN/@loader_path (the path of the executable),
// so that dynamic libraries are looked for at that path.
if (build_context.metrics.os == TargetOs_darwin) {
link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
} else {
link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
}
}
if (!build_context.no_crt) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
if (build_context.metrics.os == TargetOs_darwin) {
+20 -10
View File
@@ -531,6 +531,7 @@ namespace lbAbiAmd64SysV {
RegClass_SSEInt16,
RegClass_SSEInt32,
RegClass_SSEInt64,
RegClass_SSEInt128,
RegClass_SSEUp,
RegClass_X87,
RegClass_X87Up,
@@ -572,6 +573,15 @@ namespace lbAbiAmd64SysV {
gb_internal Array<RegClass> classify(LLVMTypeRef t);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes, LLVMTypeRef type);
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type) {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
}
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention);
}
gb_internal LB_ABI_INFO(abi_info) {
LLVMContextRef c = m->ctx;
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
@@ -582,12 +592,7 @@ namespace lbAbiAmd64SysV {
for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
}
if (return_is_defined) {
ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect, calling_convention);
} else {
ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
}
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
return ft;
}
@@ -616,6 +621,10 @@ namespace lbAbiAmd64SysV {
}
switch (kind) {
case LLVMIntegerTypeKind:
if (LLVM_VERSION_MAJOR >= 18 && sz >= 16) {
return true;
}
return false;
case LLVMHalfTypeKind:
case LLVMFloatTypeKind:
case LLVMDoubleTypeKind:
@@ -1257,11 +1266,12 @@ namespace lbAbiWasm {
}
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
if (!is_return && type == LLVMIntTypeInContext(c, 128)) {
LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
if (type == LLVMIntTypeInContext(c, 128)) {
// LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
LLVMTypeRef cast_type = nullptr;
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
}
if (!is_return && lb_sizeof(type) > 8) {
return lb_arg_type_indirect(type, nullptr);
}
@@ -1282,7 +1292,7 @@ namespace lbAbiWasm {
case LLVMPointerTypeKind:
return true;
case LLVMIntegerTypeKind:
return lb_sizeof(type) <= 8;
return lb_sizeof(type) <= 16;
}
return false;
}
+1 -1
View File
@@ -528,7 +528,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos);
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast *call_expression);
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
+2 -2
View File
@@ -154,7 +154,7 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
GB_ASSERT(value_count_ == bt->Struct.fields.count);
auto field_remapping = lb_get_struct_remapping(m, t);
unsigned values_with_padding_count = LLVMCountStructElementTypes(struct_type);
unsigned values_with_padding_count = elem_count;
LLVMValueRef *values_with_padding = gb_alloc_array(permanent_allocator(), LLVMValueRef, values_with_padding_count);
for (unsigned i = 0; i < value_count; i++) {
@@ -722,7 +722,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
case ExactValue_Integer:
if (is_type_pointer(type) || is_type_multi_pointer(type)) {
if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(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);
+44 -36
View File
@@ -552,6 +552,48 @@ gb_internal LLVMMetadataRef lb_debug_bitset(lbModule *m, Type *type, String name
return final_decl;
}
gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_BitField);
lb_debug_file_line(m, bt->BitField.node, &file, &line);
u64 size_in_bits = 8*type_size_of(bt);
u32 align_in_bits = 8*cast(u32)type_align_of(bt);
unsigned element_count = cast(unsigned)bt->BitField.fields.count;
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
u64 offset_in_bits = 0;
for (unsigned i = 0; i < element_count; i++) {
Entity *f = bt->BitField.fields[i];
u8 bit_size = bt->BitField.bit_sizes[i];
GB_ASSERT(f->kind == Entity_Variable);
String name = f->token.string;
elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line,
bit_size, offset_in_bits, 0,
LLVMDIFlagZero, lb_debug_type(m, f->type)
);
offset_in_bits += bit_size;
}
LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
m->debug_builder, scope,
cast(char const *)name.text, cast(size_t)name.len,
file, line,
size_in_bits, align_in_bits,
LLVMDIFlagZero,
nullptr,
elements, element_count,
0,
nullptr,
"", 0
);
lb_set_llvm_metadata(m, type, final_decl);
return final_decl;
}
gb_internal LLVMMetadataRef lb_debug_enum(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_Enum);
@@ -816,6 +858,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_BitField: return lb_debug_bitfield( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Tuple:
if (type->Tuple.variables.count == 1) {
@@ -901,42 +944,6 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
lb_debug_type(m, type->Matrix.elem),
subscripts, gb_count_of(subscripts));
}
case Type_BitField: {
LLVMMetadataRef parent_scope = nullptr;
LLVMMetadataRef scope = nullptr;
LLVMMetadataRef file = nullptr;
unsigned line = 0;
u64 size_in_bits = 8*cast(u64)type_size_of(type);
u32 align_in_bits = 8*cast(u32)type_align_of(type);
LLVMDIFlags flags = LLVMDIFlagZero;
unsigned element_count = cast(unsigned)type->BitField.fields.count;
LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
u64 offset_in_bits = 0;
for (unsigned i = 0; i < element_count; i++) {
Entity *f = type->BitField.fields[i];
u8 bit_size = type->BitField.bit_sizes[i];
GB_ASSERT(f->kind == Entity_Variable);
String name = f->token.string;
unsigned field_line = 0;
LLVMDIFlags field_flags = LLVMDIFlagZero;
elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
bit_size, offset_in_bits, offset_in_bits,
field_flags, lb_debug_type(m, f->type)
);
offset_in_bits += bit_size;
}
return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line,
size_in_bits, align_in_bits, flags,
nullptr, elements, element_count, 0, nullptr,
"", 0
);
}
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -1022,6 +1029,7 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
case Type_Union: return lb_debug_union(m, type, name, scope, file, line);
case Type_BitSet: return lb_debug_bitset(m, type, name, scope, file, line);
case Type_Enum: return lb_debug_enum(m, type, name, scope, file, line);
case Type_BitField: return lb_debug_bitfield(m, type, name, scope, file, line);
}
}
+24 -8
View File
@@ -1225,10 +1225,10 @@ gb_internal lbValue lb_emit_arith(lbProcedure *p, TokenKind op, lbValue lhs, lbV
lbValue d3 = lb_emit_struct_ep(p, res.addr, 3);
if (immediate_type != ft) {
d0 = lb_emit_conv(p, d0, ft);
d1 = lb_emit_conv(p, d1, ft);
d2 = lb_emit_conv(p, d2, ft);
d3 = lb_emit_conv(p, d3, ft);
z0 = lb_emit_conv(p, z0, ft);
z1 = lb_emit_conv(p, z1, ft);
z2 = lb_emit_conv(p, z2, ft);
z3 = lb_emit_conv(p, z3, ft);
}
lb_emit_store(p, d0, z0);
@@ -2555,17 +2555,27 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
if (are_types_identical(a, b)) {
// NOTE(bill): No need for a conversion
} else if (lb_is_const(left) || lb_is_const_nil(left)) {
} else if ((lb_is_const(left) && !is_type_array(left.type)) || lb_is_const_nil(left)) {
// NOTE(karl): !is_type_array(left.type) is there to avoid lb_emit_conv
// trying to convert a constant array into a non-array. In that case we
// want the `else` branch to happen, so it can try to convert the
// non-array into an array instead.
if (lb_is_const_nil(left)) {
if (internal_check_is_assignable_to(right.type, left.type)) {
right = lb_emit_conv(p, right, left.type);
}
return lb_emit_comp_against_nil(p, op_kind, right);
}
left = lb_emit_conv(p, left, right.type);
} else if (lb_is_const(right) || lb_is_const_nil(right)) {
} else if ((lb_is_const(right) && !is_type_array(right.type)) || lb_is_const_nil(right)) {
if (lb_is_const_nil(right)) {
if (internal_check_is_assignable_to(left.type, right.type)) {
left = lb_emit_conv(p, left, right.type);
}
return lb_emit_comp_against_nil(p, op_kind, left);
}
right = lb_emit_conv(p, right, left.type);
} else {
Type *lt = left.type;
Type *rt = right.type;
@@ -3451,8 +3461,14 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
switch (expr->kind) {
case_ast_node(bl, BasicLit, expr);
if (type != nullptr && type->Named.name == "Error") {
Entity *e = type->Named.type_name;
if (e->pkg && e->pkg->name == "os") {
return lb_const_nil(p->module, type);
}
}
TokenPos pos = bl->token.pos;
GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]));
GB_PANIC("Non-constant basic literal %s - %.*s (%s)", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]), type_to_string(type));
case_end;
case_ast_node(bd, BasicDirective, expr);
+57 -5
View File
@@ -699,7 +699,9 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
}
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Location);
GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Expression);
lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, nullptr, nullptr);
lb_addr_store(p, res, c);
}
@@ -3420,7 +3422,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos) {
gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast* call_expression) {
switch (param_value.kind) {
case ParameterValue_Constant:
if (is_type_constant_type(parameter_type)) {
@@ -3446,8 +3448,60 @@ gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type,
if (p->entity != nullptr) {
proc_name = p->entity->token.string;
}
ast_node(ce, CallExpr, call_expression);
TokenPos pos = ast_token(ce->proc).pos;
return lb_emit_source_code_location_as_global(p, proc_name, pos);
}
case ParameterValue_Expression:
{
Ast *orig = param_value.original_ast_expr;
if (orig->kind == Ast_BasicDirective) {
gbString expr = expr_to_string(call_expression, temporary_allocator());
return lb_const_string(p->module, make_string_c(expr));
}
isize param_idx = -1;
String param_str = {0};
{
Ast *call = unparen_expr(orig);
GB_ASSERT(call->kind == Ast_CallExpr);
ast_node(ce, CallExpr, call);
GB_ASSERT(ce->proc->kind == Ast_BasicDirective);
GB_ASSERT(ce->args.count == 1);
Ast *target = ce->args[0];
GB_ASSERT(target->kind == Ast_Ident);
String target_str = target->Ident.token.string;
param_idx = lookup_procedure_parameter(procedure_type, target_str);
param_str = target_str;
}
GB_ASSERT(param_idx >= 0);
Ast *target_expr = nullptr;
ast_node(ce, CallExpr, call_expression);
if (ce->split_args->positional.count > param_idx) {
target_expr = ce->split_args->positional[param_idx];
}
for_array(i, ce->split_args->named) {
Ast *arg = ce->split_args->named[i];
ast_node(fv, FieldValue, arg);
GB_ASSERT(fv->field->kind == Ast_Ident);
String name = fv->field->Ident.token.string;
if (name == param_str) {
target_expr = fv->value;
break;
}
}
gbString expr = expr_to_string(target_expr, temporary_allocator());
return lb_const_string(p->module, make_string_c(expr));
}
case ParameterValue_Value:
return lb_build_expr(p, param_value.ast_value);
}
@@ -3739,8 +3793,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
TokenPos pos = ast_token(ce->proc).pos;
if (pt->params != nullptr) {
isize min_count = pt->params->Tuple.variables.count;
@@ -3764,7 +3816,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
args[arg_index] = lb_const_nil(p->module, e->type);
break;
case Entity_Variable:
args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos);
args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pt, expr);
break;
case Entity_Constant:
+1 -1
View File
@@ -826,7 +826,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
if (t->Struct.soa_kind != StructSoa_None) {
Type *kind_type = get_struct_field_type(tag_type, 10);
Type *kind_type = get_struct_field_type(tag_type, 7);
lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
LLVMValueRef soa_type = get_type_info_ptr(m, t->Struct.soa_elem);
+440 -338
View File
@@ -325,6 +325,7 @@ enum BuildFlagKind {
BuildFlag_NoTypeAssert,
BuildFlag_NoDynamicLiterals,
BuildFlag_NoCRT,
BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
BuildFlag_UseLLD,
BuildFlag_UseSeparateModules,
@@ -339,12 +340,14 @@ enum BuildFlagKind {
BuildFlag_VetUnused,
BuildFlag_VetUnusedImports,
BuildFlag_VetUnusedVariables,
BuildFlag_VetUnusedProcedures,
BuildFlag_VetUsingStmt,
BuildFlag_VetUsingParam,
BuildFlag_VetStyle,
BuildFlag_VetSemicolon,
BuildFlag_VetCast,
BuildFlag_VetTabs,
BuildFlag_VetPackages,
BuildFlag_CustomAttribute,
BuildFlag_IgnoreUnknownAttributes,
@@ -508,7 +511,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check);
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test);
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test | Command_doc);
add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
@@ -533,6 +536,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
@@ -545,6 +549,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedProcedures, str_lit("vet-unused-procedures"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedImports, str_lit("vet-unused-imports"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check);
@@ -553,6 +558,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true);
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
@@ -1183,6 +1189,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoCRT:
build_context.no_crt = true;
break;
case BuildFlag_NoRPath:
build_context.no_rpath = true;
break;
case BuildFlag_NoEntryPoint:
build_context.no_entry_point = true;
break;
@@ -1215,6 +1224,36 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break;
case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break;
case BuildFlag_VetUnusedProcedures:
build_context.vet_flags |= VetFlag_UnusedProcedures;
if (!set_flags[BuildFlag_VetPackages]) {
gb_printf_err("-%.*s must be used with -vet-packages\n", LIT(name));
bad_flags = true;
}
break;
case BuildFlag_VetPackages:
{
GB_ASSERT(value.kind == ExactValue_String);
String val = value.value_string;
String_Iterator it = {val, 0};
for (;;) {
String pkg = string_split_iterator(&it, ',');
if (pkg.len == 0) {
break;
}
pkg = string_trim_whitespace(pkg);
if (!string_is_valid_identifier(pkg)) {
gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(pkg));
bad_flags = true;
continue;
}
string_set_add(&build_context.vet_packages, pkg);
}
}
break;
case BuildFlag_CustomAttribute:
{
@@ -1229,7 +1268,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
attr = string_trim_whitespace(attr);
if (!string_is_valid_identifier(attr)) {
gb_printf_err("-custom-attribute '%.*s' must be a valid identifier\n", LIT(attr));
gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(attr));
bad_flags = true;
continue;
}
@@ -1369,7 +1408,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.terse_errors = true;
break;
case BuildFlag_VerboseErrors:
gb_printf_err("-verbose-errors is not the default, -terse-errors can now disable it\n");
gb_printf_err("-verbose-errors is now the default, -terse-errors can disable it\n");
build_context.hide_error_line = false;
build_context.terse_errors = false;
break;
@@ -2140,131 +2179,15 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "Flags");
print_usage_line(0, "");
if (check) {
print_usage_line(1, "-file");
print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents.");
print_usage_line(0, "");
}
if (doc) {
print_usage_line(1, "-short");
print_usage_line(2, "Shows shortened documentation for the packages.");
print_usage_line(0, "");
print_usage_line(1, "-all-packages");
print_usage_line(2, "Generates documentation for all packages used in the current project.");
print_usage_line(0, "");
print_usage_line(1, "-doc-format");
print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling).");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-out:<filepath>");
print_usage_line(2, "Sets the file name of the outputted executable.");
print_usage_line(2, "Example: -out:foo.exe");
print_usage_line(0, "");
print_usage_line(1, "-o:<string>");
print_usage_line(2, "Sets the optimization mode for compilation.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-o:none");
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
if (LB_USE_NEW_PASS_SYSTEM) {
print_usage_line(3, "-o:aggressive");
}
print_usage_line(2, "The default is -o:minimal.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-show-timings");
print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds.");
print_usage_line(0, "");
print_usage_line(1, "-show-more-timings");
print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds.");
print_usage_line(0, "");
print_usage_line(1, "-show-system-calls");
print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler.");
print_usage_line(0, "");
print_usage_line(1, "-export-timings:<format>");
print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
print_usage_line(0, "");
print_usage_line(1, "-export-timings-file:<filename>");
print_usage_line(2, "Specifies the filename for `-export-timings`.");
print_usage_line(2, "Example: -export-timings-file:timings.json");
print_usage_line(0, "");
print_usage_line(1, "-export-dependencies:<format>");
print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
print_usage_line(0, "");
print_usage_line(1, "-export-dependencies-file:<filename>");
print_usage_line(2, "Specifies the filename for `-export-dependencies`.");
print_usage_line(2, "Example: -export-dependencies-file:dependencies.d");
print_usage_line(0, "");
print_usage_line(1, "-thread-count:<integer>");
print_usage_line(2, "Overrides the number of threads the compiler will use to compile with.");
print_usage_line(2, "Example: -thread-count:2");
print_usage_line(0, "");
}
if (check_only) {
print_usage_line(1, "-show-unused");
print_usage_line(2, "Shows unused package declarations within the current project.");
print_usage_line(0, "");
print_usage_line(1, "-show-unused-with-location");
print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-keep-temp-files");
print_usage_line(2, "Keeps the temporary files generated during compilation.");
print_usage_line(0, "");
} else if (strip_semicolon) {
print_usage_line(1, "-keep-temp-files");
print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-collection:<name>=<filepath>");
print_usage_line(2, "Defines a library collection used for imports.");
print_usage_line(2, "Example: -collection:shared=dir/to/shared");
print_usage_line(2, "Usage in Code:");
print_usage_line(3, "import \"shared:foo\"");
print_usage_line(0, "");
print_usage_line(1, "-define:<name>=<value>");
print_usage_line(2, "Defines a scalar boolean, integer or string as global constant.");
print_usage_line(2, "Example: -define:SPAM=123");
print_usage_line(2, "Usage in code:");
print_usage_line(3, "#config(SPAM, default_value)");
print_usage_line(0, "");
print_usage_line(1, "-show-defineables");
print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project.");
print_usage_line(0, "");
print_usage_line(1, "-export-defineables:<filename>");
print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path.");
print_usage_line(2, "Example: -export-defineables:defineables.csv");
if (test_only) {
print_usage_line(1, "-all-packages");
print_usage_line(2, "Tests all packages imported into the given initial package.");
print_usage_line(0, "");
}
@@ -2289,8 +2212,20 @@ gb_internal void print_show_help(String const arg0, String const &command) {
}
if (check) {
print_usage_line(1, "-target:<string>");
print_usage_line(2, "Sets the target for the executable to be built in.");
print_usage_line(1, "-collection:<name>=<filepath>");
print_usage_line(2, "Defines a library collection used for imports.");
print_usage_line(2, "Example: -collection:shared=dir/to/shared");
print_usage_line(2, "Usage in Code:");
print_usage_line(3, "import \"shared:foo\"");
print_usage_line(0, "");
print_usage_line(1, "-custom-attribute:<string>");
print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-custom-attribute:my_tag");
print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
print_usage_line(0, "");
}
@@ -2298,31 +2233,378 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-debug");
print_usage_line(2, "Enables debug information, and defines the global constant ODIN_DEBUG to be 'true'.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-default-to-nil-allocator");
print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing.");
print_usage_line(0, "");
print_usage_line(1, "-define:<name>=<value>");
print_usage_line(2, "Defines a scalar boolean, integer or string as global constant.");
print_usage_line(2, "Example: -define:SPAM=123");
print_usage_line(2, "Usage in code:");
print_usage_line(3, "#config(SPAM, default_value)");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-disable-assert");
print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'.");
print_usage_line(0, "");
print_usage_line(1, "-disable-red-zone");
print_usage_line(2, "Disables red zone on a supported freestanding target.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-disallow-do");
print_usage_line(2, "Disallows the 'do' keyword in the project.");
print_usage_line(0, "");
}
if (doc) {
print_usage_line(1, "-doc-format");
print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling).");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-dynamic-map-calls");
print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-error-pos-style:<string>");
print_usage_line(2, "Available options:");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
print_usage_line(0, "");
print_usage_line(1, "-export-defineables:<filename>");
print_usage_line(2, "Exports an overview of all the #config/#defined usages in CSV format to the given file path.");
print_usage_line(2, "Example: -export-defineables:defineables.csv");
print_usage_line(0, "");
print_usage_line(1, "-export-dependencies:<format>");
print_usage_line(2, "Exports dependencies to one of a few formats. Requires `-export-dependencies-file`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-dependencies:make Exports in Makefile format");
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
print_usage_line(0, "");
print_usage_line(1, "-export-dependencies-file:<filename>");
print_usage_line(2, "Specifies the filename for `-export-dependencies`.");
print_usage_line(2, "Example: -export-dependencies-file:dependencies.d");
print_usage_line(0, "");
print_usage_line(1, "-export-timings:<format>");
print_usage_line(2, "Exports timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-export-timings:json Exports compile time stats to JSON.");
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
print_usage_line(0, "");
print_usage_line(1, "-export-timings-file:<filename>");
print_usage_line(2, "Specifies the filename for `-export-timings`.");
print_usage_line(2, "Example: -export-timings-file:timings.json");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-extra-assembler-flags:<string>");
print_usage_line(2, "Adds extra assembler specific flags in a string.");
print_usage_line(0, "");
print_usage_line(1, "-extra-linker-flags:<string>");
print_usage_line(2, "Adds extra linker specific flags in a string.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-file");
print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
print_usage_line(2, "This means that `<dir>/a.odin` won't have access to `<dir>/b.odin`'s contents.");
print_usage_line(0, "");
print_usage_line(1, "-foreign-error-procedures");
print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit.");
print_usage_line(0, "");
print_usage_line(1, "-ignore-unknown-attributes");
print_usage_line(2, "Ignores unknown attributes.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(0, "");
}
if (run_or_build) {
#if defined(GB_SYSTEM_WINDOWS)
print_usage_line(1, "-ignore-vs-search");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Ignores the Visual Studio search for library paths.");
print_usage_line(0, "");
#endif
}
if (check) {
print_usage_line(1, "-ignore-warnings");
print_usage_line(2, "Ignores warning messages.");
print_usage_line(0, "");
print_usage_line(1, "-json-errors");
print_usage_line(2, "Prints the error messages as json to stderr.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-keep-temp-files");
print_usage_line(2, "Keeps the temporary files generated during compilation.");
print_usage_line(0, "");
} else if (strip_semicolon) {
print_usage_line(1, "-keep-temp-files");
print_usage_line(2, "Keeps the temporary files generated during stripping the unneeded semicolons from files.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-lld");
print_usage_line(2, "Uses the LLD linker rather than the default.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-max-error-count:<integer>");
print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates.");
print_usage_line(2, "Must be an integer >0.");
print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT);
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-microarch:<string>");
print_usage_line(2, "Specifies the specific micro-architecture for the build in a string.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(3, "-microarch:\"?\" for a list");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-min-link-libs");
print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications.");
print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-minimum-os-version:<string>");
print_usage_line(2, "Sets the minimum OS version targeted by the application.");
print_usage_line(2, "Default: -minimum-os-version:11.0.0");
print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning.");
print_usage_line(0, "");
print_usage_line(1, "-no-bounds-check");
print_usage_line(2, "Disables bounds checking program wide.");
print_usage_line(0, "");
print_usage_line(1, "-no-type-assert");
print_usage_line(2, "Disables type assertion checking program wide.");
print_usage_line(0, "");
print_usage_line(1, "-no-crt");
print_usage_line(2, "Disables automatic linking with the C Run Time.");
print_usage_line(0, "");
}
if (check && command != "test") {
print_usage_line(1, "-no-entry-point");
print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure).");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-no-rpath");
print_usage_line(2, "Disables automatic addition of an rpath linked to the executable directory.");
print_usage_line(0, "");
print_usage_line(1, "-no-thread-local");
print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded.");
print_usage_line(0, "");
print_usage_line(1, "-lld");
print_usage_line(2, "Uses the LLD linker rather than the default.");
print_usage_line(1, "-no-threaded-checker");
print_usage_line(2, "Disables multithreading in the semantic checker stage.");
print_usage_line(0, "");
print_usage_line(1, "-no-type-assert");
print_usage_line(2, "Disables type assertion checking program wide.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-o:<string>");
print_usage_line(2, "Sets the optimization mode for compilation.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-o:none");
print_usage_line(3, "-o:minimal");
print_usage_line(3, "-o:size");
print_usage_line(3, "-o:speed");
if (LB_USE_NEW_PASS_SYSTEM) {
print_usage_line(3, "-o:aggressive (use this with caution)");
}
print_usage_line(2, "The default is -o:minimal.");
print_usage_line(0, "");
print_usage_line(1, "-obfuscate-source-code-locations");
print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value.");
print_usage_line(0, "");
print_usage_line(1, "-out:<filepath>");
print_usage_line(2, "Sets the file name of the outputted executable.");
print_usage_line(2, "Example: -out:foo.exe");
print_usage_line(0, "");
}
if (doc) {
print_usage_line(1, "-out:<filepath>");
print_usage_line(2, "Sets the base name of the resultig .odin-doc file.");
print_usage_line(2, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'.");
print_usage_line(2, "Example: -out:foo");
print_usage_line(0, "");
}
if (run_or_build) {
#if defined(GB_SYSTEM_WINDOWS)
print_usage_line(1, "-pdb-name:<filepath>");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the generated PDB name when -debug is enabled.");
print_usage_line(2, "Example: -pdb-name:different.pdb");
print_usage_line(0, "");
#endif
}
if (build) {
print_usage_line(1, "-print-linker-flags");
print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-reloc-mode:<string>");
print_usage_line(2, "Specifies the reloc mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-reloc-mode:default");
print_usage_line(3, "-reloc-mode:static");
print_usage_line(3, "-reloc-mode:pic");
print_usage_line(3, "-reloc-mode:dynamic-no-pic");
print_usage_line(0, "");
#if defined(GB_SYSTEM_WINDOWS)
print_usage_line(1, "-resource:<filepath>");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the resource file for the executable.");
print_usage_line(2, "Example: -resource:path/to/file.rc");
print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one.");
print_usage_line(0, "");
#endif
print_usage_line(1, "-sanitize:<string>");
print_usage_line(2, "Enables sanitization analysis.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-sanitize:address");
print_usage_line(3, "-sanitize:memory");
print_usage_line(3, "-sanitize:thread");
print_usage_line(2, "NOTE: This flag can be used multiple times.");
print_usage_line(0, "");
}
if (doc) {
print_usage_line(1, "-short");
print_usage_line(2, "Shows shortened documentation for the packages.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-show-defineables");
print_usage_line(2, "Shows an overview of all the #config/#defined usages in the project.");
print_usage_line(0, "");
print_usage_line(1, "-show-system-calls");
print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler.");
print_usage_line(0, "");
print_usage_line(1, "-show-timings");
print_usage_line(2, "Shows basic overview of the timings of different stages within the compiler in milliseconds.");
print_usage_line(0, "");
print_usage_line(1, "-show-more-timings");
print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds.");
print_usage_line(0, "");
}
if (check_only) {
print_usage_line(1, "-show-unused");
print_usage_line(2, "Shows unused package declarations within the current project.");
print_usage_line(0, "");
print_usage_line(1, "-show-unused-with-location");
print_usage_line(2, "Shows unused package declarations within the current project with the declarations source location.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-strict-style");
print_usage_line(2, "This enforces parts of same style as the Odin compiler, prefer '-vet-style -vet-semicolon' if you do not want to match it exactly.");
print_usage_line(2, "");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons.");
print_usage_line(2, "Errs on missing trailing commas followed by a newline.");
print_usage_line(2, "Errs on deprecated syntax.");
print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS).");
print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-strict-target-features");
print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\").");
print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features.");
print_usage_line(0, "");
#if defined(GB_SYSTEM_WINDOWS)
print_usage_line(1, "-subsystem:<option>");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the subsystem for the application.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-subsystem:console");
print_usage_line(3, "-subsystem:windows");
print_usage_line(0, "");
#endif
print_usage_line(1, "-target-features:<string>");
print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-target-features:atomics");
print_usage_line(3, "-target-features:\"sse2,aes\"");
print_usage_line(3, "-target-features:\"?\" for a list");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-target:<string>");
print_usage_line(2, "Sets the target for the executable to be built in.");
print_usage_line(0, "");
print_usage_line(1, "-terse-errors");
print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line.");
print_usage_line(0, "");
print_usage_line(1, "-thread-count:<integer>");
print_usage_line(2, "Overrides the number of threads the compiler will use to compile with.");
print_usage_line(2, "Example: -thread-count:2");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-use-separate-modules");
print_usage_line(2, "The backend generates multiple build units which are then linked together.");
print_usage_line(2, "Normally, a single build unit is generated for a standard project.");
@@ -2331,11 +2613,6 @@ gb_internal void print_show_help(String const arg0, String const &command) {
}
if (check) {
print_usage_line(1, "-no-threaded-checker");
print_usage_line(2, "Disables multithreading in the semantic checker stage.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-vet");
@@ -2348,243 +2625,69 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(3, "-vet-using-stmt");
print_usage_line(0, "");
print_usage_line(1, "-vet-unused");
print_usage_line(2, "Checks for unused declarations.");
print_usage_line(1, "-vet-cast");
print_usage_line(2, "Errs on casting a value to its own type or using `transmute` rather than `cast`.");
print_usage_line(0, "");
print_usage_line(1, "-vet-unused-variables");
print_usage_line(2, "Checks for unused variable declarations.");
print_usage_line(1, "-vet-packages:<comma-separated-strings>");
print_usage_line(2, "Sets which packages by name will be vetted.");
print_usage_line(2, "Files with specific +vet tags will not be ignored if they are not in the packages set.");
print_usage_line(0, "");
print_usage_line(1, "-vet-unused-imports");
print_usage_line(2, "Checks for unused import declarations.");
print_usage_line(1, "-vet-semicolon");
print_usage_line(2, "Errs on unneeded semicolons.");
print_usage_line(0, "");
print_usage_line(1, "-vet-shadowing");
print_usage_line(2, "Checks for variable shadowing within procedures.");
print_usage_line(0, "");
print_usage_line(1, "-vet-using-stmt");
print_usage_line(2, "Checks for the use of 'using' as a statement.");
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
print_usage_line(0, "");
print_usage_line(1, "-vet-using-param");
print_usage_line(2, "Checks for the use of 'using' on procedure parameters.");
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
print_usage_line(0, "");
print_usage_line(1, "-vet-style");
print_usage_line(2, "Errs on missing trailing commas followed by a newline.");
print_usage_line(2, "Errs on deprecated syntax.");
print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style).");
print_usage_line(0, "");
print_usage_line(1, "-vet-semicolon");
print_usage_line(2, "Errs on unneeded semicolons.");
print_usage_line(0, "");
print_usage_line(1, "-vet-cast");
print_usage_line(2, "Errs on casting a value to its own type or using `transmute` rather than `cast`.");
print_usage_line(0, "");
print_usage_line(1, "-vet-tabs");
print_usage_line(2, "Errs when the use of tabs has not been used for indentation.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-custom-attribute:<string>");
print_usage_line(2, "Add a custom attribute which will be ignored if it is unknown.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-custom-attribute:my_tag");
print_usage_line(3, "-custom-attribute:my_tag,the_other_thing");
print_usage_line(3, "-custom-attribute:my_tag -custom-attribute:the_other_thing");
print_usage_line(1, "-vet-unused");
print_usage_line(2, "Checks for unused declarations (variables and imports).");
print_usage_line(0, "");
print_usage_line(1, "-ignore-unknown-attributes");
print_usage_line(2, "Ignores unknown attributes.");
print_usage_line(2, "This can be used with metaprogramming tools.");
print_usage_line(1, "-vet-unused-imports");
print_usage_line(2, "Checks for unused import declarations.");
print_usage_line(0, "");
if (command != "test") {
print_usage_line(1, "-no-entry-point");
print_usage_line(2, "Removes default requirement of an entry point (e.g. main procedure).");
print_usage_line(0, "");
}
}
if (test_only) {
print_usage_line(1, "-all-packages");
print_usage_line(2, "Tests all packages imported into the given initial package.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-minimum-os-version:<string>");
print_usage_line(2, "Sets the minimum OS version targeted by the application.");
print_usage_line(2, "Default: -minimum-os-version:11.0.0");
print_usage_line(2, "Only used when target is Darwin, if given, linking mismatched versions will emit a warning.");
print_usage_line(1, "-vet-unused-procedures");
print_usage_line(2, "Checks for unused procedures.");
print_usage_line(2, "Must be used with -vet-packages or specified on a per file with +vet tags.");
print_usage_line(0, "");
print_usage_line(1, "-extra-linker-flags:<string>");
print_usage_line(2, "Adds extra linker specific flags in a string.");
print_usage_line(1, "-vet-unused-variables");
print_usage_line(2, "Checks for unused variable declarations.");
print_usage_line(0, "");
print_usage_line(1, "-extra-assembler-flags:<string>");
print_usage_line(2, "Adds extra assembler specific flags in a string.");
print_usage_line(1, "-vet-using-param");
print_usage_line(2, "Checks for the use of 'using' on procedure parameters.");
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
print_usage_line(0, "");
print_usage_line(1, "-microarch:<string>");
print_usage_line(2, "Specifies the specific micro-architecture for the build in a string.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(3, "-microarch:\"?\" for a list");
print_usage_line(0, "");
print_usage_line(1, "-target-features:<string>");
print_usage_line(2, "Specifies CPU features to enable on top of the enabled features implied by -microarch.");
print_usage_line(2, "Examples:");
print_usage_line(3, "-target-features:atomics");
print_usage_line(3, "-target-features:\"sse2,aes\"");
print_usage_line(3, "-target-features:\"?\" for a list");
print_usage_line(0, "");
print_usage_line(1, "-strict-target-features");
print_usage_line(2, "Makes @(enable_target_features=\"...\") behave the same way as @(require_target_features=\"...\").");
print_usage_line(2, "This enforces that all generated code uses features supported by the combination of -target, -microarch, and -target-features.");
print_usage_line(0, "");
print_usage_line(1, "-reloc-mode:<string>");
print_usage_line(2, "Specifies the reloc mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-reloc-mode:default");
print_usage_line(3, "-reloc-mode:static");
print_usage_line(3, "-reloc-mode:pic");
print_usage_line(3, "-reloc-mode:dynamic-no-pic");
print_usage_line(0, "");
print_usage_line(1, "-disable-red-zone");
print_usage_line(2, "Disables red zone on a supported freestanding target.");
print_usage_line(0, "");
print_usage_line(1, "-dynamic-map-calls");
print_usage_line(2, "Uses dynamic map calls to minimize code generation at the cost of runtime execution.");
print_usage_line(0, "");
}
if (build) {
print_usage_line(1, "-print-linker-flags");
print_usage_line(2, "Prints the all of the flags/arguments that will be passed to the linker.");
print_usage_line(1, "-vet-using-stmt");
print_usage_line(2, "Checks for the use of 'using' as a statement.");
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring.");
print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-disallow-do");
print_usage_line(2, "Disallows the 'do' keyword in the project.");
print_usage_line(0, "");
print_usage_line(1, "-default-to-nil-allocator");
print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing.");
print_usage_line(0, "");
print_usage_line(1, "-strict-style");
print_usage_line(2, "This enforces parts of same style as the Odin compiler, prefer '-vet-style -vet-semicolon' if you do not want to match it exactly.");
print_usage_line(2, "");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons.");
print_usage_line(2, "Errs on missing trailing commas followed by a newline.");
print_usage_line(2, "Errs on deprecated syntax.");
print_usage_line(2, "Errs when the attached-brace style in not adhered to (also known as 1TBS).");
print_usage_line(2, "Errs when 'case' labels are not in the same column as the associated 'switch' token.");
print_usage_line(0, "");
print_usage_line(1, "-ignore-warnings");
print_usage_line(2, "Ignores warning messages.");
print_usage_line(0, "");
print_usage_line(1, "-warnings-as-errors");
print_usage_line(2, "Treats warning messages as error messages.");
print_usage_line(0, "");
print_usage_line(1, "-terse-errors");
print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line.");
print_usage_line(0, "");
print_usage_line(1, "-json-errors");
print_usage_line(2, "Prints the error messages as json to stderr.");
print_usage_line(0, "");
print_usage_line(1, "-error-pos-style:<string>");
print_usage_line(2, "Available options:");
print_usage_line(3, "-error-pos-style:unix file/path:45:3:");
print_usage_line(3, "-error-pos-style:odin file/path(45:3)");
print_usage_line(3, "-error-pos-style:default (Defaults to 'odin'.)");
print_usage_line(0, "");
print_usage_line(1, "-max-error-count:<integer>");
print_usage_line(2, "Sets the maximum number of errors that can be displayed before the compiler terminates.");
print_usage_line(2, "Must be an integer >0.");
print_usage_line(2, "If not set, the default max error count is %d.", DEFAULT_MAX_ERROR_COLLECTOR_COUNT);
print_usage_line(0, "");
print_usage_line(1, "-min-link-libs");
print_usage_line(2, "If set, the number of linked libraries will be minimized to prevent duplications.");
print_usage_line(2, "This is useful for so called \"dumb\" linkers compared to \"smart\" linkers.");
print_usage_line(0, "");
print_usage_line(1, "-foreign-error-procedures");
print_usage_line(2, "States that the error procedures used in the runtime are defined in a separate translation unit.");
print_usage_line(0, "");
}
if (run_or_build) {
print_usage_line(1, "-obfuscate-source-code-locations");
print_usage_line(2, "Obfuscate the file and procedure strings, and line and column numbers, stored with a 'runtime.Source_Code_Location' value.");
print_usage_line(0, "");
print_usage_line(1, "-sanitize:<string>");
print_usage_line(2, "Enables sanitization analysis.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-sanitize:address");
print_usage_line(3, "-sanitize:memory");
print_usage_line(3, "-sanitize:thread");
print_usage_line(2, "NOTE: This flag can be used multiple times.");
print_usage_line(0, "");
}
if (run_or_build) {
#if defined(GB_SYSTEM_WINDOWS)
print_usage_line(1, "-ignore-vs-search");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Ignores the Visual Studio search for library paths.");
print_usage_line(0, "");
print_usage_line(1, "-resource:<filepath>");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the resource file for the executable.");
print_usage_line(2, "Example: -resource:path/to/file.rc");
print_usage_line(2, "or: -resource:path/to/file.res for a precompiled one.");
print_usage_line(0, "");
print_usage_line(1, "-pdb-name:<filepath>");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the generated PDB name when -debug is enabled.");
print_usage_line(2, "Example: -pdb-name:different.pdb");
print_usage_line(0, "");
print_usage_line(1, "-subsystem:<option>");
print_usage_line(2, "[Windows only]");
print_usage_line(2, "Defines the subsystem for the application.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-subsystem:console");
print_usage_line(3, "-subsystem:windows");
print_usage_line(0, "");
#endif
}
}
@@ -3135,6 +3238,9 @@ int main(int arg_count, char const **arg_ptr) {
build_context.command = command;
string_set_init(&build_context.custom_attributes);
string_set_init(&build_context.vet_packages);
if (!parse_build_flags(args)) {
return 1;
}
@@ -3304,10 +3410,6 @@ int main(int arg_count, char const **arg_ptr) {
print_all_errors();
return 1;
}
if (any_warnings()) {
print_all_errors();
}
checker->parser = parser;
init_checker(checker);
+193 -91
View File
@@ -1,10 +1,28 @@
#include "parser_pos.cpp"
gb_internal bool in_vet_packages(AstFile *file) {
if (file == nullptr) {
return true;
}
if (file->pkg == nullptr) {
return true;
}
if (build_context.vet_packages.entries.count == 0) {
return true;
}
return string_set_exists(&build_context.vet_packages, file->pkg->name);
}
gb_internal u64 ast_file_vet_flags(AstFile *f) {
if (f != nullptr && f->vet_flags_set) {
return f->vet_flags;
}
return build_context.vet_flags;
bool found = in_vet_packages(f);
if (found) {
return build_context.vet_flags;
}
return 0;
}
gb_internal bool ast_file_vet_style(AstFile *f) {
@@ -430,7 +448,8 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->StructType.fields = clone_ast_array(n->StructType.fields, f);
n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params, f);
n->StructType.align = clone_ast(n->StructType.align, f);
n->StructType.field_align = clone_ast(n->StructType.field_align, f);
n->StructType.min_field_align = clone_ast(n->StructType.min_field_align, f);
n->StructType.max_field_align = clone_ast(n->StructType.max_field_align, f);
n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses, f);
break;
case Ast_UnionType:
@@ -1199,7 +1218,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_no_copy,
Ast *align, Ast *field_align,
Ast *align, Ast *min_field_align, Ast *max_field_align,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_StructType);
result->StructType.token = token;
@@ -1210,7 +1229,8 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
result->StructType.is_raw_union = is_raw_union;
result->StructType.is_no_copy = is_no_copy;
result->StructType.align = align;
result->StructType.field_align = field_align;
result->StructType.min_field_align = min_field_align;
result->StructType.max_field_align = max_field_align;
result->StructType.where_token = where_token;
result->StructType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -2739,7 +2759,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
bool is_raw_union = false;
bool no_copy = false;
Ast *align = nullptr;
Ast *field_align = nullptr;
Ast *min_field_align = nullptr;
Ast *max_field_align = nullptr;
if (allow_token(f, Token_OpenParen)) {
isize param_count = 0;
@@ -2777,18 +2798,43 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
gb_string_free(s);
}
} else if (tag.string == "field_align") {
if (field_align) {
if (min_field_align) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
field_align = parse_expr(f, true);
if (field_align && field_align->kind != Ast_ParenExpr) {
syntax_warning(tag, "#field_align has been deprecated in favour of #min_field_align");
min_field_align = parse_expr(f, true);
if (min_field_align && min_field_align->kind != Ast_ParenExpr) {
ERROR_BLOCK();
gbString s = expr_to_string(field_align);
gbString s = expr_to_string(min_field_align);
syntax_warning(tag, "#field_align requires parentheses around the expression");
error_line("\tSuggestion: #field_align(%s)", s);
error_line("\tSuggestion: #min_field_align(%s)", s);
gb_string_free(s);
}
} else if (tag.string == "raw_union") {
} else if (tag.string == "min_field_align") {
if (min_field_align) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
min_field_align = parse_expr(f, true);
if (min_field_align && min_field_align->kind != Ast_ParenExpr) {
ERROR_BLOCK();
gbString s = expr_to_string(min_field_align);
syntax_warning(tag, "#min_field_align requires parentheses around the expression");
error_line("\tSuggestion: #min_field_align(%s)", s);
gb_string_free(s);
}
} else if (tag.string == "max_field_align") {
if (max_field_align) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
max_field_align = parse_expr(f, true);
if (max_field_align && max_field_align->kind != Ast_ParenExpr) {
ERROR_BLOCK();
gbString s = expr_to_string(max_field_align);
syntax_warning(tag, "#max_field_align requires parentheses around the expression");
error_line("\tSuggestion: #max_field_align(%s)", s);
gb_string_free(s);
}
}else if (tag.string == "raw_union") {
if (is_raw_union) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
@@ -2838,7 +2884,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
parser_check_polymorphic_record_parameters(f, polymorphic_params);
return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, field_align, where_token, where_clauses);
return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, no_copy, align, min_field_align, max_field_align, where_token, where_clauses);
} break;
case Token_union: {
@@ -4359,10 +4405,14 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
}
}
allow_field_separator(f);
bool more_fields = allow_field_separator(f);
Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment);
array_add(&params, param);
if (!more_fields) {
if (name_count_) *name_count_ = total_name_count;
return ast_field_list(f, start_token, params);
}
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF &&
@@ -4949,7 +4999,7 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
}
if (f->in_when_statement) {
syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags");
syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '#+build' tags");
}
if (kind != ImportDecl_Standard) {
@@ -5337,6 +5387,12 @@ gb_internal Ast *parse_stmt(AstFile *f) {
s = ast_empty_stmt(f, token);
expect_semicolon(f);
return s;
case Token_FileTag:
// This is always an error because all valid file tags will have been processed in `parse_file` already.
// Any remaining file tags must be past the package line and thus invalid.
syntax_error(token, "Lines starting with #+ (file tags) are only allowed before the package line.");
return ast_bad_stmt(f, token, f->curr_token);
}
// Error correction statements
@@ -5372,18 +5428,9 @@ gb_internal Ast *parse_stmt(AstFile *f) {
}
gb_internal u64 check_vet_flags(AstFile *file) {
if (file && file->vet_flags_set) {
return file->vet_flags;
}
return build_context.vet_flags;
}
gb_internal void parse_enforce_tabs(AstFile *f) {
// Checks to see if tabs have been used for indentation
if ((check_vet_flags(f) & VetFlag_Tabs) == 0) {
if ((ast_file_vet_flags(f) & VetFlag_Tabs) == 0) {
return;
}
@@ -5969,20 +6016,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
do_error(node, "Unknown library collection: '%.*s'", LIT(collection_name));
return false;
}
} else {
#if !defined(GB_SYSTEM_WINDOWS)
// @NOTE(vassvik): foreign imports of shared libraries that are not in the system collection on
// linux/mac have to be local to the executable for consistency with shared libraries.
// Unix does not have a concept of "import library" for shared/dynamic libraries,
// so we need to pass the relative path to the linker, and add the current
// working directory of the exe to the library search paths.
// Static libraries can be linked directly with the full pathname
//
if (node->kind == Ast_ForeignImportDecl && (string_ends_with(file_str, str_lit(".so")) || string_contains_string(file_str, str_lit(".so.")))) {
*path = file_str;
return true;
}
#endif
}
if (is_package_name_reserved(file_str)) {
@@ -6128,7 +6161,7 @@ gb_internal String build_tag_get_token(String s, String *out) {
}
gb_internal bool parse_build_tag(Token token_for_pos, String s) {
String const prefix = str_lit("+build");
String const prefix = str_lit("build");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
@@ -6141,6 +6174,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
while (s.len > 0) {
bool this_kind_correct = true;
bool this_kind_os_seen = false;
bool this_kind_arch_seen = false;
int num_tokens = 0;
do {
String p = string_trim_whitespace(build_tag_get_token(s, &s));
if (p.len == 0) break;
@@ -6166,7 +6203,18 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
TargetOsKind os = get_target_os_from_string(p);
TargetArchKind arch = get_target_arch_from_string(p);
num_tokens += 1;
// Catches 'windows linux', which is an impossible combination.
// Also catches usage of more than two things within a comma separated group.
if (num_tokens > 2 || (this_kind_os_seen && os != TargetOs_Invalid) || (this_kind_arch_seen && arch != TargetArch_Invalid)) {
syntax_error(token_for_pos, "Invalid build tag: Missing ',' before '%.*s'. Format: '#+build linux, windows amd64, darwin'", LIT(p));
break;
}
if (os != TargetOs_Invalid) {
this_kind_os_seen = true;
GB_ASSERT(arch == TargetArch_Invalid);
if (is_notted) {
this_kind_correct = this_kind_correct && (os != build_context.metrics.os);
@@ -6174,6 +6222,8 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
this_kind_correct = this_kind_correct && (os == build_context.metrics.os);
}
} else if (arch != TargetArch_Invalid) {
this_kind_arch_seen = true;
if (is_notted) {
this_kind_correct = this_kind_correct && (arch != build_context.metrics.arch);
} else {
@@ -6213,7 +6263,7 @@ gb_internal String vet_tag_get_token(String s, String *out) {
gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
String const prefix = str_lit("+vet");
String const prefix = str_lit("vet");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
@@ -6318,7 +6368,7 @@ gb_internal isize calc_decl_count(Ast *decl) {
}
gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s) {
String const prefix = str_lit("+build-project-name");
String const prefix = str_lit("build-project-name");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
if (s.len == 0) {
@@ -6362,6 +6412,48 @@ gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s
return any_correct;
}
gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) {
if (string_starts_with(lc, str_lit("build-project-name"))) {
if (!parse_build_project_directory_tag(tok, lc)) {
return false;
}
} else if (string_starts_with(lc, str_lit("build"))) {
if (!parse_build_tag(tok, lc)) {
return false;
}
} else if (string_starts_with(lc, str_lit("vet"))) {
f->vet_flags = parse_vet_tag(tok, lc);
f->vet_flags_set = true;
} else if (string_starts_with(lc, str_lit("ignore"))) {
return false;
} 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
} else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
// Ignore
} else {
f->flags |= AstFile_IsLazy;
}
} else if (lc == "no-instrumentation") {
f->flags |= AstFile_NoInstrumentation;
} else {
error(tok, "Unknown tag '%.*s'", LIT(lc));
}
return true;
}
gb_internal bool parse_file(Parser *p, AstFile *f) {
if (f->tokens.count == 0) {
return true;
@@ -6380,9 +6472,34 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
CommentGroup *docs = f->lead_comment;
Array<Token> tags = array_make<Token>(temporary_allocator());
bool first_invalid_token_set = false;
Token first_invalid_token = {};
while (f->curr_token.kind != Token_package && f->curr_token.kind != Token_EOF) {
if (f->curr_token.kind == Token_Comment) {
consume_comment_groups(f, f->prev_token);
} else if (f->curr_token.kind == Token_FileTag) {
array_add(&tags, f->curr_token);
advance_token(f);
} else {
if (!first_invalid_token_set) {
first_invalid_token_set = true;
first_invalid_token = f->curr_token;
}
advance_token(f);
}
}
if (f->curr_token.kind != Token_package) {
ERROR_BLOCK();
syntax_error(f->curr_token, "Expected a package declaration at the beginning of the file");
// The while loop above scanned until it found the package token. If we never
// found one, then make this error appear on the first invalid token line.
Token t = first_invalid_token_set ? first_invalid_token : f->curr_token;
syntax_error(t, "Expected a package declaration at the beginning of the file");
// IMPORTANT NOTE(bill): this is technically a race condition with the suggestion, but it's ony a suggession
// so in practice is should be "fine"
if (f->pkg && f->pkg->name != "") {
@@ -6391,18 +6508,16 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
return false;
}
// There was an OK package declaration. But there some invalid token was hit before the package declaration.
if (first_invalid_token_set) {
syntax_error(first_invalid_token, "Expected only comments or lines starting with '#+' before the package declaration");
return false;
}
f->package_token = expect_token(f, Token_package);
if (f->package_token.kind != Token_package) {
return false;
}
if (docs != nullptr) {
TokenPos end = token_pos_end(docs->list[docs->list.count-1]);
if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) {
// Okay
} else {
docs = nullptr;
}
}
Token package_name = expect_token_after(f, Token_Ident, "package");
if (package_name.kind == Token_Ident) {
@@ -6416,53 +6531,40 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
}
f->package_name = package_name.string;
if (!f->pkg->is_single_file && docs != nullptr && docs->list.count > 0) {
for (Token const &tok : docs->list) {
GB_ASSERT(tok.kind == Token_Comment);
String str = tok.string;
if (string_starts_with(str, str_lit("//"))) {
// TODO: Shouldn't single file only matter for build tags? no-instrumentation for example
// should be respected even when in single file mode.
if (!f->pkg->is_single_file) {
if (docs != nullptr && docs->list.count > 0) {
for (Token const &tok : docs->list) {
GB_ASSERT(tok.kind == Token_Comment);
String str = tok.string;
if (!string_starts_with(str, str_lit("//"))) {
continue;
}
String lc = string_trim_whitespace(substring(str, 2, str.len));
if (lc.len > 0 && lc[0] == '+') {
if (string_starts_with(lc, str_lit("+build-project-name"))) {
if (!parse_build_project_directory_tag(tok, lc)) {
return false;
}
} else if (string_starts_with(lc, str_lit("+build"))) {
if (!parse_build_tag(tok, lc)) {
return false;
}
} else if (string_starts_with(lc, str_lit("+vet"))) {
f->vet_flags = parse_vet_tag(tok, lc);
f->vet_flags_set = true;
} else if (string_starts_with(lc, str_lit("+ignore"))) {
if (string_starts_with(lc, str_lit("+"))) {
syntax_warning(tok, "'//+' is deprecated: Use '#+' instead");
String lt = substring(lc, 1, lc.len);
if (parse_file_tag(lt, tok, f) == false) {
return false;
} 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
} else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
// Ignore
} else {
f->flags |= AstFile_IsLazy;
}
} else if (lc == "+no-instrumentation") {
f->flags |= AstFile_NoInstrumentation;
} else {
warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
}
}
}
}
for (Token const &tok : tags) {
GB_ASSERT(tok.kind == Token_FileTag);
String str = tok.string;
if (string_starts_with(str, str_lit("#+"))) {
String lt = string_trim_whitespace(substring(str, 2, str.len));
if (parse_file_tag(lt, tok, f) == false) {
return false;
}
}
}
}
Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment);
+2 -1
View File
@@ -736,7 +736,8 @@ AST_KIND(_TypeBegin, "", bool) \
isize field_count; \
Ast *polymorphic_params; \
Ast *align; \
Ast *field_align; \
Ast *min_field_align; \
Ast *max_field_align; \
Token where_token; \
Slice<Ast *> where_clauses; \
bool is_packed; \
+15
View File
@@ -2,6 +2,7 @@
TOKEN_KIND(Token_Invalid, "Invalid"), \
TOKEN_KIND(Token_EOF, "EOF"), \
TOKEN_KIND(Token_Comment, "Comment"), \
TOKEN_KIND(Token_FileTag, "FileTag"), \
\
TOKEN_KIND(Token__LiteralBegin, ""), \
TOKEN_KIND(Token_Ident, "identifier"), \
@@ -939,6 +940,20 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '!') {
token->kind = Token_Comment;
tokenizer_skip_line(t);
} else if (t->curr_rune == '+') {
token->kind = Token_FileTag;
// Skip until end of line or until we hit what is probably a comment.
// The parsing of tags happens in `parse_file`.
while (t->curr_rune != GB_RUNE_EOF) {
if (t->curr_rune == '\n') {
break;
}
if (t->curr_rune == '/') {
break;
}
advance_to_next_rune(t);
}
}
break;
case '/':
+16 -4
View File
@@ -137,7 +137,8 @@ struct TypeStruct {
Scope * scope;
i64 custom_align;
i64 custom_field_align;
i64 custom_min_field_align;
i64 custom_max_field_align;
Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent;
Wait_Signal polymorphic_wait_signal;
@@ -3912,6 +3913,14 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
max = align;
}
}
if (t->Struct.custom_min_field_align > 0) {
max = gb_max(max, t->Struct.custom_min_field_align);
}
if (t->Struct.custom_max_field_align != 0 &&
t->Struct.custom_max_field_align > t->Struct.custom_min_field_align) {
max = gb_min(max, t->Struct.custom_max_field_align);
}
return max;
} break;
@@ -3950,7 +3959,7 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
return gb_clamp(next_pow2(type_size_of_internal(t, path)), 1, build_context.max_align);
}
gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align) {
gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_packed, bool is_raw_union, i64 min_field_align, i64 max_field_align) {
gbAllocator a = permanent_allocator();
auto offsets = gb_alloc_array(a, i64, fields.count);
i64 curr_offset = 0;
@@ -3980,6 +3989,9 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
} else {
Type *t = fields[i]->type;
i64 align = gb_max(type_align_of(t), min_field_align);
if (max_field_align > min_field_align) {
align = gb_min(align, max_field_align);
}
i64 size = gb_max(type_size_of( t), 0);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
@@ -3996,7 +4008,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Struct.offset_mutex);
if (!t->Struct.are_offsets_set) {
t->Struct.are_offsets_being_processed = true;
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_field_align);
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union, t->Struct.custom_min_field_align, t->Struct.custom_max_field_align);
t->Struct.are_offsets_being_processed = false;
t->Struct.are_offsets_set = true;
return true;
@@ -4005,7 +4017,7 @@ gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) {
t->Tuple.are_offsets_being_processed = true;
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1);
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false, 1, 0);
t->Tuple.are_offsets_being_processed = false;
t->Tuple.are_offsets_set = true;
return true;