mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-01 18:44:41 +00:00
Merge remote-tracking branch 'upstream/master' into fix_odin_test
This commit is contained in:
@@ -89,7 +89,9 @@ template <typename T>
|
||||
void slice_init(Slice<T> *s, gbAllocator const &allocator, isize count) {
|
||||
GB_ASSERT(count >= 0);
|
||||
s->data = gb_alloc_array(allocator, T, count);
|
||||
GB_ASSERT(s->data != nullptr);
|
||||
if (count > 0) {
|
||||
GB_ASSERT(s->data != nullptr);
|
||||
}
|
||||
s->count = count;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_OPENBSD)
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
|
||||
*/
|
||||
@@ -242,6 +247,14 @@ void report_ram_info() {
|
||||
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
|
||||
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
uint64_t ram_amount;
|
||||
size_t val_size = sizeof(ram_amount);
|
||||
|
||||
int sysctls[] = { CTL_HW, HW_PHYSMEM64 };
|
||||
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
|
||||
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
|
||||
}
|
||||
#else
|
||||
gb_printf("Unknown.\n");
|
||||
#endif
|
||||
@@ -473,11 +486,11 @@ void print_bug_report_help() {
|
||||
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
/*
|
||||
Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
|
||||
Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
|
||||
*/
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release");
|
||||
gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
|
||||
defer (gb_file_free_contents(&release));
|
||||
|
||||
b32 found = 0;
|
||||
@@ -643,6 +656,14 @@ void print_bug_report_help() {
|
||||
} else {
|
||||
gb_printf("macOS: Unknown\n");
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
struct utsname un;
|
||||
|
||||
if (uname(&un) != -1) {
|
||||
gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
|
||||
} else {
|
||||
gb_printf("OpenBSD: Unknown\n");
|
||||
}
|
||||
#else
|
||||
gb_printf("Unknown\n");
|
||||
|
||||
@@ -657,4 +678,4 @@ void print_bug_report_help() {
|
||||
And RAM info.
|
||||
*/
|
||||
report_ram_info();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if defined(GB_SYSTEM_FREEBSD)
|
||||
#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
@@ -16,6 +16,7 @@ enum TargetOsKind {
|
||||
TargetOs_linux,
|
||||
TargetOs_essence,
|
||||
TargetOs_freebsd,
|
||||
TargetOs_openbsd,
|
||||
|
||||
TargetOs_wasi,
|
||||
TargetOs_js,
|
||||
@@ -53,6 +54,7 @@ String target_os_names[TargetOs_COUNT] = {
|
||||
str_lit("linux"),
|
||||
str_lit("essence"),
|
||||
str_lit("freebsd"),
|
||||
str_lit("openbsd"),
|
||||
|
||||
str_lit("wasi"),
|
||||
str_lit("js"),
|
||||
@@ -278,7 +280,7 @@ bool global_ignore_warnings(void) {
|
||||
}
|
||||
|
||||
|
||||
gb_global TargetMetrics target_windows_386 = {
|
||||
gb_global TargetMetrics target_windows_i386 = {
|
||||
TargetOs_windows,
|
||||
TargetArch_i386,
|
||||
4,
|
||||
@@ -294,7 +296,7 @@ gb_global TargetMetrics target_windows_amd64 = {
|
||||
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_linux_386 = {
|
||||
gb_global TargetMetrics target_linux_i386 = {
|
||||
TargetOs_linux,
|
||||
TargetArch_i386,
|
||||
4,
|
||||
@@ -337,7 +339,7 @@ gb_global TargetMetrics target_darwin_arm64 = {
|
||||
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_freebsd_386 = {
|
||||
gb_global TargetMetrics target_freebsd_i386 = {
|
||||
TargetOs_freebsd,
|
||||
TargetArch_i386,
|
||||
4,
|
||||
@@ -354,6 +356,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
|
||||
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_openbsd_amd64 = {
|
||||
TargetOs_openbsd,
|
||||
TargetArch_amd64,
|
||||
8,
|
||||
16,
|
||||
str_lit("x86_64-unknown-openbsd-elf"),
|
||||
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_essence_amd64 = {
|
||||
TargetOs_essence,
|
||||
TargetArch_amd64,
|
||||
@@ -410,13 +421,14 @@ gb_global NamedTargetMetrics named_targets[] = {
|
||||
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
|
||||
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
|
||||
{ str_lit("essence_amd64"), &target_essence_amd64 },
|
||||
{ str_lit("linux_386"), &target_linux_386 },
|
||||
{ str_lit("linux_i386"), &target_linux_i386 },
|
||||
{ str_lit("linux_amd64"), &target_linux_amd64 },
|
||||
{ str_lit("linux_arm64"), &target_linux_arm64 },
|
||||
{ str_lit("windows_386"), &target_windows_386 },
|
||||
{ str_lit("windows_i386"), &target_windows_i386 },
|
||||
{ str_lit("windows_amd64"), &target_windows_amd64 },
|
||||
{ str_lit("freebsd_386"), &target_freebsd_386 },
|
||||
{ str_lit("freebsd_i386"), &target_freebsd_i386 },
|
||||
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
||||
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
|
||||
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
||||
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
|
||||
{ str_lit("js_wasm32"), &target_js_wasm32 },
|
||||
@@ -722,10 +734,38 @@ String internal_odin_root_dir(void) {
|
||||
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
|
||||
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
|
||||
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
|
||||
#else
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
int error;
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC_ARGS,
|
||||
getpid(),
|
||||
KERN_PROC_ARGV,
|
||||
};
|
||||
// get argv size
|
||||
error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
|
||||
if (error == -1) {
|
||||
// sysctl error
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
// get argv
|
||||
char **argv = (char **)gb_malloc(len);
|
||||
error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
|
||||
if (error == -1) {
|
||||
// sysctl error
|
||||
gb_mfree(argv);
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
// copy argv[0] to path_buf
|
||||
len = gb_strlen(argv[0]);
|
||||
if(len < path_buf.count) {
|
||||
gb_memmove(&path_buf[0], argv[0], len);
|
||||
}
|
||||
gb_mfree(argv);
|
||||
#endif
|
||||
if(len == 0) {
|
||||
if(len == 0 || len == -1) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
@@ -922,6 +962,8 @@ void init_build_context(TargetMetrics *cross_target) {
|
||||
#endif
|
||||
#elif defined(GB_SYSTEM_FREEBSD)
|
||||
metrics = &target_freebsd_amd64;
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
metrics = &target_openbsd_amd64;
|
||||
#elif defined(GB_CPU_ARM)
|
||||
metrics = &target_linux_arm64;
|
||||
#else
|
||||
@@ -929,13 +971,13 @@ void init_build_context(TargetMetrics *cross_target) {
|
||||
#endif
|
||||
#else
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
metrics = &target_windows_386;
|
||||
metrics = &target_windows_i386;
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
#error "Build Error: Unsupported architecture"
|
||||
#elif defined(GB_SYSTEM_FREEBSD)
|
||||
metrics = &target_freebsd_386;
|
||||
metrics = &target_freebsd_i386;
|
||||
#else
|
||||
metrics = &target_linux_386;
|
||||
metrics = &target_linux_i386;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -980,6 +1022,9 @@ void init_build_context(TargetMetrics *cross_target) {
|
||||
case TargetOs_freebsd:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
case TargetOs_openbsd:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
}
|
||||
} else if (bc->metrics.arch == TargetArch_i386) {
|
||||
switch (bc->metrics.os) {
|
||||
|
||||
@@ -223,36 +223,35 @@ void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<T
|
||||
map_set(&c->info->objc_msgSend_types, call, data);
|
||||
mutex_unlock(&c->info->objc_types_mutex);
|
||||
|
||||
add_package_dependency(c, "runtime", "objc_lookUpClass");
|
||||
add_package_dependency(c, "runtime", "sel_registerName");
|
||||
add_package_dependency(c, "runtime", "objc_allocateClassPair");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
|
||||
}
|
||||
|
||||
add_package_dependency(c, "runtime", "objc_msgSend");
|
||||
add_package_dependency(c, "runtime", "objc_msgSend_fpret");
|
||||
add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
|
||||
add_package_dependency(c, "runtime", "objc_msgSend_stret");
|
||||
bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
|
||||
Operand op = {};
|
||||
check_expr(c, &op, expr);
|
||||
if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
|
||||
if (name_) *name_ = op.value.value_string;
|
||||
return true;
|
||||
}
|
||||
gbString e = expr_to_string(op.expr);
|
||||
gbString t = type_to_string(op.type);
|
||||
error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
|
||||
auto const is_constant_string = [](CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) -> bool {
|
||||
Operand op = {};
|
||||
check_expr(c, &op, expr);
|
||||
if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
|
||||
if (name_) *name_ = op.value.value_string;
|
||||
return true;
|
||||
}
|
||||
gbString e = expr_to_string(op.expr);
|
||||
gbString t = type_to_string(op.type);
|
||||
error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
};
|
||||
String builtin_name = builtin_procs[id].name;
|
||||
|
||||
if (build_context.metrics.os != TargetOs_darwin) {
|
||||
error(call, "'%.*s' only works on darwin", LIT(builtin_name));
|
||||
return false;
|
||||
// allow on doc generation (e.g. Metal stuff)
|
||||
if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) {
|
||||
error(call, "'%.*s' only works on darwin", LIT(builtin_name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -371,6 +370,10 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
|
||||
|
||||
}
|
||||
operand->mode = Addressing_Value;
|
||||
|
||||
try_to_add_package_dependency(c, "runtime", "objc_lookUpClass");
|
||||
try_to_add_package_dependency(c, "runtime", "sel_registerName");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
|
||||
return true;
|
||||
} break;
|
||||
}
|
||||
@@ -949,7 +952,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
mode = Addressing_Constant;
|
||||
value = exact_value_i64(at->EnumeratedArray.count);
|
||||
type = t_untyped_integer;
|
||||
} else if (is_type_slice(op_type) && id == BuiltinProc_len) {
|
||||
} else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
|
||||
mode = Addressing_Value;
|
||||
} else if (is_type_dynamic_array(op_type)) {
|
||||
mode = Addressing_Value;
|
||||
@@ -1103,7 +1106,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
|
||||
Selection sel = lookup_field(type, field_name, false);
|
||||
if (sel.entity == nullptr) {
|
||||
gbString type_str = type_to_string(type);
|
||||
gbString type_str = type_to_string_shorthand(type);
|
||||
error(ce->args[0],
|
||||
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
||||
gb_string_free(type_str);
|
||||
@@ -1115,7 +1118,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
return false;
|
||||
}
|
||||
if (sel.indirect) {
|
||||
gbString type_str = type_to_string(type);
|
||||
gbString type_str = type_to_string_shorthand(type);
|
||||
error(ce->args[0],
|
||||
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
|
||||
gb_string_free(type_str);
|
||||
@@ -1176,7 +1179,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
|
||||
Selection sel = lookup_field(type, field_name, false);
|
||||
if (sel.entity == nullptr) {
|
||||
gbString type_str = type_to_string(type);
|
||||
gbString type_str = type_to_string_shorthand(type);
|
||||
error(ce->args[0],
|
||||
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
|
||||
gb_string_free(type_str);
|
||||
@@ -1188,7 +1191,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
return false;
|
||||
}
|
||||
if (sel.indirect) {
|
||||
gbString type_str = type_to_string(type);
|
||||
gbString type_str = type_to_string_shorthand(type);
|
||||
error(ce->args[0],
|
||||
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
|
||||
gb_string_free(type_str);
|
||||
@@ -3503,6 +3506,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
case TargetOs_linux:
|
||||
case TargetOs_essence:
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_openbsd:
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_i386:
|
||||
case TargetArch_amd64:
|
||||
@@ -4086,6 +4090,18 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_constant_utf16_cstring:
|
||||
{
|
||||
String value = {};
|
||||
if (!is_constant_string(c, builtin_name, ce->args[0], &value)) {
|
||||
return false;
|
||||
}
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = alloc_type_multi_pointer(t_u16);
|
||||
operand->value = {};
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_type_proc(e->type)) {
|
||||
error(e->token, "Illegal declaration of a constant procedure value");
|
||||
}
|
||||
|
||||
e->parent_proc_decl = ctx->curr_proc_decl;
|
||||
|
||||
e->Constant.value = operand->value;
|
||||
|
||||
@@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
|
||||
}
|
||||
}
|
||||
|
||||
void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
|
||||
if (e->kind != Entity_TypeName) {
|
||||
return;
|
||||
}
|
||||
if (e->TypeName.objc_metadata == nullptr) {
|
||||
return;
|
||||
}
|
||||
TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata;
|
||||
Type *t = base_type(e->type);
|
||||
GB_ASSERT(t->kind == Type_Struct);
|
||||
|
||||
if (is_type) {
|
||||
for_array(i, objc_metadata->type_entries) {
|
||||
String name = objc_metadata->type_entries[i].name;
|
||||
string_set_add(set, name);
|
||||
}
|
||||
} else {
|
||||
for_array(i, objc_metadata->value_entries) {
|
||||
String name = objc_metadata->value_entries[i].name;
|
||||
string_set_add(set, name);
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, t->Struct.fields) {
|
||||
Entity *f = t->Struct.fields[i];
|
||||
if (f->flags & EntityFlag_Using && f->type != nullptr) {
|
||||
if (f->type->kind == Type_Named && f->type->Named.type_name) {
|
||||
populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
|
||||
ERROR_BLOCK();
|
||||
GB_ASSERT(e->kind == Entity_TypeName);
|
||||
GB_ASSERT(e->TypeName.objc_metadata != nullptr);
|
||||
auto *objc_metadata = e->TypeName.objc_metadata;
|
||||
mutex_lock(objc_metadata->mutex);
|
||||
defer (mutex_unlock(objc_metadata->mutex));
|
||||
|
||||
StringSet set = {};
|
||||
string_set_init(&set, heap_allocator());
|
||||
defer (string_set_destroy(&set));
|
||||
populate_check_did_you_mean_objc_entity(&set, e, is_type);
|
||||
|
||||
|
||||
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
|
||||
defer (did_you_mean_destroy(&d));
|
||||
for_array(i, set.entries) {
|
||||
did_you_mean_append(&d, set.entries[i].value);
|
||||
}
|
||||
check_did_you_mean_print(&d, prefix);
|
||||
}
|
||||
|
||||
void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
|
||||
ERROR_BLOCK();
|
||||
|
||||
@@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array<Entity *> const &fields,
|
||||
check_did_you_mean_print(&d, prefix);
|
||||
}
|
||||
|
||||
|
||||
void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
|
||||
ERROR_BLOCK();
|
||||
|
||||
@@ -4413,14 +4470,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
||||
|
||||
if (entity == nullptr) {
|
||||
gbString op_str = expr_to_string(op_expr);
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
gbString type_str = type_to_string_shorthand(operand->type);
|
||||
gbString sel_str = expr_to_string(selector);
|
||||
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
|
||||
|
||||
if (operand->type != nullptr && selector->kind == Ast_Ident) {
|
||||
String const &name = selector->Ident.token.string;
|
||||
Type *bt = base_type(operand->type);
|
||||
if (bt->kind == Type_Struct) {
|
||||
if (operand->type->kind == Type_Named &&
|
||||
operand->type->Named.type_name &&
|
||||
operand->type->Named.type_name->kind == Entity_TypeName &&
|
||||
operand->type->Named.type_name->TypeName.objc_metadata) {
|
||||
check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
|
||||
} else if (bt->kind == Type_Struct) {
|
||||
check_did_you_mean_type(name, bt->Struct.fields);
|
||||
} else if (bt->kind == Type_Enum) {
|
||||
check_did_you_mean_type(name, bt->Enum.fields);
|
||||
@@ -4449,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
||||
}
|
||||
|
||||
gbString op_str = expr_to_string(op_expr);
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
gbString type_str = type_to_string_shorthand(operand->type);
|
||||
gbString sel_str = expr_to_string(selector);
|
||||
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
|
||||
gb_string_free(sel_str);
|
||||
@@ -4474,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
||||
}
|
||||
|
||||
gbString op_str = expr_to_string(op_expr);
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
gbString type_str = type_to_string_shorthand(operand->type);
|
||||
gbString sel_str = expr_to_string(selector);
|
||||
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
|
||||
gb_string_free(sel_str);
|
||||
@@ -4487,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
|
||||
|
||||
if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
|
||||
gbString op_str = expr_to_string(op_expr);
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
gbString type_str = type_to_string_shorthand(operand->type);
|
||||
gbString sel_str = expr_to_string(selector);
|
||||
error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
|
||||
gb_string_free(sel_str);
|
||||
@@ -9774,6 +9836,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
|
||||
if (f->flags&FieldFlag_const) {
|
||||
str = gb_string_appendc(str, "#const ");
|
||||
}
|
||||
if (f->flags&FieldFlag_subtype) {
|
||||
str = gb_string_appendc(str, "#subtype ");
|
||||
}
|
||||
|
||||
for_array(i, f->names) {
|
||||
Ast *name = f->names[i];
|
||||
|
||||
@@ -144,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
|
||||
}
|
||||
|
||||
bool is_using = (p->flags&FieldFlag_using) != 0;
|
||||
bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
|
||||
|
||||
for_array(j, p->names) {
|
||||
Ast *name = p->names[j];
|
||||
@@ -158,6 +159,9 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
|
||||
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
|
||||
add_entity(ctx, ctx->scope, name, field);
|
||||
field->Variable.field_group_index = field_group_index;
|
||||
if (is_subtype) {
|
||||
field->flags |= EntityFlag_Subtype;
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
field->Variable.docs = docs;
|
||||
@@ -194,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entity *> *fields
|
||||
|
||||
populate_using_entity_scope(ctx, node, p, type);
|
||||
}
|
||||
|
||||
if (is_subtype && p->names.count > 0) {
|
||||
Type *first_type = fields_array[fields_array.count-1]->type;
|
||||
Type *t = base_type(type_deref(first_type));
|
||||
|
||||
if (!does_field_type_allow_using(t) &&
|
||||
p->names.count >= 1 &&
|
||||
p->names[0]->kind == Ast_Ident) {
|
||||
Token name_token = p->names[0]->Ident.token;
|
||||
gbString type_str = type_to_string(first_type);
|
||||
error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
|
||||
gb_string_free(type_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*fields = slice_from_array(fields_array);
|
||||
|
||||
@@ -225,8 +225,8 @@ bool decl_info_has_init(DeclInfo *d) {
|
||||
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
||||
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
|
||||
s->parent = parent;
|
||||
string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
|
||||
ptr_set_init(&s->imported, permanent_allocator(), 0);
|
||||
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
|
||||
ptr_set_init(&s->imported, heap_allocator(), 0);
|
||||
mutex_init(&s->mutex);
|
||||
|
||||
if (parent != nullptr && parent != builtin_pkg->scope) {
|
||||
@@ -733,12 +733,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co
|
||||
String n = make_string_c(name);
|
||||
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
|
||||
Entity *e = scope_lookup(p->scope, n);
|
||||
e->flags |= EntityFlag_Used;
|
||||
GB_ASSERT_MSG(e != nullptr, "%s", name);
|
||||
GB_ASSERT(c->decl != nullptr);
|
||||
e->flags |= EntityFlag_Used;
|
||||
add_dependency(c->info, c->decl, e);
|
||||
}
|
||||
|
||||
void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
|
||||
String n = make_string_c(name);
|
||||
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
|
||||
Entity *e = scope_lookup(p->scope, n);
|
||||
if (e == nullptr) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(c->decl != nullptr);
|
||||
e->flags |= EntityFlag_Used;
|
||||
add_dependency(c->info, c->decl, e);
|
||||
}
|
||||
|
||||
|
||||
void add_declaration_dependency(CheckerContext *c, Entity *e) {
|
||||
if (e == nullptr) {
|
||||
return;
|
||||
@@ -893,6 +906,7 @@ void init_universal(void) {
|
||||
{"Linux", TargetOs_linux},
|
||||
{"Essence", TargetOs_essence},
|
||||
{"FreeBSD", TargetOs_freebsd},
|
||||
{"OpenBSD", TargetOs_openbsd},
|
||||
{"WASI", TargetOs_wasi},
|
||||
{"JS", TargetOs_js},
|
||||
{"Freestanding", TargetOs_freestanding},
|
||||
|
||||
@@ -258,6 +258,9 @@ BuiltinProc__type_end,
|
||||
BuiltinProc_objc_register_selector,
|
||||
BuiltinProc_objc_register_class,
|
||||
|
||||
BuiltinProc_constant_utf16_cstring,
|
||||
|
||||
|
||||
BuiltinProc_COUNT,
|
||||
};
|
||||
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
@@ -517,4 +520,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
};
|
||||
|
||||
@@ -848,7 +848,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD)
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
@@ -1021,7 +1021,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
|
||||
#endif
|
||||
}
|
||||
|
||||
gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
|
||||
gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
|
||||
|
||||
if (fc.size > I32_MAX) {
|
||||
err = LoadedFile_FileTooLarge;
|
||||
|
||||
@@ -139,6 +139,7 @@ struct PlatformMemoryBlock {
|
||||
};
|
||||
|
||||
|
||||
gb_global std::atomic<isize> global_platform_memory_total_usage;
|
||||
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
|
||||
|
||||
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
|
||||
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
|
||||
|
||||
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
|
||||
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
||||
if (pmblock == nullptr) {
|
||||
gb_printf_err("Out of Virtual memory, oh no...\n");
|
||||
gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
|
||||
gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
|
||||
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
||||
}
|
||||
global_platform_memory_total_usage += total_size;
|
||||
return pmblock;
|
||||
}
|
||||
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
|
||||
global_platform_memory_total_usage -= block->total_size;
|
||||
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
|
||||
}
|
||||
void platform_virtual_memory_protect(void *memory, isize size) {
|
||||
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
|
||||
|
||||
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
|
||||
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
||||
if (pmblock == nullptr) {
|
||||
gb_printf_err("Out of Virtual memory, oh no...\n");
|
||||
gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
|
||||
gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
|
||||
GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
|
||||
}
|
||||
global_platform_memory_total_usage += total_size;
|
||||
return pmblock;
|
||||
}
|
||||
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
|
||||
isize size = block->total_size;
|
||||
global_platform_memory_total_usage -= size;
|
||||
munmap(block, size);
|
||||
}
|
||||
void platform_virtual_memory_protect(void *memory, isize size) {
|
||||
|
||||
@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
|
||||
|
||||
EntityFlag_Test = 1ull<<30,
|
||||
EntityFlag_Init = 1ull<<31,
|
||||
EntityFlag_Subtype = 1ull<<32,
|
||||
|
||||
EntityFlag_CustomLinkName = 1ull<<40,
|
||||
EntityFlag_CustomLinkage_Internal = 1ull<<41,
|
||||
@@ -86,6 +87,10 @@ enum EntityFlag : u64 {
|
||||
EntityFlag_Overridden = 1ull<<63,
|
||||
};
|
||||
|
||||
enum : u64 {
|
||||
EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
|
||||
};
|
||||
|
||||
enum EntityState : u32 {
|
||||
EntityState_Unresolved = 0,
|
||||
EntityState_InProgress = 1,
|
||||
|
||||
@@ -50,9 +50,9 @@ struct ExactValue {
|
||||
union {
|
||||
bool value_bool;
|
||||
String value_string;
|
||||
BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
|
||||
BigInt value_integer;
|
||||
f64 value_float;
|
||||
i64 value_pointer;
|
||||
i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer
|
||||
Complex128 *value_complex;
|
||||
Quaternion256 *value_quaternion;
|
||||
Ast * value_compound;
|
||||
@@ -630,6 +630,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
case ExactValue_Quaternion:
|
||||
case ExactValue_Pointer:
|
||||
case ExactValue_Procedure:
|
||||
case ExactValue_Typeid:
|
||||
return;
|
||||
|
||||
case ExactValue_Integer:
|
||||
@@ -671,9 +674,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Procedure:
|
||||
return;
|
||||
}
|
||||
|
||||
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
|
||||
@@ -932,6 +932,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ExactValue_Pointer: {
|
||||
switch (op) {
|
||||
case Token_CmpEq: return x.value_pointer == y.value_pointer;
|
||||
case Token_NotEq: return x.value_pointer != y.value_pointer;
|
||||
case Token_Lt: return x.value_pointer < y.value_pointer;
|
||||
case Token_LtEq: return x.value_pointer <= y.value_pointer;
|
||||
case Token_Gt: return x.value_pointer > y.value_pointer;
|
||||
case Token_GtEq: return x.value_pointer >= y.value_pointer;
|
||||
}
|
||||
}
|
||||
|
||||
case ExactValue_Typeid:
|
||||
switch (op) {
|
||||
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
|
||||
|
||||
89
src/gb/gb.h
89
src/gb/gb.h
@@ -79,6 +79,10 @@ extern "C" {
|
||||
#ifndef GB_SYSTEM_FREEBSD
|
||||
#define GB_SYSTEM_FREEBSD 1
|
||||
#endif
|
||||
#elif defined(__OpenBSD__)
|
||||
#ifndef GB_SYSTEM_OPENBSD
|
||||
#define GB_SYSTEM_OPENBSD 1
|
||||
#endif
|
||||
#else
|
||||
#error This UNIX operating system is not supported
|
||||
#endif
|
||||
@@ -199,7 +203,7 @@ extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h> // NOTE(bill): malloc on linux
|
||||
#include <sys/mman.h>
|
||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
|
||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
@@ -235,6 +239,12 @@ extern "C" {
|
||||
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_OPENBSD)
|
||||
#include <stdio.h>
|
||||
#include <pthread_np.h>
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
|
||||
isize thread_count;
|
||||
isize threads_per_core;
|
||||
} gbAffinity;
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
typedef struct gbAffinity {
|
||||
b32 is_accurate;
|
||||
isize core_count;
|
||||
isize thread_count;
|
||||
isize threads_per_core;
|
||||
} gbAffinity;
|
||||
#else
|
||||
#error TODO(bill): Unknown system
|
||||
#endif
|
||||
@@ -3678,6 +3695,30 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||
GB_ASSERT(0 <= core && core < a->core_count);
|
||||
return a->threads_per_core;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
#include <unistd.h>
|
||||
|
||||
void gb_affinity_init(gbAffinity *a) {
|
||||
a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
a->threads_per_core = 1;
|
||||
a->is_accurate = a->core_count > 0;
|
||||
a->core_count = a->is_accurate ? a->core_count : 1;
|
||||
a->thread_count = a->core_count;
|
||||
}
|
||||
|
||||
void gb_affinity_destroy(gbAffinity *a) {
|
||||
gb_unused(a);
|
||||
}
|
||||
|
||||
b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||
GB_ASSERT(0 <= core && core < a->core_count);
|
||||
return a->threads_per_core;
|
||||
@@ -6025,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
|
||||
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
|
||||
#else
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
|
||||
isize size;
|
||||
int existing_fd = open(existing_filename, O_RDONLY, 0);
|
||||
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
|
||||
@@ -6041,6 +6082,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
|
||||
close(new_fd);
|
||||
close(existing_fd);
|
||||
|
||||
return size == stat_existing.st_size;
|
||||
#else
|
||||
int new_flags = O_WRONLY | O_CREAT;
|
||||
if (fail_if_exists) {
|
||||
new_flags |= O_EXCL;
|
||||
}
|
||||
int existing_fd = open(existing_filename, O_RDONLY, 0);
|
||||
int new_fd = open(new_filename, new_flags, 0666);
|
||||
|
||||
struct stat stat_existing;
|
||||
if (fstat(existing_fd, &stat_existing) == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
|
||||
char *buf = (char *)malloc(bsize);
|
||||
if (buf == NULL) {
|
||||
close(new_fd);
|
||||
close(existing_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
isize size = 0;
|
||||
ssize_t nread, nwrite, offset;
|
||||
while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
|
||||
for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
|
||||
if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
|
||||
free(buf);
|
||||
close(new_fd);
|
||||
close(existing_fd);
|
||||
return 0;
|
||||
}
|
||||
size += nwrite;
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
close(new_fd);
|
||||
close(existing_fd);
|
||||
|
||||
if (nread == -1) {
|
||||
return 0;
|
||||
}
|
||||
return size == stat_existing.st_size;
|
||||
#endif
|
||||
}
|
||||
@@ -6093,6 +6177,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
|
||||
}
|
||||
|
||||
void gb_file_free_contents(gbFileContents *fc) {
|
||||
if (fc == NULL || fc->size == 0) return;
|
||||
GB_ASSERT_NOT_NULL(fc->data);
|
||||
gb_free(fc->allocator, fc->data);
|
||||
fc->data = NULL;
|
||||
|
||||
@@ -1295,6 +1295,11 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
reloc_mode = LLVMRelocPIC;
|
||||
}
|
||||
|
||||
if (build_context.metrics.os == TargetOs_openbsd) {
|
||||
// Always use PIC for OpenBSD: it defaults to PIE
|
||||
reloc_mode = LLVMRelocPIC;
|
||||
}
|
||||
|
||||
for_array(i, gen->modules.entries) {
|
||||
target_machines[i] = LLVMCreateTargetMachine(
|
||||
target, target_triple, llvm_cpu,
|
||||
|
||||
@@ -962,7 +962,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
|
||||
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
|
||||
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
|
||||
lb_set_llvm_metadata(m, ptr, llvm_expr);
|
||||
LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
|
||||
LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2202,6 +2202,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
|
||||
Type *tl = base_type(a);
|
||||
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
|
||||
lbValue rhs = lb_address_from_load_or_generate_local(p, right);
|
||||
|
||||
|
||||
// TODO(bill): Test to see if this is actually faster!!!!
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
args[0] = lb_emit_conv(p, lhs, t_rawptr);
|
||||
args[1] = lb_emit_conv(p, rhs, t_rawptr);
|
||||
args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
|
||||
lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
|
||||
lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
|
||||
return lb_emit_conv(p, res, t_bool);
|
||||
}
|
||||
if (is_type_array(a) || is_type_enumerated_array(a)) {
|
||||
Type *tl = base_type(a);
|
||||
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
|
||||
|
||||
@@ -57,6 +57,7 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
|
||||
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
|
||||
}
|
||||
|
||||
|
||||
lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
|
||||
GB_ASSERT(entity != nullptr);
|
||||
GB_ASSERT(entity->kind == Entity_Procedure);
|
||||
@@ -163,14 +164,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// lbCallingConventionKind cc_kind = lbCallingConvention_C;
|
||||
// // TODO(bill): Clean up this logic
|
||||
// if (build_context.metrics.os != TargetOs_js) {
|
||||
// cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
|
||||
// }
|
||||
// LLVMSetFunctionCallConv(p->value, cc_kind);
|
||||
lbValue proc_value = {p->value, p->type};
|
||||
lb_add_entity(m, entity, proc_value);
|
||||
lb_add_member(m, p->name, proc_value);
|
||||
@@ -1049,7 +1042,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
return lb_string_len(p, v);
|
||||
} else if (is_type_array(t)) {
|
||||
GB_PANIC("Array lengths are constant");
|
||||
} else if (is_type_slice(t)) {
|
||||
} else if (is_type_slice(t) || is_type_relative_slice(t)) {
|
||||
return lb_slice_len(p, v);
|
||||
} else if (is_type_dynamic_array(t)) {
|
||||
return lb_dynamic_array_len(p, v);
|
||||
@@ -1075,7 +1068,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
GB_PANIC("Unreachable");
|
||||
} else if (is_type_array(t)) {
|
||||
GB_PANIC("Array lengths are constant");
|
||||
} else if (is_type_slice(t)) {
|
||||
} else if (is_type_slice(t) || is_type_relative_slice(t)) {
|
||||
return lb_slice_len(p, v);
|
||||
} else if (is_type_dynamic_array(t)) {
|
||||
return lb_dynamic_array_cap(p, v);
|
||||
@@ -2122,6 +2115,77 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
|
||||
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
|
||||
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
|
||||
|
||||
|
||||
case BuiltinProc_constant_utf16_cstring:
|
||||
{
|
||||
auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) {
|
||||
if (r < 0x10000 || r > 0x10ffff) {
|
||||
*r1 = 0xfffd;
|
||||
*r2 = 0xfffd;
|
||||
} else {
|
||||
r -= 0x10000;
|
||||
*r1 = 0xd800 + ((r>>10)&0x3ff);
|
||||
*r2 = 0xdc00 + (r&0x3ff);
|
||||
}
|
||||
};
|
||||
|
||||
lbModule *m = p->module;
|
||||
|
||||
auto tav = type_and_value_of_expr(ce->args[0]);
|
||||
GB_ASSERT(tav.value.kind == ExactValue_String);
|
||||
String value = tav.value.value_string;
|
||||
|
||||
LLVMTypeRef llvm_u16 = lb_type(m, t_u16);
|
||||
|
||||
isize max_len = value.len*2 + 1;
|
||||
LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len);
|
||||
isize n = 0;
|
||||
while (value.len > 0) {
|
||||
Rune r = 0;
|
||||
isize w = gb_utf8_decode(value.text, value.len, &r);
|
||||
value.text += w;
|
||||
value.len -= w;
|
||||
if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) {
|
||||
buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false);
|
||||
} else if (0x10000 <= r && r <= 0x10ffff) {
|
||||
u16 r1, r2;
|
||||
encode_surrogate_pair(r, &r1, &r2);
|
||||
buffer[n++] = LLVMConstInt(llvm_u16, r1, false);
|
||||
buffer[n++] = LLVMConstInt(llvm_u16, r2, false);
|
||||
} else {
|
||||
buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[n++] = LLVMConstInt(llvm_u16, 0, false);
|
||||
|
||||
LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n);
|
||||
|
||||
char *name = nullptr;
|
||||
{
|
||||
isize max_len = 7+8+1;
|
||||
name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
|
||||
len -= 1;
|
||||
}
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name);
|
||||
LLVMSetInitializer(global_data, array);
|
||||
LLVMSetLinkage(global_data, LLVMInternalLinkage);
|
||||
|
||||
|
||||
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstInt(lb_type(m, t_u32), 0, false),
|
||||
LLVMConstInt(lb_type(m, t_u32), 0, false),
|
||||
};
|
||||
lbValue res = {};
|
||||
res.type = tv.type;
|
||||
res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), "");
|
||||
return res;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
|
||||
|
||||
@@ -1373,7 +1373,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
|
||||
return lb_emit_struct_ev(p, slice, 0);
|
||||
}
|
||||
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
|
||||
GB_ASSERT(is_type_slice(slice.type));
|
||||
GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
|
||||
return lb_emit_struct_ev(p, slice, 1);
|
||||
}
|
||||
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
|
||||
|
||||
100
src/main.cpp
100
src/main.cpp
@@ -384,7 +384,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
||||
// This allows you to specify '-f' in a #foreign_system_library,
|
||||
// without having to implement any new syntax specifically for MacOS.
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
if (string_ends_with(lib, str_lit(".framework"))) {
|
||||
// framework thingie
|
||||
String lib_name = lib;
|
||||
@@ -400,14 +400,14 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
// dynamic or static system lib, just link regularly searching system library paths
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||
}
|
||||
#else
|
||||
} else {
|
||||
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
|
||||
// since those are statically linked to at link time. shared libraries (.so) has to be
|
||||
// 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"))) {
|
||||
// static libs, 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"))) {
|
||||
// static libs and object files, absolute full path relative to the file in which the lib was imported from
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
|
||||
} else if (string_ends_with(lib, str_lit(".so"))) {
|
||||
// dynamic lib, relative path to executable
|
||||
@@ -418,7 +418,7 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
// dynamic or static system lib, just link regularly searching system library paths
|
||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
gbString object_files = gb_string_make(heap_allocator(), "");
|
||||
@@ -456,14 +456,15 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
// line arguments prepared previously are incompatible with ld.
|
||||
//
|
||||
// Shared libraries are .dylib on MacOS and .so on Linux.
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
output_ext = STR_LIT(".dylib");
|
||||
#else
|
||||
} else {
|
||||
output_ext = STR_LIT(".so");
|
||||
#endif
|
||||
}
|
||||
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||
} else {
|
||||
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||
}
|
||||
if (build_context.out_filepath.len > 0) {
|
||||
@@ -474,34 +475,39 @@ i32 linker_stage(lbGenerator *gen) {
|
||||
}
|
||||
}
|
||||
|
||||
result = system_exec_command_line_app("ld-link",
|
||||
"clang -Wno-unused-command-line-argument %s -o \"%.*s%.*s\" %s "
|
||||
" %s "
|
||||
" %.*s "
|
||||
" %.*s "
|
||||
" %s "
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
||||
// make sure to also change the 'mtriple' param passed to 'opt'
|
||||
#if defined(GB_CPU_ARM)
|
||||
" -mmacosx-version-min=12.0.0 "
|
||||
#else
|
||||
" -mmacosx-version-min=10.8.0 "
|
||||
#endif
|
||||
// This points the linker to where the entry point is
|
||||
" -e _main "
|
||||
#endif
|
||||
, object_files, LIT(output_base), LIT(output_ext),
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
"-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
|
||||
#else
|
||||
"-lc -lm",
|
||||
#endif
|
||||
lib_str,
|
||||
LIT(build_context.link_flags),
|
||||
LIT(build_context.extra_linker_flags),
|
||||
link_settings);
|
||||
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
||||
defer (gb_string_free(platform_lib_str));
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
|
||||
} else {
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
|
||||
}
|
||||
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
||||
// make sure to also change the 'mtriple' param passed to 'opt'
|
||||
if (build_context.metrics.arch == TargetArch_arm64) {
|
||||
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
|
||||
} else {
|
||||
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
|
||||
}
|
||||
// This points the linker to where the entry point is
|
||||
link_settings = gb_string_appendc(link_settings, " -e _main ");
|
||||
}
|
||||
|
||||
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
|
||||
defer (gb_string_free(link_command_line));
|
||||
|
||||
link_command_line = gb_string_appendc(link_command_line, object_files);
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s%.*s\" ", LIT(output_base), LIT(output_ext));
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
|
||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
|
||||
|
||||
result = system_exec_command_line_app("ld-link", link_command_line);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
@@ -572,14 +578,16 @@ void usage(String argv0) {
|
||||
print_usage_line(0, "Usage:");
|
||||
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
|
||||
print_usage_line(0, "Commands:");
|
||||
print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
|
||||
print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
|
||||
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
|
||||
print_usage_line(1, "check parse and type check .odin file");
|
||||
print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
|
||||
print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
|
||||
print_usage_line(1, "version print version");
|
||||
print_usage_line(1, "report print information useful to reporting a bug");
|
||||
print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
|
||||
print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
|
||||
print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
|
||||
print_usage_line(1, "check parse, and type check an .odin file, or directory of .odin files");
|
||||
print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
|
||||
print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program");
|
||||
print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
|
||||
print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
|
||||
print_usage_line(1, "version print version");
|
||||
print_usage_line(1, "report print information useful to reporting a bug");
|
||||
print_usage_line(0, "");
|
||||
print_usage_line(0, "For further details on a command, use -help after the command name");
|
||||
print_usage_line(1, "e.g. odin build -help");
|
||||
@@ -2577,7 +2585,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
// NOTE(bill): add 'shared' directory if it is not already set
|
||||
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
|
||||
add_library_collection(str_lit("shared"),
|
||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
131
src/parser.cpp
131
src/parser.cpp
@@ -57,6 +57,9 @@ isize ast_node_size(AstKind kind) {
|
||||
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
|
||||
|
||||
}
|
||||
|
||||
gb_global std::atomic<isize> global_total_node_memory_allocated;
|
||||
|
||||
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
|
||||
Ast *alloc_ast_node(AstFile *f, AstKind kind) {
|
||||
gbAllocator a = ast_allocator(f);
|
||||
@@ -66,6 +69,9 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) {
|
||||
Ast *node = cast(Ast *)gb_alloc(a, size);
|
||||
node->kind = kind;
|
||||
node->file_id = f ? f->id : 0;
|
||||
|
||||
global_total_node_memory_allocated += size;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1532,7 +1538,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
|
||||
Token expect_closing(AstFile *f, TokenKind kind, String context) {
|
||||
if (f->curr_token.kind != kind &&
|
||||
f->curr_token.kind == Token_Semicolon &&
|
||||
f->curr_token.string == "\n") {
|
||||
(f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) {
|
||||
Token tok = f->prev_token;
|
||||
tok.pos.column += cast(i32)tok.string.len;
|
||||
syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context));
|
||||
@@ -1554,6 +1560,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) {
|
||||
switch (curr_token->kind) {
|
||||
case Token_CloseBrace:
|
||||
case Token_CloseParen:
|
||||
case Token_EOF:
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
@@ -3009,64 +3016,62 @@ i32 token_precedence(AstFile *f, TokenKind t) {
|
||||
|
||||
Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
|
||||
Ast *expr = parse_unary_expr(f, lhs);
|
||||
for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) {
|
||||
for (;;) {
|
||||
Token op = f->curr_token;
|
||||
i32 op_prec = token_precedence(f, op.kind);
|
||||
if (op_prec != prec) {
|
||||
// NOTE(bill): This will also catch operators that are not valid "binary" operators
|
||||
break;
|
||||
for (;;) {
|
||||
Token op = f->curr_token;
|
||||
i32 op_prec = token_precedence(f, op.kind);
|
||||
if (op_prec < prec_in) {
|
||||
// NOTE(bill): This will also catch operators that are not valid "binary" operators
|
||||
break;
|
||||
}
|
||||
Token prev = f->prev_token;
|
||||
switch (op.kind) {
|
||||
case Token_if:
|
||||
case Token_when:
|
||||
if (prev.pos.line < op.pos.line) {
|
||||
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
|
||||
goto loop_end;
|
||||
}
|
||||
Token prev = f->prev_token;
|
||||
break;
|
||||
}
|
||||
expect_operator(f); // NOTE(bill): error checks too
|
||||
|
||||
if (op.kind == Token_Question) {
|
||||
Ast *cond = expr;
|
||||
// Token_Question
|
||||
Ast *x = parse_expr(f, lhs);
|
||||
Token token_c = expect_token(f, Token_Colon);
|
||||
Ast *y = parse_expr(f, lhs);
|
||||
expr = ast_ternary_if_expr(f, x, cond, y);
|
||||
} else if (op.kind == Token_if || op.kind == Token_when) {
|
||||
Ast *x = expr;
|
||||
Ast *cond = parse_expr(f, lhs);
|
||||
Token tok_else = expect_token(f, Token_else);
|
||||
Ast *y = parse_expr(f, lhs);
|
||||
|
||||
switch (op.kind) {
|
||||
case Token_if:
|
||||
expr = ast_ternary_if_expr(f, x, cond, y);
|
||||
break;
|
||||
case Token_when:
|
||||
if (prev.pos.line < op.pos.line) {
|
||||
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
|
||||
goto loop_end;
|
||||
}
|
||||
expr = ast_ternary_when_expr(f, x, cond, y);
|
||||
break;
|
||||
}
|
||||
expect_operator(f); // NOTE(bill): error checks too
|
||||
|
||||
if (op.kind == Token_Question) {
|
||||
Ast *cond = expr;
|
||||
// Token_Question
|
||||
Ast *x = parse_expr(f, lhs);
|
||||
Token token_c = expect_token(f, Token_Colon);
|
||||
Ast *y = parse_expr(f, lhs);
|
||||
expr = ast_ternary_if_expr(f, x, cond, y);
|
||||
} else if (op.kind == Token_if || op.kind == Token_when) {
|
||||
Ast *x = expr;
|
||||
Ast *cond = parse_expr(f, lhs);
|
||||
Token tok_else = expect_token(f, Token_else);
|
||||
Ast *y = parse_expr(f, lhs);
|
||||
|
||||
switch (op.kind) {
|
||||
case Token_if:
|
||||
expr = ast_ternary_if_expr(f, x, cond, y);
|
||||
break;
|
||||
case Token_when:
|
||||
expr = ast_ternary_when_expr(f, x, cond, y);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Ast *right = parse_binary_expr(f, false, prec+1);
|
||||
if (right == nullptr) {
|
||||
syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string));
|
||||
}
|
||||
if (op.kind == Token_or_else) {
|
||||
// NOTE(bill): easier to handle its logic different with its own AST kind
|
||||
expr = ast_or_else_expr(f, expr, op, right);
|
||||
} else {
|
||||
expr = ast_binary_expr(f, op, expr, right);
|
||||
}
|
||||
} else {
|
||||
Ast *right = parse_binary_expr(f, false, op_prec+1);
|
||||
if (right == nullptr) {
|
||||
syntax_error(op, "Expected expression on the right-hand side of the binary operator '%.*s'", LIT(op.string));
|
||||
}
|
||||
if (op.kind == Token_or_else) {
|
||||
// NOTE(bill): easier to handle its logic different with its own AST kind
|
||||
expr = ast_or_else_expr(f, expr, op, right);
|
||||
} else {
|
||||
expr = ast_binary_expr(f, op, expr, right);
|
||||
}
|
||||
|
||||
lhs = false;
|
||||
}
|
||||
loop_end:;
|
||||
|
||||
lhs = false;
|
||||
}
|
||||
loop_end:;
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -3510,12 +3515,13 @@ enum FieldPrefixKind : i32 {
|
||||
FieldPrefix_Unknown = -1,
|
||||
FieldPrefix_Invalid = 0,
|
||||
|
||||
FieldPrefix_using,
|
||||
FieldPrefix_using, // implies #subtype
|
||||
FieldPrefix_const,
|
||||
FieldPrefix_no_alias,
|
||||
FieldPrefix_c_vararg,
|
||||
FieldPrefix_auto_cast,
|
||||
FieldPrefix_any_int,
|
||||
FieldPrefix_subtype, // does not imply `using` semantics
|
||||
};
|
||||
|
||||
struct ParseFieldPrefixMapping {
|
||||
@@ -3532,6 +3538,7 @@ gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
|
||||
{str_lit("c_vararg"), Token_Hash, FieldPrefix_c_vararg, FieldFlag_c_vararg},
|
||||
{str_lit("const"), Token_Hash, FieldPrefix_const, FieldFlag_const},
|
||||
{str_lit("any_int"), Token_Hash, FieldPrefix_any_int, FieldFlag_any_int},
|
||||
{str_lit("subtype"), Token_Hash, FieldPrefix_subtype, FieldFlag_subtype},
|
||||
};
|
||||
|
||||
|
||||
@@ -4849,12 +4856,6 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
|
||||
f->prev_token = f->tokens[f->prev_token_index];
|
||||
f->curr_token = f->tokens[f->curr_token_index];
|
||||
|
||||
isize const page_size = 4*1024;
|
||||
isize block_size = 2*f->tokens.count*gb_size_of(Ast);
|
||||
block_size = ((block_size + page_size-1)/page_size) * page_size;
|
||||
block_size = gb_clamp(block_size, page_size, DEFAULT_MINIMUM_BLOCK_SIZE);
|
||||
f->arena.minimum_block_size = block_size;
|
||||
|
||||
array_init(&f->comments, heap_allocator(), 0, 0);
|
||||
array_init(&f->imports, heap_allocator(), 0, 0);
|
||||
|
||||
@@ -5719,6 +5720,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
||||
return ParseFile_WrongExtension;
|
||||
}
|
||||
} else if (init_fullpath.len != 0) {
|
||||
String path = init_fullpath;
|
||||
if (path[path.len-1] == '/') {
|
||||
path.len -= 1;
|
||||
}
|
||||
if ((build_context.command_kind & Command__does_build) &&
|
||||
build_context.build_mode == BuildMode_Executable) {
|
||||
String short_path = filename_from_path(path);
|
||||
char *cpath = alloc_cstring(heap_allocator(), short_path);
|
||||
defer (gb_free(heap_allocator(), cpath));
|
||||
|
||||
if (gb_file_exists(cpath)) {
|
||||
error_line("Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
|
||||
return ParseFile_DirectoryAlreadyExists;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ enum ParseFileError {
|
||||
ParseFile_InvalidToken,
|
||||
ParseFile_GeneralError,
|
||||
ParseFile_FileTooLarge,
|
||||
ParseFile_DirectoryAlreadyExists,
|
||||
|
||||
ParseFile_Count,
|
||||
};
|
||||
@@ -97,8 +98,6 @@ struct AstFile {
|
||||
AstPackage * pkg;
|
||||
Scope * scope;
|
||||
|
||||
Arena arena;
|
||||
|
||||
Ast * pkg_decl;
|
||||
String fullpath;
|
||||
Tokenizer tokenizer;
|
||||
@@ -300,6 +299,7 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_auto_cast = 1<<4,
|
||||
FieldFlag_const = 1<<5,
|
||||
FieldFlag_any_int = 1<<6,
|
||||
FieldFlag_subtype = 1<<7,
|
||||
|
||||
// Internal use by the parser only
|
||||
FieldFlag_Tags = 1<<10,
|
||||
@@ -307,7 +307,7 @@ enum FieldFlag : u32 {
|
||||
|
||||
// Parameter List Restrictions
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast|FieldFlag_const|FieldFlag_any_int,
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags,
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
|
||||
};
|
||||
|
||||
enum StmtAllowFlag {
|
||||
@@ -800,10 +800,10 @@ gb_inline bool is_ast_when_stmt(Ast *node) {
|
||||
return node->kind == Ast_WhenStmt;
|
||||
}
|
||||
|
||||
gb_global gb_thread_local Arena global_ast_arena = {};
|
||||
gb_global gb_thread_local Arena global_thread_local_ast_arena = {};
|
||||
|
||||
gbAllocator ast_allocator(AstFile *f) {
|
||||
Arena *arena = f ? &f->arena : &global_ast_arena;
|
||||
Arena *arena = &global_thread_local_ast_arena;
|
||||
return arena_allocator(arena);
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ void thread_set_name(Thread *t, char const *name) {
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
// TODO(bill): Test if this works
|
||||
pthread_setname_np(name);
|
||||
#elif defined(GB_SYSTEM_FREEBSD)
|
||||
#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
pthread_set_name_np(t->posix_handle, name);
|
||||
#else
|
||||
// TODO(bill): Test if this works
|
||||
|
||||
@@ -703,7 +703,7 @@ struct TypePath;
|
||||
i64 type_size_of (Type *t);
|
||||
i64 type_align_of (Type *t);
|
||||
i64 type_offset_of (Type *t, i32 index);
|
||||
gbString type_to_string (Type *type);
|
||||
gbString type_to_string (Type *type, bool shorthand=false);
|
||||
i64 type_size_of_internal(Type *t, TypePath *path);
|
||||
void init_map_internal_types(Type *type);
|
||||
Type * bit_set_to_int(Type *t);
|
||||
@@ -2334,7 +2334,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
|
||||
GB_ASSERT(is_type_struct(src) || is_type_union(src));
|
||||
for_array(i, src->Struct.fields) {
|
||||
Entity *f = src->Struct.fields[i];
|
||||
if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) {
|
||||
if (f->kind == Entity_Variable && f->flags & EntityFlags_IsSubtype) {
|
||||
if (are_types_identical(dst, f->type)) {
|
||||
return f->token.string;
|
||||
}
|
||||
@@ -2343,7 +2343,7 @@ String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
|
||||
return f->token.string;
|
||||
}
|
||||
}
|
||||
if (is_type_struct(f->type)) {
|
||||
if ((f->flags & EntityFlag_Using) != 0 && is_type_struct(f->type)) {
|
||||
String name = lookup_subtype_polymorphic_field(dst, f->type);
|
||||
if (name.len > 0) {
|
||||
return name;
|
||||
@@ -2489,9 +2489,9 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
|
||||
if (xf->token.string != yf->token.string) {
|
||||
return false;
|
||||
}
|
||||
bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
|
||||
bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
|
||||
if (xf_is_using ^ yf_is_using) {
|
||||
u64 xf_flags = (xf->flags&EntityFlags_IsSubtype);
|
||||
u64 yf_flags = (yf->flags&EntityFlags_IsSubtype);
|
||||
if (xf_flags != yf_flags) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2623,6 +2623,17 @@ i64 union_tag_size(Type *u) {
|
||||
|
||||
// TODO(bill): Is this an okay approach?
|
||||
i64 max_align = 1;
|
||||
|
||||
if (u->Union.variants.count < 1ull<<8) {
|
||||
max_align = 1;
|
||||
} else if (u->Union.variants.count < 1ull<<16) {
|
||||
max_align = 2;
|
||||
} else if (u->Union.variants.count < 1ull<<32) {
|
||||
max_align = 4;
|
||||
} else {
|
||||
GB_PANIC("how many variants do you have?!");
|
||||
}
|
||||
|
||||
for_array(i, u->Union.variants) {
|
||||
Type *variant_type = u->Union.variants[i];
|
||||
i64 align = type_align_of(variant_type);
|
||||
@@ -3813,7 +3824,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0
|
||||
|
||||
for_array(i, src->Struct.fields) {
|
||||
Entity *f = src->Struct.fields[i];
|
||||
if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
|
||||
if (f->kind != Entity_Variable || (f->flags&EntityFlags_IsSubtype) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -3925,7 +3936,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type
|
||||
|
||||
|
||||
|
||||
gbString write_type_to_string(gbString str, Type *type) {
|
||||
gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
|
||||
if (type == nullptr) {
|
||||
return gb_string_appendc(str, "<no type>");
|
||||
}
|
||||
@@ -4040,15 +4051,21 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
|
||||
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
|
||||
str = gb_string_appendc(str, " {");
|
||||
for_array(i, type->Struct.fields) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable);
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, ", ");
|
||||
|
||||
|
||||
if (shorthand && type->Struct.fields.count > 16) {
|
||||
str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
|
||||
} else {
|
||||
for_array(i, type->Struct.fields) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable);
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, ", ");
|
||||
}
|
||||
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
|
||||
str = gb_string_appendc(str, ": ");
|
||||
str = write_type_to_string(str, f->type);
|
||||
}
|
||||
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
|
||||
str = gb_string_appendc(str, ": ");
|
||||
str = write_type_to_string(str, f->type);
|
||||
}
|
||||
str = gb_string_append_rune(str, '}');
|
||||
} break;
|
||||
@@ -4223,13 +4240,16 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
}
|
||||
|
||||
|
||||
gbString type_to_string(Type *type, gbAllocator allocator) {
|
||||
return write_type_to_string(gb_string_make(allocator, ""), type);
|
||||
gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) {
|
||||
return write_type_to_string(gb_string_make(allocator, ""), type, shorthand);
|
||||
}
|
||||
gbString type_to_string(Type *type) {
|
||||
return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
|
||||
gbString type_to_string(Type *type, bool shorthand) {
|
||||
return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
|
||||
}
|
||||
|
||||
gbString type_to_string_shorthand(Type *type) {
|
||||
return type_to_string(type, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user