mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-01 04:10:07 +00:00
Merge branch 'master' into bill/raddebugger-custom-section
This commit is contained in:
+7
-1
@@ -667,8 +667,14 @@ gb_internal void print_bug_report_help() {
|
||||
gb_printf("-nightly");
|
||||
#endif
|
||||
|
||||
String version = {};
|
||||
|
||||
#ifdef GIT_SHA
|
||||
gb_printf(":%s", GIT_SHA);
|
||||
version.text = cast(u8 *)GIT_SHA;
|
||||
version.len = gb_strlen(GIT_SHA);
|
||||
if (version != "") {
|
||||
gb_printf(":%.*s", LIT(version));
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_printf("\n");
|
||||
|
||||
+27
-9
@@ -459,6 +459,7 @@ struct BuildContext {
|
||||
bool ignore_unknown_attributes;
|
||||
bool no_bounds_check;
|
||||
bool no_type_assert;
|
||||
bool dynamic_literals; // Opt-in to `#+feature dynamic-literals` project-wide.
|
||||
bool no_output_files;
|
||||
bool no_crt;
|
||||
bool no_rpath;
|
||||
@@ -1915,12 +1916,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
}
|
||||
|
||||
|
||||
// TODO: Static map calls are bugged on `amd64sysv` abi.
|
||||
if (bc->metrics.os != TargetOs_windows && bc->metrics.arch == TargetArch_amd64) {
|
||||
// ENFORCE DYNAMIC MAP CALLS
|
||||
bc->dynamic_map_calls = true;
|
||||
}
|
||||
|
||||
bc->ODIN_VALGRIND_SUPPORT = false;
|
||||
if (build_context.metrics.os != TargetOs_windows) {
|
||||
switch (bc->metrics.arch) {
|
||||
@@ -2214,11 +2209,34 @@ gb_internal bool init_build_paths(String init_filename) {
|
||||
while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) {
|
||||
output_name.len -= 1;
|
||||
}
|
||||
// Only trim the extension if it's an Odin source file.
|
||||
// This lets people build folders with extensions or files beginning with dots.
|
||||
if (path_extension(output_name) == ".odin" && !path_is_directory(output_name)) {
|
||||
output_name = remove_extension_from_path(output_name);
|
||||
}
|
||||
output_name = remove_directory_from_path(output_name);
|
||||
output_name = remove_extension_from_path(output_name);
|
||||
output_name = copy_string(ha, string_trim_whitespace(output_name));
|
||||
output_path = path_from_string(ha, output_name);
|
||||
|
||||
// This is `path_from_string` without the extension trimming.
|
||||
Path res = {};
|
||||
if (output_name.len > 0) {
|
||||
String fullpath = path_to_full_path(ha, output_name);
|
||||
defer (gb_free(ha, fullpath.text));
|
||||
|
||||
res.basename = directory_from_path(fullpath);
|
||||
res.basename = copy_string(ha, res.basename);
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||
res.basename.len--;
|
||||
}
|
||||
} else {
|
||||
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||
res.name = substring(fullpath, name_start, fullpath.len);
|
||||
res.name = copy_string(ha, res.name);
|
||||
}
|
||||
}
|
||||
output_path = res;
|
||||
|
||||
// Note(Dragos): This is a fix for empty filenames
|
||||
// Turn the trailing folder into the file name
|
||||
if (output_path.name.len == 0) {
|
||||
|
||||
+326
-3
@@ -223,9 +223,9 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
|
||||
data.kind = kind;
|
||||
data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
|
||||
|
||||
mutex_lock(&c->info->objc_types_mutex);
|
||||
mutex_lock(&c->info->objc_objc_msgSend_mutex);
|
||||
map_set(&c->info->objc_msgSend_types, call, data);
|
||||
mutex_unlock(&c->info->objc_types_mutex);
|
||||
mutex_unlock(&c->info->objc_objc_msgSend_mutex);
|
||||
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend");
|
||||
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
|
||||
@@ -387,6 +387,59 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
|
||||
try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
|
||||
return true;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_objc_ivar_get:
|
||||
{
|
||||
Type *self_type = nullptr;
|
||||
|
||||
Operand self = {};
|
||||
check_expr_or_type(c, &self, ce->args[0]);
|
||||
|
||||
if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
|
||||
gbString e = expr_to_string(self.expr);
|
||||
gbString t = type_to_string(self.type);
|
||||
error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
} else if (!is_type_pointer(self.type)) {
|
||||
gbString e = expr_to_string(self.expr);
|
||||
gbString t = type_to_string(self.type);
|
||||
error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
|
||||
gb_string_free(t);
|
||||
gb_string_free(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
self_type = type_deref(self.type);
|
||||
|
||||
if (!(self_type->kind == Type_Named &&
|
||||
self_type->Named.type_name != nullptr &&
|
||||
self_type->Named.type_name->TypeName.objc_class_name != "")) {
|
||||
gbString t = type_to_string(self_type);
|
||||
error(self.expr, "'%.*s' expected a named type with the attribute @(objc_class=<string>) , got type %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
|
||||
if (ivar_type == nullptr) {
|
||||
gbString t = type_to_string(self_type);
|
||||
error(self.expr, "'%.*s' requires that type %s have the attribute @(objc_ivar=<ivar_type_name>).", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) {
|
||||
operand->type = type_hint;
|
||||
} else {
|
||||
operand->type = alloc_type_pointer(ivar_type);
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
return true;
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2167,7 +2220,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case BuiltinProc_objc_find_selector:
|
||||
case BuiltinProc_objc_find_class:
|
||||
case BuiltinProc_objc_register_selector:
|
||||
case BuiltinProc_objc_register_class:
|
||||
case BuiltinProc_objc_register_class:
|
||||
case BuiltinProc_objc_ivar_get:
|
||||
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
|
||||
|
||||
case BuiltinProc___entry_point:
|
||||
@@ -3189,6 +3243,194 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_compress_values: {
|
||||
Operand *ops = gb_alloc_array(temporary_allocator(), Operand, ce->args.count);
|
||||
|
||||
isize value_count = 0;
|
||||
|
||||
for_array(i, ce->args) {
|
||||
Ast *arg = ce->args[i];
|
||||
Operand *op = &ops[i];
|
||||
check_multi_expr(c, op, arg);
|
||||
if (op->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->type == nullptr || op->type == t_invalid) {
|
||||
gbString s = expr_to_string(op->expr);
|
||||
error(op->expr, "Invalid expression to '%.*s', got %s", LIT(builtin_name), s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
if (is_type_tuple(op->type)) {
|
||||
value_count += op->type->Tuple.variables.count;
|
||||
} else {
|
||||
value_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
GB_ASSERT(value_count >= 1);
|
||||
|
||||
if (value_count == 1) {
|
||||
*operand = ops[0];
|
||||
break;
|
||||
}
|
||||
|
||||
if (type_hint != nullptr) {
|
||||
Type *th = base_type(type_hint);
|
||||
if (th->kind == Type_Struct) {
|
||||
if (value_count == th->Struct.fields.count) {
|
||||
isize index = 0;
|
||||
for_array(i, ce->args) {
|
||||
Operand *op = &ops[i];
|
||||
if (is_type_tuple(op->type)) {
|
||||
for (Entity *v : op->type->Tuple.variables) {
|
||||
Operand x = {};
|
||||
x.mode = Addressing_Value;
|
||||
x.type = v->type;
|
||||
check_assignment(c, &x, th->Struct.fields[index++]->type, builtin_name);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
check_assignment(c, op, th->Struct.fields[index++]->type, builtin_name);
|
||||
if (op->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = type_hint;
|
||||
operand->mode = Addressing_Value;
|
||||
break;
|
||||
}
|
||||
} else if (is_type_array_like(th)) {
|
||||
if (cast(i64)value_count == get_array_type_count(th)) {
|
||||
Type *elem = base_array_type(th);
|
||||
for_array(i, ce->args) {
|
||||
Operand *op = &ops[i];
|
||||
if (is_type_tuple(op->type)) {
|
||||
for (Entity *v : op->type->Tuple.variables) {
|
||||
Operand x = {};
|
||||
x.mode = Addressing_Value;
|
||||
x.type = v->type;
|
||||
check_assignment(c, &x, elem, builtin_name);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
check_assignment(c, op, elem, builtin_name);
|
||||
if (op->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = type_hint;
|
||||
operand->mode = Addressing_Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool all_types_the_same = true;
|
||||
Type *last_type = nullptr;
|
||||
for_array(i, ce->args) {
|
||||
Operand *op = &ops[i];
|
||||
if (is_type_tuple(op->type)) {
|
||||
if (last_type == nullptr) {
|
||||
op->type->Tuple.variables[0]->type;
|
||||
}
|
||||
for (Entity *v : op->type->Tuple.variables) {
|
||||
if (!are_types_identical(last_type, v->type)) {
|
||||
all_types_the_same = false;
|
||||
break;
|
||||
}
|
||||
last_type = v->type;
|
||||
}
|
||||
} else {
|
||||
if (last_type == nullptr) {
|
||||
last_type = op->type;
|
||||
} else {
|
||||
if (!are_types_identical(last_type, op->type)) {
|
||||
all_types_the_same = false;
|
||||
break;
|
||||
}
|
||||
last_type = op->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_types_the_same) {
|
||||
Type *elem_type = default_type(last_type);
|
||||
if (is_type_untyped(elem_type)) {
|
||||
gbString s = expr_to_string(call);
|
||||
error(call, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
|
||||
gb_string_free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->type = alloc_type_array(elem_type, value_count);
|
||||
operand->mode = Addressing_Value;
|
||||
} else {
|
||||
Type *st = alloc_type_struct_complete();
|
||||
st->Struct.fields = slice_make<Entity *>(permanent_allocator(), value_count);
|
||||
st->Struct.tags = gb_alloc_array(permanent_allocator(), String, value_count);
|
||||
st->Struct.offsets = gb_alloc_array(permanent_allocator(), i64, value_count);
|
||||
|
||||
Scope *scope = create_scope(c->info, nullptr);
|
||||
|
||||
Token token = {};
|
||||
token.kind = Token_Ident;
|
||||
token.pos = ast_token(call).pos;
|
||||
|
||||
isize index = 0;
|
||||
for_array(i, ce->args) {
|
||||
Operand *op = &ops[i];
|
||||
if (is_type_tuple(op->type)) {
|
||||
for (Entity *v : op->type->Tuple.variables) {
|
||||
Type *t = default_type(v->type);
|
||||
if (is_type_untyped(t)) {
|
||||
gbString s = expr_to_string(op->expr);
|
||||
error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
|
||||
gb_string_free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
gbString s = gb_string_make_reserve(permanent_allocator(), 32);
|
||||
s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
|
||||
token.string = make_string_c(s);
|
||||
Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
|
||||
st->Struct.fields[index++] = e;
|
||||
}
|
||||
} else {
|
||||
Type *t = default_type(op->type);
|
||||
if (is_type_untyped(t)) {
|
||||
gbString s = expr_to_string(op->expr);
|
||||
error(op->expr, "Invalid use of '%s' in '%.*s'", s, LIT(builtin_name));
|
||||
gb_string_free(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
gbString s = gb_string_make_reserve(permanent_allocator(), 32);
|
||||
s = gb_string_append_fmt(s, "v%lld", cast(long long)index);
|
||||
token.string = make_string_c(s);
|
||||
Entity *e = alloc_entity_field(scope, token, t, false, cast(i32)index, EntityState_Resolved);
|
||||
st->Struct.fields[index++] = e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_unused(type_size_of(st));
|
||||
|
||||
operand->type = st;
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case BuiltinProc_min: {
|
||||
// min :: proc($T: typeid) -> ordered
|
||||
// min :: proc(a: ..ordered) -> ordered
|
||||
@@ -5635,6 +5877,87 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
operand->mode = Addressing_Type;
|
||||
break;
|
||||
case BuiltinProc_type_integer_to_unsigned:
|
||||
if (operand->mode != Addressing_Type) {
|
||||
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_type_polymorphic(operand->type)) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
Type *bt = base_type(operand->type);
|
||||
|
||||
if (bt->kind != Type_Basic ||
|
||||
(bt->Basic.flags & BasicFlag_Unsigned) != 0 ||
|
||||
(bt->Basic.flags & BasicFlag_Integer) == 0) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Expected a signed integer type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *u_type = &basic_types[bt->Basic.kind + 1];
|
||||
|
||||
operand->type = u_type;
|
||||
}
|
||||
break;
|
||||
case BuiltinProc_type_integer_to_signed:
|
||||
if (operand->mode != Addressing_Type) {
|
||||
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_type_polymorphic(operand->type)) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
Type *bt = base_type(operand->type);
|
||||
|
||||
if (bt->kind != Type_Basic ||
|
||||
(bt->Basic.flags & BasicFlag_Unsigned) == 0 ||
|
||||
(bt->Basic.flags & BasicFlag_Integer) == 0) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Expected an unsigned integer type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((bt->Basic.flags & BasicFlag_Untyped) != 0) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Expected a non-untyped integer type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bt->Basic.kind == Basic_uintptr) {
|
||||
gbString t = type_to_string(operand->type);
|
||||
error(operand->expr, "Type %s does not have a signed integer mapping for '%.*s'", t, LIT(builtin_name));
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *u_type = &basic_types[bt->Basic.kind - 1];
|
||||
|
||||
operand->type = u_type;
|
||||
}
|
||||
break;
|
||||
case BuiltinProc_type_merge:
|
||||
{
|
||||
operand->mode = Addressing_Type;
|
||||
|
||||
+177
-36
@@ -524,12 +524,90 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
|
||||
if (decl != nullptr) {
|
||||
AttributeContext ac = {};
|
||||
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
|
||||
|
||||
if (e->kind == Entity_TypeName && ac.objc_class != "") {
|
||||
|
||||
e->TypeName.objc_class_name = ac.objc_class;
|
||||
|
||||
if (ac.objc_is_implementation) {
|
||||
e->TypeName.objc_is_implementation = ac.objc_is_implementation;
|
||||
e->TypeName.objc_superclass = ac.objc_superclass;
|
||||
e->TypeName.objc_ivar = ac.objc_ivar;
|
||||
e->TypeName.objc_context_provider = ac.objc_context_provider;
|
||||
|
||||
mutex_lock(&ctx->info->objc_class_name_mutex);
|
||||
bool class_exists = string_set_update(&ctx->info->obcj_class_name_set, ac.objc_class);
|
||||
mutex_unlock(&ctx->info->objc_class_name_mutex);
|
||||
if (class_exists) {
|
||||
error(e->token, "@(objc_class) name '%.*s' has already been used elsewhere", LIT(ac.objc_class));
|
||||
}
|
||||
|
||||
mpsc_enqueue(&ctx->info->objc_class_implementations, e);
|
||||
|
||||
GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named);
|
||||
|
||||
// Enqueue the contex_provider proc to be checked after it is resolved
|
||||
if (e->TypeName.objc_context_provider != nullptr) {
|
||||
mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e);
|
||||
}
|
||||
|
||||
// TODO(harold): I think there's a Check elsewhere in the checker for checking cycles.
|
||||
// See about moving this to the right location.
|
||||
// Ensure superclass hierarchy are all Objective-C classes and does not cycle
|
||||
|
||||
// NOTE(harold): We check for superclass unconditionally (before checking if super is null)
|
||||
// because this should be the case 99.99% of the time. Not subclassing something that
|
||||
// is, or is the child of, NSObject means the objc runtime messaging will not properly work on this type.
|
||||
TypeSet super_set{};
|
||||
type_set_init(&super_set, 8);
|
||||
defer (type_set_destroy(&super_set));
|
||||
|
||||
type_set_update(&super_set, e->type);
|
||||
|
||||
Type *super = ac.objc_superclass;
|
||||
while (super != nullptr) {
|
||||
if (type_set_update(&super_set, super)) {
|
||||
error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
|
||||
break;
|
||||
}
|
||||
|
||||
check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
|
||||
|
||||
if (super->kind != Type_Named) {
|
||||
error(e->token, "@(objc_superclass) Referenced type must be a named struct");
|
||||
break;
|
||||
}
|
||||
|
||||
Type* named_type = base_named_type(super);
|
||||
GB_ASSERT(named_type->kind == Type_Named);
|
||||
|
||||
if (!is_type_objc_object(named_type)) {
|
||||
error(e->token, "@(objc_superclass) Superclass '%.*s' must be an Objective-C class", LIT(named_type->Named.name));
|
||||
break;
|
||||
}
|
||||
|
||||
if (named_type->Named.type_name->TypeName.objc_class_name == "") {
|
||||
error(e->token, "@(objc_superclass) Superclass '%.*s' must have a valid @(objc_class) attribute", LIT(named_type->Named.name));
|
||||
break;
|
||||
}
|
||||
|
||||
super = named_type->Named.type_name->TypeName.objc_superclass;
|
||||
}
|
||||
} else {
|
||||
if (ac.objc_superclass != nullptr) {
|
||||
error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied");
|
||||
} else if (ac.objc_ivar != nullptr) {
|
||||
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
|
||||
} else if (ac.objc_context_provider != nullptr) {
|
||||
error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
|
||||
}
|
||||
}
|
||||
|
||||
if (type_size_of(e->type) > 0) {
|
||||
error(e->token, "@(objc_class) marked type must be of zero size");
|
||||
}
|
||||
} else if (ac.objc_is_implementation) {
|
||||
error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -922,7 +1000,7 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
|
||||
}
|
||||
|
||||
|
||||
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
|
||||
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext &ac) {
|
||||
if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
|
||||
return;
|
||||
}
|
||||
@@ -934,48 +1012,107 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
|
||||
error(e->token, "@(objc_name) is required with @(objc_type)");
|
||||
} else {
|
||||
Type *t = ac.objc_type;
|
||||
if (t->kind == Type_Named) {
|
||||
Entity *tn = t->Named.type_name;
|
||||
|
||||
GB_ASSERT(tn->kind == Entity_TypeName);
|
||||
GB_ASSERT(t->kind == Type_Named); // NOTE(harold): This is already checked for at the attribute resolution stage.
|
||||
Entity *tn = t->Named.type_name;
|
||||
|
||||
if (tn->scope != e->scope) {
|
||||
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
|
||||
} else {
|
||||
mutex_lock(&global_type_name_objc_metadata_mutex);
|
||||
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
|
||||
GB_ASSERT(tn->kind == Entity_TypeName);
|
||||
|
||||
if (!tn->TypeName.objc_metadata) {
|
||||
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
|
||||
}
|
||||
auto *md = tn->TypeName.objc_metadata;
|
||||
mutex_lock(md->mutex);
|
||||
defer (mutex_unlock(md->mutex));
|
||||
if (tn->scope != e->scope) {
|
||||
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
|
||||
} else {
|
||||
|
||||
if (!ac.objc_is_class_method) {
|
||||
bool ok = true;
|
||||
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
|
||||
if (entry.name == ac.objc_name) {
|
||||
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
||||
}
|
||||
// Enable implementation by default if the class is an implementer too and
|
||||
// @objc_implement was not set to false explicitly in this proc.
|
||||
bool implement = tn->TypeName.objc_is_implementation;
|
||||
if (ac.objc_is_disabled_implement) {
|
||||
implement = false;
|
||||
}
|
||||
|
||||
if (implement) {
|
||||
GB_ASSERT(e->kind == Entity_Procedure);
|
||||
|
||||
auto &proc = e->type->Proc;
|
||||
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
|
||||
|
||||
if (!tn->TypeName.objc_is_implementation) {
|
||||
error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
|
||||
} else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
|
||||
error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
|
||||
} else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
|
||||
error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
|
||||
} else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
|
||||
error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
|
||||
} else if (proc.result_count > 1) {
|
||||
error(e->token, "Objective-C method implementations may return at most 1 value");
|
||||
} else {
|
||||
bool ok = true;
|
||||
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
|
||||
if (entry.name == ac.objc_name) {
|
||||
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
// Always export unconditionally
|
||||
// NOTE(harold): This means check_objc_methods() MUST be called before
|
||||
// e->Procedure.is_export is set in check_proc_decl()!
|
||||
if (ac.is_export) {
|
||||
error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
|
||||
}
|
||||
if (ok) {
|
||||
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
||||
if (ac.link_name != "") {
|
||||
error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
|
||||
}
|
||||
|
||||
ac.is_export = true;
|
||||
ac.linkage = STR_LIT("strong");
|
||||
|
||||
auto method = ObjcMethodData{ ac, e };
|
||||
method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
|
||||
|
||||
CheckerInfo *info = ctx->info;
|
||||
mutex_lock(&info->objc_method_mutex);
|
||||
defer (mutex_unlock(&info->objc_method_mutex));
|
||||
|
||||
Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
|
||||
if (method_list) {
|
||||
array_add(method_list, method);
|
||||
} else {
|
||||
auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
|
||||
list[0] = method;
|
||||
|
||||
map_set(&info->objc_method_implementations, t, list);
|
||||
}
|
||||
}
|
||||
} else if (ac.objc_selector != "") {
|
||||
error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations.");
|
||||
}
|
||||
|
||||
mutex_lock(&global_type_name_objc_metadata_mutex);
|
||||
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
|
||||
|
||||
if (!tn->TypeName.objc_metadata) {
|
||||
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
|
||||
}
|
||||
auto *md = tn->TypeName.objc_metadata;
|
||||
mutex_lock(md->mutex);
|
||||
defer (mutex_unlock(md->mutex));
|
||||
|
||||
if (!ac.objc_is_class_method) {
|
||||
bool ok = true;
|
||||
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
|
||||
if (entry.name == ac.objc_name) {
|
||||
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
||||
}
|
||||
} else {
|
||||
bool ok = true;
|
||||
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
|
||||
if (entry.name == ac.objc_name) {
|
||||
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1145,6 +1282,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(harold): For Objective-C method implementations, this must happen after
|
||||
// check_objc_methods() is called as it re-sets ac.is_export to true unconditionally.
|
||||
// The same is true for the linkage, set below.
|
||||
e->Procedure.entry_point_only = ac.entry_point_only;
|
||||
e->Procedure.is_export = ac.is_export;
|
||||
|
||||
@@ -1245,6 +1385,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(harold): See export/linkage note above(where is_export is assigned) regarding Objective-C method implementations
|
||||
bool is_foreign = e->Procedure.is_foreign;
|
||||
bool is_export = e->Procedure.is_export;
|
||||
|
||||
|
||||
+42
-16
@@ -643,7 +643,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
|
||||
gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
|
||||
if (operand->expr == nullptr) return false;
|
||||
Entity *base_entity = entity_of_node(operand->expr);
|
||||
Entity *base_entity = entity_from_expr(operand->expr);
|
||||
if (base_entity == nullptr) return false;
|
||||
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
|
||||
}
|
||||
@@ -995,14 +995,34 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
|
||||
|
||||
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
|
||||
i64 score = 0;
|
||||
i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
|
||||
bool ok = distance >= 0;
|
||||
if (ok) {
|
||||
score = assign_score_function(distance, is_variadic);
|
||||
if (c == nullptr) {
|
||||
GB_ASSERT(operand->mode == Addressing_Value);
|
||||
GB_ASSERT(is_type_typed(operand->type));
|
||||
}
|
||||
if (score_) *score_ = score;
|
||||
return ok;
|
||||
if (operand->mode == Addressing_Invalid || type == t_invalid) {
|
||||
if (score_) *score_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle polymorphic procedure used as default parameter
|
||||
if (operand->mode == Addressing_Value && is_type_proc(type) && is_type_proc(operand->type)) {
|
||||
Entity *e = entity_from_expr(operand->expr);
|
||||
if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type) && !is_type_polymorphic(type)) {
|
||||
// Special case: Allow a polymorphic procedure to be used as default value for concrete proc type
|
||||
// during the initial check. It will be properly instantiated when actually used.
|
||||
if (score_) *score_ = assign_score_function(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
i64 score = check_distance_between_types(c, operand, type, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
if (score_) *score_ = assign_score_function(score, is_variadic);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (score_) *score_ = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5441,8 +5461,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
}
|
||||
}
|
||||
|
||||
if (operand->type && is_type_simd_vector(type_deref(operand->type))) {
|
||||
String field_name = selector->Ident.token.string;
|
||||
if (field_name.len == 1) {
|
||||
error(op_expr, "Extracting an element from a #simd array using .%.*s syntax is disallowed, prefer `simd.extract`", LIT(field_name));
|
||||
} else {
|
||||
error(op_expr, "Extracting elements from a #simd array using .%.*s syntax is disallowed, prefer `swizzle`", LIT(field_name));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr &&
|
||||
(is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) {
|
||||
(is_type_array(type_deref(operand->type)))) {
|
||||
String field_name = selector->Ident.token.string;
|
||||
if (1 < field_name.len && field_name.len <= 4) {
|
||||
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
|
||||
@@ -5497,7 +5527,7 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
|
||||
Type *original_type = operand->type;
|
||||
Type *array_type = base_type(type_deref(original_type));
|
||||
GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
|
||||
GB_ASSERT(array_type->kind == Type_Array);
|
||||
|
||||
i64 array_count = get_array_type_count(array_type);
|
||||
|
||||
@@ -5538,10 +5568,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
break;
|
||||
}
|
||||
|
||||
if (array_type->kind == Type_SimdVector) {
|
||||
operand->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
|
||||
add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
|
||||
return swizzle_entity;
|
||||
@@ -9413,7 +9439,7 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
|
||||
}
|
||||
|
||||
gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
|
||||
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
|
||||
if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0 && !build_context.dynamic_literals) {
|
||||
ERROR_BLOCK();
|
||||
error(node, "Compound literals of dynamic types are disabled by default");
|
||||
error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
|
||||
@@ -10996,7 +11022,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
return kind;
|
||||
case_end;
|
||||
|
||||
case_ast_node(i, Implicit, node)
|
||||
case_ast_node(i, Implicit, node);
|
||||
switch (i->kind) {
|
||||
case Token_context:
|
||||
{
|
||||
|
||||
@@ -2108,10 +2108,12 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
|
||||
if (init_type == nullptr) {
|
||||
init_type = t_invalid;
|
||||
} else if (is_type_polymorphic(base_type(init_type))) {
|
||||
/* DISABLED: This error seems too aggressive for instantiated generic types.
|
||||
gbString str = type_to_string(init_type);
|
||||
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
*/
|
||||
}
|
||||
if (init_type == t_invalid && entity_count == 1 && (mod_flags & (Stmt_BreakAllowed|Stmt_FallthroughAllowed))) {
|
||||
Entity *e = entities[0];
|
||||
|
||||
+10
-1
@@ -1910,9 +1910,18 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
case ParameterValue_Location:
|
||||
case ParameterValue_Expression:
|
||||
case ParameterValue_Value:
|
||||
// Special case for polymorphic procedures as default values
|
||||
if (param_value.ast_value != nullptr) {
|
||||
Entity *e = entity_from_expr(param_value.ast_value);
|
||||
if (e != nullptr && e->kind == Entity_Procedure && is_type_polymorphic(e->type)) {
|
||||
// Allow polymorphic procedures as default parameter values
|
||||
// The type will be correctly determined at call site
|
||||
break;
|
||||
}
|
||||
}
|
||||
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);
|
||||
gb_string_free(str);
|
||||
gb_string_free(str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+135
-2
@@ -728,12 +728,17 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
|
||||
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) {
|
||||
} 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->flags & EntityFlag_Init) {
|
||||
is_unused = false;
|
||||
} else if (e->flags & EntityFlag_Fini) {
|
||||
is_unused = false;
|
||||
} else if (e->Procedure.is_export) {
|
||||
is_unused = false;
|
||||
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
|
||||
is_unused = false;
|
||||
} else {
|
||||
@@ -1351,10 +1356,12 @@ gb_internal void init_universal(void) {
|
||||
t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
|
||||
t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
|
||||
t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
|
||||
t_objc_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete());
|
||||
|
||||
t_objc_id = alloc_type_pointer(t_objc_object);
|
||||
t_objc_SEL = alloc_type_pointer(t_objc_selector);
|
||||
t_objc_Class = alloc_type_pointer(t_objc_class);
|
||||
t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1387,6 +1394,10 @@ gb_internal void init_checker_info(CheckerInfo *i) {
|
||||
array_init(&i->defineables, a);
|
||||
|
||||
map_init(&i->objc_msgSend_types);
|
||||
mpsc_init(&i->objc_class_implementations, a);
|
||||
string_set_init(&i->obcj_class_name_set, 0);
|
||||
map_init(&i->objc_method_implementations);
|
||||
|
||||
string_map_init(&i->load_file_cache);
|
||||
array_init(&i->all_procedures, heap_allocator());
|
||||
|
||||
@@ -1497,6 +1508,8 @@ gb_internal void init_checker(Checker *c) {
|
||||
|
||||
TIME_SECTION("init proc queues");
|
||||
mpsc_init(&c->procs_with_deferred_to_check, a); //, 1<<10);
|
||||
mpsc_init(&c->procs_with_objc_context_provider_to_check, a);
|
||||
|
||||
|
||||
// NOTE(bill): 1 Mi elements should be enough on average
|
||||
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
|
||||
@@ -3662,6 +3675,33 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else if (name == "objc_implement") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_Bool) {
|
||||
ac->objc_is_implementation = ev.value_bool;
|
||||
|
||||
if (!ac->objc_is_implementation) {
|
||||
ac->objc_is_disabled_implement = true;
|
||||
}
|
||||
} else if (ev.kind == ExactValue_Invalid) {
|
||||
ac->objc_is_implementation = true;
|
||||
} else {
|
||||
error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (name == "objc_selector") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
if (string_is_valid_identifier(ev.value_string)) {
|
||||
ac->objc_selector = ev.value_string;
|
||||
} else {
|
||||
error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
} else if (name == "require_target_feature") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
@@ -3907,6 +3947,51 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
|
||||
ac->objc_class = ev.value_string;
|
||||
}
|
||||
return true;
|
||||
} else if (name == "objc_implement") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_Bool) {
|
||||
ac->objc_is_implementation = ev.value_bool;
|
||||
} else if (ev.kind == ExactValue_Invalid) {
|
||||
ac->objc_is_implementation = true;
|
||||
} else {
|
||||
error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
|
||||
}
|
||||
return true;
|
||||
} else if (name == "objc_superclass") {
|
||||
Type *objc_superclass = check_type(c, value);
|
||||
|
||||
if (objc_superclass != nullptr) {
|
||||
ac->objc_superclass = objc_superclass;
|
||||
} else {
|
||||
error(value, "'%.*s' expected a named type", LIT(name));
|
||||
}
|
||||
return true;
|
||||
} else if (name == "objc_ivar") {
|
||||
Type *objc_ivar = check_type(c, value);
|
||||
|
||||
if (objc_ivar != nullptr && objc_ivar->kind == Type_Named) {
|
||||
ac->objc_ivar = objc_ivar;
|
||||
} else {
|
||||
error(value, "'%.*s' expected a named type", LIT(name));
|
||||
}
|
||||
return true;
|
||||
} else if (name == "objc_context_provider") {
|
||||
Operand o = {};
|
||||
check_expr(c, &o, value);
|
||||
Entity *e = entity_of_node(o.expr);
|
||||
|
||||
if (e != nullptr) {
|
||||
if (ac->objc_context_provider != nullptr) {
|
||||
error(elem, "Previous usage of a 'objc_context_provider' attribute");
|
||||
}
|
||||
if (e->kind != Entity_Procedure) {
|
||||
error(elem, "'objc_context_provider' must refer to a procedure");
|
||||
} else {
|
||||
ac->objc_context_provider = e;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -6240,6 +6325,12 @@ gb_internal void check_deferred_procedures(Checker *c) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dst->flags & EntityFlag_Disabled) {
|
||||
// Prevent procedures that have been disabled from acting as deferrals.
|
||||
src->Procedure.deferred_procedure = {};
|
||||
continue;
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_proc(src->type));
|
||||
GB_ASSERT(is_type_proc(dst->type));
|
||||
Type *src_params = base_type(src->type)->Proc.params;
|
||||
@@ -6395,6 +6486,44 @@ gb_internal void check_deferred_procedures(Checker *c) {
|
||||
|
||||
}
|
||||
|
||||
gb_internal void check_objc_context_provider_procedures(Checker *c) {
|
||||
for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
|
||||
GB_ASSERT(e->kind == Entity_TypeName);
|
||||
|
||||
Entity *proc_entity = e->TypeName.objc_context_provider;
|
||||
GB_ASSERT(proc_entity->kind == Entity_Procedure);
|
||||
|
||||
auto &proc = proc_entity->type->Proc;
|
||||
|
||||
Type *return_type = proc.result_count != 1 ? t_untyped_nil : base_named_type(proc.results->Tuple.variables[0]->type);
|
||||
if (return_type != t_context) {
|
||||
error(proc_entity->token, "The @(objc_context_provider) procedure must only return a context.");
|
||||
}
|
||||
|
||||
const char *self_param_err = "The @(objc_context_provider) procedure must take as a parameter a single pointer to the @(objc_type) value.";
|
||||
if (proc.param_count != 1) {
|
||||
error(proc_entity->token, self_param_err);
|
||||
}
|
||||
|
||||
Type *self_param = base_type(proc.params->Tuple.variables[0]->type);
|
||||
if (self_param->kind != Type_Pointer) {
|
||||
error(proc_entity->token, self_param_err);
|
||||
}
|
||||
|
||||
Type *self_type = base_named_type(self_param->Pointer.elem);
|
||||
if (!internal_check_is_assignable_to(self_type, e->type) &&
|
||||
!(e->TypeName.objc_ivar && internal_check_is_assignable_to(self_type, e->TypeName.objc_ivar))) {
|
||||
error(proc_entity->token, self_param_err);
|
||||
}
|
||||
if (proc.calling_convention != ProcCC_CDecl && proc.calling_convention != ProcCC_Contextless) {
|
||||
error(e->token, self_param_err);
|
||||
}
|
||||
if (proc.is_polymorphic) {
|
||||
error(e->token, self_param_err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void check_unique_package_names(Checker *c) {
|
||||
ERROR_BLOCK();
|
||||
|
||||
@@ -6555,6 +6684,7 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gb_internal void check_parsed_files(Checker *c) {
|
||||
TIME_SECTION("map full filepaths to scope");
|
||||
add_type_info_type(&c->builtin_ctx, t_invalid);
|
||||
@@ -6664,6 +6794,9 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
TIME_SECTION("check deferred procedures");
|
||||
check_deferred_procedures(c);
|
||||
|
||||
TIME_SECTION("check objc context provider procedures");
|
||||
check_objc_context_provider_procedures(c);
|
||||
|
||||
TIME_SECTION("calculate global init order");
|
||||
calculate_global_init_order(c);
|
||||
|
||||
|
||||
+22
-2
@@ -149,8 +149,14 @@ struct AttributeContext {
|
||||
|
||||
String objc_class;
|
||||
String objc_name;
|
||||
bool objc_is_class_method;
|
||||
String objc_selector;
|
||||
Type * objc_type;
|
||||
Type * objc_superclass;
|
||||
Type * objc_ivar;
|
||||
Entity *objc_context_provider;
|
||||
bool objc_is_class_method;
|
||||
bool objc_is_implementation; // This struct or proc provides a class/method implementation, not a binding to an existing type.
|
||||
bool objc_is_disabled_implement; // This means the method explicitly set @objc_implement to false so it won't be inferred from the class' attribute.
|
||||
|
||||
String require_target_feature; // required by the target micro-architecture
|
||||
String enable_target_feature; // will be enabled for the procedure only
|
||||
@@ -366,6 +372,11 @@ struct ObjcMsgData {
|
||||
Type *proc_type;
|
||||
};
|
||||
|
||||
struct ObjcMethodData {
|
||||
AttributeContext ac;
|
||||
Entity *proc_entity;
|
||||
};
|
||||
|
||||
enum LoadFileTier {
|
||||
LoadFileTier_Invalid,
|
||||
LoadFileTier_Exists,
|
||||
@@ -477,9 +488,17 @@ struct CheckerInfo {
|
||||
|
||||
MPSCQueue<Ast *> intrinsics_entry_point_usage;
|
||||
|
||||
BlockingMutex objc_types_mutex;
|
||||
BlockingMutex objc_objc_msgSend_mutex;
|
||||
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
|
||||
|
||||
BlockingMutex objc_class_name_mutex;
|
||||
StringSet obcj_class_name_set;
|
||||
MPSCQueue<Entity *> objc_class_implementations;
|
||||
|
||||
BlockingMutex objc_method_mutex;
|
||||
PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;
|
||||
|
||||
|
||||
BlockingMutex load_file_mutex;
|
||||
StringMap<LoadFileCache *> load_file_cache;
|
||||
|
||||
@@ -556,6 +575,7 @@ struct Checker {
|
||||
CheckerContext builtin_ctx;
|
||||
|
||||
MPSCQueue<Entity *> procs_with_deferred_to_check;
|
||||
MPSCQueue<Entity *> procs_with_objc_context_provider_to_check;
|
||||
Array<ProcInfo *> procs_to_check;
|
||||
|
||||
BlockingMutex nested_proc_lits_mutex;
|
||||
|
||||
@@ -26,6 +26,7 @@ enum BuiltinProcId {
|
||||
BuiltinProc_conj,
|
||||
|
||||
BuiltinProc_expand_values,
|
||||
BuiltinProc_compress_values,
|
||||
|
||||
BuiltinProc_min,
|
||||
BuiltinProc_max,
|
||||
@@ -234,6 +235,9 @@ BuiltinProc__type_begin,
|
||||
BuiltinProc_type_convert_variants_to_pointers,
|
||||
BuiltinProc_type_merge,
|
||||
|
||||
BuiltinProc_type_integer_to_unsigned,
|
||||
BuiltinProc_type_integer_to_signed,
|
||||
|
||||
BuiltinProc__type_simple_boolean_begin,
|
||||
BuiltinProc_type_is_boolean,
|
||||
BuiltinProc_type_is_integer,
|
||||
@@ -338,6 +342,7 @@ BuiltinProc__type_end,
|
||||
BuiltinProc_objc_find_class,
|
||||
BuiltinProc_objc_register_selector,
|
||||
BuiltinProc_objc_register_class,
|
||||
BuiltinProc_objc_ivar_get,
|
||||
|
||||
BuiltinProc_constant_utf16_cstring,
|
||||
|
||||
@@ -375,6 +380,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("expand_values"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("compress_values"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
@@ -582,6 +588,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_merge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("type_integer_to_unsigned"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_integer_to_signed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
@@ -686,6 +695,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, false, true},
|
||||
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
|
||||
|
||||
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
@@ -235,6 +235,10 @@ struct Entity {
|
||||
Type * type_parameter_specialization;
|
||||
String ir_mangled_name;
|
||||
bool is_type_alias;
|
||||
bool objc_is_implementation;
|
||||
Type* objc_superclass;
|
||||
Type* objc_ivar;
|
||||
Entity*objc_context_provider;
|
||||
String objc_class_name;
|
||||
TypeNameObjCMetadata *objc_metadata;
|
||||
} TypeName;
|
||||
|
||||
+14
-1
@@ -769,7 +769,17 @@ try_cross_linking:;
|
||||
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, "-Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib ");
|
||||
// Get the MacOSX SDK path.
|
||||
gbString darwin_sdk_path = gb_string_make(temporary_allocator(), "");
|
||||
if (!system_exec_command_line_app_output("xcrun --sdk macosx --show-sdk-path", &darwin_sdk_path)) {
|
||||
darwin_sdk_path = gb_string_set(darwin_sdk_path, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk");
|
||||
} else {
|
||||
// Trim the trailing newline.
|
||||
darwin_sdk_path = gb_string_trim_space(darwin_sdk_path);
|
||||
}
|
||||
platform_lib_str = gb_string_append_fmt(platform_lib_str, "--sysroot %s ", darwin_sdk_path);
|
||||
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-L/usr/local/lib ");
|
||||
|
||||
// Homebrew's default library path, checking if it exists to avoid linking warnings.
|
||||
if (gb_file_exists("/opt/homebrew/lib")) {
|
||||
@@ -791,6 +801,9 @@ try_cross_linking:;
|
||||
// This points the linker to where the entry point is
|
||||
link_settings = gb_string_appendc(link_settings, "-e _main ");
|
||||
}
|
||||
} else if (build_context.metrics.os == TargetOs_freebsd) {
|
||||
// FreeBSD pkg installs third-party shared libraries in /usr/local/lib.
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-L/usr/local/lib ");
|
||||
} else if (build_context.metrics.os == TargetOs_openbsd) {
|
||||
// OpenBSD ports install shared libraries in /usr/local/lib. Also, we must explicitly link libpthread.
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread -Wl,-L/usr/local/lib ");
|
||||
|
||||
+14
-28
@@ -977,7 +977,7 @@ namespace lbAbiAmd64SysV {
|
||||
return types[0];
|
||||
}
|
||||
|
||||
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, sz == 0);
|
||||
return LLVMStructTypeInContext(c, types.data, cast(unsigned)types.count, false);
|
||||
}
|
||||
|
||||
gb_internal void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off) {
|
||||
@@ -1231,38 +1231,24 @@ namespace lbAbiArm64 {
|
||||
}
|
||||
} else {
|
||||
i64 size = lb_sizeof(return_type);
|
||||
if (size <= 16) {
|
||||
LLVMTypeRef cast_type = nullptr;
|
||||
|
||||
if (size == 0) {
|
||||
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
|
||||
} else if (size <= 8) {
|
||||
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
|
||||
} else {
|
||||
unsigned count = cast(unsigned)((size+7)/8);
|
||||
|
||||
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
|
||||
LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
|
||||
|
||||
i64 size_copy = size;
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (size_copy >= 8) {
|
||||
types[i] = llvm_i64;
|
||||
} else {
|
||||
types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
|
||||
}
|
||||
size_copy -= 8;
|
||||
}
|
||||
GB_ASSERT(size_copy <= 0);
|
||||
cast_type = LLVMStructTypeInContext(c, types, count, true);
|
||||
}
|
||||
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
|
||||
} else {
|
||||
if (size > 16) {
|
||||
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
|
||||
|
||||
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
|
||||
return lb_arg_type_indirect(return_type, attr);
|
||||
}
|
||||
|
||||
GB_ASSERT(size <= 16);
|
||||
LLVMTypeRef cast_type = nullptr;
|
||||
if (size == 0) {
|
||||
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
|
||||
} else if (size <= 8) {
|
||||
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
|
||||
} else {
|
||||
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
|
||||
cast_type = llvm_array_type(llvm_i64, 2);
|
||||
}
|
||||
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+667
-45
@@ -1173,6 +1173,344 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
|
||||
return p;
|
||||
}
|
||||
|
||||
String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
|
||||
// NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
|
||||
|
||||
// NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always?
|
||||
#define INT_SIZE_ENCODING (build_context.metrics.int_size == 4 ? "i" : "q")
|
||||
switch (t->kind) {
|
||||
case Type_Basic: {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_Invalid:
|
||||
return str_lit("?");
|
||||
|
||||
case Basic_llvm_bool:
|
||||
case Basic_bool:
|
||||
case Basic_b8:
|
||||
return str_lit("B");
|
||||
|
||||
case Basic_b16:
|
||||
return str_lit("C");
|
||||
case Basic_b32:
|
||||
return str_lit("I");
|
||||
case Basic_b64:
|
||||
return str_lit("q");
|
||||
case Basic_i8:
|
||||
return str_lit("c");
|
||||
case Basic_u8:
|
||||
return str_lit("C");
|
||||
case Basic_i16:
|
||||
case Basic_i16le:
|
||||
case Basic_i16be:
|
||||
return str_lit("s");
|
||||
case Basic_u16:
|
||||
case Basic_u16le:
|
||||
case Basic_u16be:
|
||||
return str_lit("S");
|
||||
case Basic_i32:
|
||||
case Basic_i32le:
|
||||
case Basic_i32be:
|
||||
return str_lit("i");
|
||||
case Basic_u32le:
|
||||
case Basic_u32:
|
||||
case Basic_u32be:
|
||||
return str_lit("I");
|
||||
case Basic_i64:
|
||||
case Basic_i64le:
|
||||
case Basic_i64be:
|
||||
return str_lit("q");
|
||||
case Basic_u64:
|
||||
case Basic_u64le:
|
||||
case Basic_u64be:
|
||||
return str_lit("Q");
|
||||
case Basic_i128:
|
||||
case Basic_i128le:
|
||||
case Basic_i128be:
|
||||
return str_lit("t");
|
||||
case Basic_u128:
|
||||
case Basic_u128le:
|
||||
case Basic_u128be:
|
||||
return str_lit("T");
|
||||
case Basic_rune:
|
||||
return str_lit("I");
|
||||
case Basic_f16:
|
||||
case Basic_f16le:
|
||||
case Basic_f16be:
|
||||
return str_lit("s"); // @harold: Closest we've got?
|
||||
case Basic_f32:
|
||||
case Basic_f32le:
|
||||
case Basic_f32be:
|
||||
return str_lit("f");
|
||||
case Basic_f64:
|
||||
case Basic_f64le:
|
||||
case Basic_f64be:
|
||||
return str_lit("d");
|
||||
|
||||
case Basic_complex32: return str_lit("{complex32=ss}"); // No f16 encoding, so fallback to i16, as above in Basic_f16*
|
||||
case Basic_complex64: return str_lit("{complex64=ff}");
|
||||
case Basic_complex128: return str_lit("{complex128=dd}");
|
||||
case Basic_quaternion64: return str_lit("{quaternion64=ssss}");
|
||||
case Basic_quaternion128: return str_lit("{quaternion128=ffff}");
|
||||
case Basic_quaternion256: return str_lit("{quaternion256=dddd}");
|
||||
|
||||
case Basic_int:
|
||||
return str_lit(INT_SIZE_ENCODING);
|
||||
case Basic_uint:
|
||||
return build_context.metrics.int_size == 4 ? str_lit("I") : str_lit("Q");
|
||||
case Basic_uintptr:
|
||||
case Basic_rawptr:
|
||||
return str_lit("^v");
|
||||
|
||||
case Basic_string:
|
||||
return build_context.metrics.int_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
|
||||
|
||||
case Basic_cstring: return str_lit("*");
|
||||
case Basic_any: return str_lit("{any=^v^v}"); // rawptr + ^Type_Info
|
||||
|
||||
case Basic_typeid:
|
||||
GB_ASSERT(t->Basic.size == 8);
|
||||
return str_lit("q");
|
||||
|
||||
// Untyped types
|
||||
case Basic_UntypedBool:
|
||||
case Basic_UntypedInteger:
|
||||
case Basic_UntypedFloat:
|
||||
case Basic_UntypedComplex:
|
||||
case Basic_UntypedQuaternion:
|
||||
case Basic_UntypedString:
|
||||
case Basic_UntypedRune:
|
||||
case Basic_UntypedNil:
|
||||
case Basic_UntypedUninit:
|
||||
GB_PANIC("Untyped types cannot be @encoded()");
|
||||
return str_lit("?");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Type_Named:
|
||||
case Type_Struct:
|
||||
case Type_Union: {
|
||||
Type* base = t;
|
||||
if (base->kind == Type_Named) {
|
||||
base = base_type(base);
|
||||
if(base->kind != Type_Struct && base->kind != Type_Union) {
|
||||
return lb_get_objc_type_encoding(base, pointer_depth);
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_union = base->kind == Type_Union;
|
||||
if (!is_union) {
|
||||
// Check for objc_SEL
|
||||
if (internal_check_is_assignable_to(base, t_objc_SEL)) {
|
||||
return str_lit(":");
|
||||
}
|
||||
|
||||
// Check for objc_Class
|
||||
if (internal_check_is_assignable_to(base, t_objc_SEL)) {
|
||||
return str_lit("#");
|
||||
}
|
||||
|
||||
// Treat struct as an Objective-C Class?
|
||||
if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) {
|
||||
return str_lit("#");
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_objc_object(base)) {
|
||||
return str_lit("@");
|
||||
}
|
||||
|
||||
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), 16);
|
||||
s = gb_string_append_length(s, is_union ? "(" :"{", 1);
|
||||
if (t->kind == Type_Named) {
|
||||
s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len);
|
||||
}
|
||||
|
||||
// Write fields
|
||||
if (pointer_depth < 2) {
|
||||
s = gb_string_append_length(s, "=", 1);
|
||||
|
||||
if (!is_union) {
|
||||
for( auto& f : base->Struct.fields ) {
|
||||
String field_type = lb_get_objc_type_encoding(f->type, pointer_depth);
|
||||
s = gb_string_append_length(s, field_type.text, field_type.len);
|
||||
}
|
||||
} else {
|
||||
for( auto& v : base->Union.variants ) {
|
||||
String variant_type = lb_get_objc_type_encoding(v, pointer_depth);
|
||||
s = gb_string_append_length(s, variant_type.text, variant_type.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s = gb_string_append_length(s, is_union ? ")" :"}", 1);
|
||||
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_Generic:
|
||||
GB_PANIC("Generic types cannot be @encoded()");
|
||||
return str_lit("?");
|
||||
|
||||
case Type_Pointer: {
|
||||
String pointee = lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1);
|
||||
// Special case for Objective-C Objects
|
||||
if (pointer_depth == 0 && pointee == "@") {
|
||||
return pointee;
|
||||
}
|
||||
|
||||
return concatenate_strings(temporary_allocator(), str_lit("^"), pointee);
|
||||
}
|
||||
|
||||
case Type_MultiPointer:
|
||||
return concatenate_strings(temporary_allocator(), str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, pointer_depth +1));
|
||||
|
||||
case Type_Array: {
|
||||
String type_str = lb_get_objc_type_encoding(t->Array.elem, pointer_depth);
|
||||
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
|
||||
s = gb_string_append_fmt(s, "[%lld%.*s]", t->Array.count, LIT(type_str));
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_EnumeratedArray: {
|
||||
String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, pointer_depth);
|
||||
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
|
||||
s = gb_string_append_fmt(s, "[%lld%.*s]", t->EnumeratedArray.count, LIT(type_str));
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_Slice: {
|
||||
String type_str = lb_get_objc_type_encoding(t->Slice.elem, pointer_depth);
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
|
||||
s = gb_string_append_fmt(s, "{slice=^%.*s%s}", LIT(type_str), INT_SIZE_ENCODING);
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_DynamicArray: {
|
||||
String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, pointer_depth);
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 8);
|
||||
s = gb_string_append_fmt(s, "{dynamic=^%.*s%s%sAllocator={?^v}}", LIT(type_str), INT_SIZE_ENCODING, INT_SIZE_ENCODING);
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_Map:
|
||||
return str_lit("{^v^v{Allocator=?^v}}");
|
||||
case Type_Enum:
|
||||
return lb_get_objc_type_encoding(t->Enum.base_type, pointer_depth);
|
||||
case Type_Tuple:
|
||||
// NOTE(harold): Is this type allowed here?
|
||||
return str_lit("?");
|
||||
case Type_Proc:
|
||||
return str_lit("?");
|
||||
case Type_BitSet:
|
||||
return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth);
|
||||
|
||||
case Type_SimdVector: {
|
||||
String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
|
||||
gb_string_append_fmt(s, "[%lld%.*s]", t->SimdVector.count, LIT(type_str));
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_Matrix: {
|
||||
String type_str = lb_get_objc_type_encoding(t->Matrix.elem, pointer_depth);
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), type_str.len + 5);
|
||||
i64 element_count = t->Matrix.column_count * t->Matrix.row_count;
|
||||
gb_string_append_fmt(s, "[%lld%.*s]", element_count, LIT(type_str));
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
case Type_BitField:
|
||||
return lb_get_objc_type_encoding(t->BitField.backing_type, pointer_depth);
|
||||
case Type_SoaPointer: {
|
||||
gbString s = gb_string_make_reserve(temporary_allocator(), 8);
|
||||
s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING);
|
||||
return make_string_c(s);
|
||||
}
|
||||
|
||||
} // End switch t->kind
|
||||
#undef INT_SIZE_ENCODING
|
||||
|
||||
GB_PANIC("Unreachable");
|
||||
return str_lit("");
|
||||
}
|
||||
|
||||
struct lbObjCGlobalClass {
|
||||
lbObjCGlobal g;
|
||||
lbValue class_value; // Local registered class value
|
||||
};
|
||||
|
||||
gb_internal void lb_register_objc_thing(
|
||||
StringSet &handled,
|
||||
lbModule *m,
|
||||
Array<lbValue> &args,
|
||||
Array<lbObjCGlobalClass> &class_impls,
|
||||
StringMap<lbObjCGlobalClass> &class_map,
|
||||
lbProcedure *p,
|
||||
lbObjCGlobal const &g,
|
||||
char const *call
|
||||
) {
|
||||
if (string_set_update(&handled, g.name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
lbAddr addr = {};
|
||||
lbValue *found = string_map_get(&m->members, g.global_name);
|
||||
if (found) {
|
||||
addr = lb_addr(*found);
|
||||
} else {
|
||||
lbValue v = {};
|
||||
LLVMTypeRef t = lb_type(m, g.type);
|
||||
v.value = LLVMAddGlobal(m->mod, t, g.global_name);
|
||||
v.type = alloc_type_pointer(g.type);
|
||||
addr = lb_addr(v);
|
||||
LLVMSetInitializer(v.value, LLVMConstNull(t));
|
||||
}
|
||||
|
||||
lbValue class_ptr = {};
|
||||
lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
|
||||
|
||||
// If this class requires an implementation, save it for registration below.
|
||||
if (g.class_impl_type != nullptr) {
|
||||
|
||||
// Make sure the superclass has been initialized before us
|
||||
lbValue superclass_value = lb_const_nil(m, t_objc_Class);
|
||||
|
||||
auto &tn = g.class_impl_type->Named.type_name->TypeName;
|
||||
Type *superclass = tn.objc_superclass;
|
||||
if (superclass != nullptr) {
|
||||
auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
|
||||
lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
|
||||
GB_ASSERT(superclass_global.class_value.value);
|
||||
|
||||
superclass_value = superclass_global.class_value;
|
||||
}
|
||||
|
||||
args.count = 3;
|
||||
args[0] = superclass_value;
|
||||
args[1] = class_name;
|
||||
args[2] = lb_const_int(m, t_uint, 0);
|
||||
class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
|
||||
|
||||
array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
|
||||
}
|
||||
else {
|
||||
args.count = 1;
|
||||
args[0] = class_name;
|
||||
class_ptr = lb_emit_runtime_call(p, call, args);
|
||||
}
|
||||
|
||||
lb_addr_store(p, addr, class_ptr);
|
||||
|
||||
lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
|
||||
if (class_global != nullptr) {
|
||||
class_global->class_value = class_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
if (p == nullptr) {
|
||||
return;
|
||||
@@ -1186,38 +1524,329 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
|
||||
string_set_init(&handled);
|
||||
defer (string_set_destroy(&handled));
|
||||
|
||||
auto args = array_make<lbValue>(temporary_allocator(), 1);
|
||||
auto args = array_make<lbValue>(temporary_allocator(), 3, 8);
|
||||
auto class_impls = array_make<lbObjCGlobalClass>(temporary_allocator(), 0, 16);
|
||||
|
||||
// Register all class implementations unconditionally, even if not statically referenced
|
||||
for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) {
|
||||
GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation);
|
||||
lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type);
|
||||
}
|
||||
|
||||
// Ensure classes that have been implicitly referenced through
|
||||
// the objc_superclass attribute have a global variable available for them.
|
||||
TypeSet class_set{};
|
||||
type_set_init(&class_set, gen->objc_classes.count+16);
|
||||
defer (type_set_destroy(&class_set));
|
||||
|
||||
auto referenced_classes = array_make<lbObjCGlobal>(temporary_allocator());
|
||||
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
|
||||
array_add(&referenced_classes, g);
|
||||
|
||||
Type *cls = g.class_impl_type;
|
||||
while (cls) {
|
||||
if (type_set_update(&class_set, cls)) {
|
||||
break;
|
||||
}
|
||||
GB_ASSERT(cls->kind == Type_Named);
|
||||
|
||||
cls = cls->Named.type_name->TypeName.objc_superclass;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto pair : class_set) {
|
||||
auto& tn = pair.type->Named.type_name->TypeName;
|
||||
Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type;
|
||||
lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl);
|
||||
}
|
||||
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
|
||||
array_add( &referenced_classes, g );
|
||||
}
|
||||
|
||||
// Add all class globals to a map so that we can look them up dynamically
|
||||
// in order to resolve out-of-order because classes that are being implemented
|
||||
// require their superclasses to be registered before them.
|
||||
StringMap<lbObjCGlobalClass> global_class_map{};
|
||||
string_map_init(&global_class_map, (usize)gen->objc_classes.count);
|
||||
defer (string_map_destroy(&global_class_map));
|
||||
|
||||
for (lbObjCGlobal g :referenced_classes) {
|
||||
string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
|
||||
}
|
||||
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
auto register_thing = [&handled, &m, &args](lbProcedure *p, lbObjCGlobal const &g, char const *call) {
|
||||
if (!string_set_update(&handled, g.name)) {
|
||||
lbAddr addr = {};
|
||||
lbValue *found = string_map_get(&m->members, g.global_name);
|
||||
if (found) {
|
||||
addr = lb_addr(*found);
|
||||
} else {
|
||||
lbValue v = {};
|
||||
LLVMTypeRef t = lb_type(m, g.type);
|
||||
v.value = LLVMAddGlobal(m->mod, t, g.global_name);
|
||||
v.type = alloc_type_pointer(g.type);
|
||||
addr = lb_addr(v);
|
||||
LLVMSetInitializer(v.value, LLVMConstNull(t));
|
||||
}
|
||||
|
||||
args[0] = lb_const_value(m, t_cstring, exact_value_string(g.name));
|
||||
lbValue ptr = lb_emit_runtime_call(p, call, args);
|
||||
lb_addr_store(p, addr, ptr);
|
||||
}
|
||||
};
|
||||
|
||||
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
|
||||
register_thing(p, g, "objc_lookUpClass");
|
||||
// Register class globals, gathering classes that must be implemented
|
||||
for (auto& kv : global_class_map) {
|
||||
lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass");
|
||||
}
|
||||
|
||||
// Prefetch selectors for implemented methods so that they can also be registered.
|
||||
for (const auto& cd : class_impls) {
|
||||
auto& g = cd.g;
|
||||
Type *class_type = g.class_impl_type;
|
||||
|
||||
Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
|
||||
if (!methods) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const ObjcMethodData& md : *methods) {
|
||||
lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can register all referenced selectors
|
||||
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
|
||||
register_thing(p, g, "sel_registerName");
|
||||
lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName");
|
||||
}
|
||||
|
||||
|
||||
// Emit method wrapper implementations and registration
|
||||
auto wrapper_args = array_make<Type *>(temporary_allocator(), 2, 8);
|
||||
auto get_context_args = array_make<lbValue>(temporary_allocator(), 1);
|
||||
|
||||
|
||||
PtrMap<Type *, lbObjCGlobal> ivar_map{};
|
||||
map_init(&ivar_map, gen->objc_ivars.count);
|
||||
|
||||
for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_ivars, &g); /**/) {
|
||||
map_set(&ivar_map, g.class_impl_type, g);
|
||||
}
|
||||
|
||||
for (const auto &cd : class_impls) {
|
||||
auto &g = cd.g;
|
||||
Type *class_type = g.class_impl_type;
|
||||
Type *class_ptr_type = alloc_type_pointer(class_type);
|
||||
lbValue class_value = cd.class_value;
|
||||
|
||||
Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
|
||||
|
||||
Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider;
|
||||
Type *contex_provider_self_ptr_type = nullptr;
|
||||
Type *contex_provider_self_named_type = nullptr;
|
||||
bool is_context_provider_ivar = false;
|
||||
lbValue context_provider_proc_value{};
|
||||
|
||||
if (context_provider) {
|
||||
context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider);
|
||||
|
||||
contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type);
|
||||
GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer);
|
||||
contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type));
|
||||
|
||||
is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
|
||||
}
|
||||
|
||||
|
||||
Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
|
||||
if (!methods) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const ObjcMethodData &md : *methods) {
|
||||
GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
|
||||
Type *method_type = md.proc_entity->type;
|
||||
|
||||
String proc_name = make_string_c("__$objc_method::");
|
||||
proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name);
|
||||
proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::"));
|
||||
proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name);
|
||||
|
||||
wrapper_args.count = 2;
|
||||
wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type;
|
||||
wrapper_args[1] = t_objc_SEL;
|
||||
|
||||
isize method_param_count = method_type->Proc.param_count;
|
||||
isize method_param_offset = 0;
|
||||
|
||||
if (!md.ac.objc_is_class_method) {
|
||||
GB_ASSERT(method_param_count >= 1);
|
||||
method_param_count -= 1;
|
||||
method_param_offset = 1;
|
||||
}
|
||||
|
||||
for (isize i = 0; i < method_param_count; i++) {
|
||||
array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type);
|
||||
}
|
||||
|
||||
Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true);
|
||||
Type *wrapper_results_tuple = nullptr;
|
||||
|
||||
if (method_type->Proc.result_count > 0) {
|
||||
GB_ASSERT(method_type->Proc.result_count == 1);
|
||||
wrapper_results_tuple = alloc_type_tuple_from_field_types(&method_type->Proc.results->Tuple.variables[0]->type, 1, false, true);
|
||||
}
|
||||
|
||||
Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, wrapper_args_tuple->Tuple.variables.count,
|
||||
wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
|
||||
|
||||
lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
|
||||
lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
|
||||
|
||||
// Emit the wrapper
|
||||
LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
|
||||
lb_begin_procedure_body(wrapper_proc);
|
||||
{
|
||||
if (method_type->Proc.calling_convention == ProcCC_Odin) {
|
||||
GB_ASSERT(context_provider);
|
||||
|
||||
// Emit the get odin context call
|
||||
|
||||
get_context_args[0] = lbValue {
|
||||
wrapper_proc->raw_input_parameters[0],
|
||||
contex_provider_self_ptr_type,
|
||||
};
|
||||
|
||||
if (is_context_provider_ivar) {
|
||||
// The context provider takes the ivar's type.
|
||||
// Emit an objc_ivar_get call and use that pointer for 'self' instead.
|
||||
lbValue real_self {
|
||||
wrapper_proc->raw_input_parameters[0],
|
||||
class_ptr_type
|
||||
};
|
||||
get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self);
|
||||
}
|
||||
|
||||
lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
|
||||
lbAddr context_addr = lb_addr(lb_address_from_load_or_generate_local(wrapper_proc, context));
|
||||
lb_push_context_onto_stack(wrapper_proc, context_addr);
|
||||
}
|
||||
|
||||
|
||||
auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + method_param_offset);
|
||||
|
||||
if (!md.ac.objc_is_class_method) {
|
||||
method_call_args[0] = lbValue {
|
||||
wrapper_proc->raw_input_parameters[0],
|
||||
class_ptr_type,
|
||||
};
|
||||
}
|
||||
|
||||
for (isize i = 0; i < method_param_count; i++) {
|
||||
method_call_args[i+method_param_offset] = lbValue {
|
||||
wrapper_proc->raw_input_parameters[i+2],
|
||||
method_type->Proc.params->Tuple.variables[i+method_param_offset]->type,
|
||||
};
|
||||
}
|
||||
lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
|
||||
|
||||
// Call real procedure for method from here, passing the parameters expected, if any.
|
||||
lbValue return_value = lb_emit_call(wrapper_proc, method_proc_value, method_call_args);
|
||||
|
||||
if (wrapper_results_tuple != nullptr) {
|
||||
auto &result_var = method_type->Proc.results->Tuple.variables[0];
|
||||
return_value = lb_emit_conv(wrapper_proc, return_value, result_var->type);
|
||||
lb_build_return_stmt_internal(wrapper_proc, return_value, result_var->token.pos);
|
||||
}
|
||||
}
|
||||
lb_end_procedure_body(wrapper_proc);
|
||||
|
||||
|
||||
// Add the method to the class
|
||||
String method_encoding = str_lit("v");
|
||||
// TODO (harold): Checker must ensure that objc_methods have a single return value or none!
|
||||
GB_ASSERT(method_type->Proc.result_count <= 1);
|
||||
if (method_type->Proc.result_count != 0) {
|
||||
method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
|
||||
}
|
||||
|
||||
if (!md.ac.objc_is_class_method) {
|
||||
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:"));
|
||||
} else {
|
||||
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
|
||||
}
|
||||
|
||||
for (isize i = method_param_offset; i < method_param_count; i++) {
|
||||
Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
|
||||
String param_encoding = lb_get_objc_type_encoding(param_type);
|
||||
|
||||
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
|
||||
}
|
||||
|
||||
// Emit method registration
|
||||
lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector);
|
||||
GB_ASSERT(sel_address);
|
||||
lbValue selector_value = lb_addr_load(p, *sel_address);
|
||||
|
||||
args.count = 4;
|
||||
args[0] = class_value; // Class
|
||||
args[1] = selector_value; // SEL
|
||||
args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
|
||||
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
|
||||
|
||||
// TODO(harold): Emit check BOOL result and panic if false.
|
||||
lb_emit_runtime_call(p, "class_addMethod", args);
|
||||
|
||||
} // End methods
|
||||
|
||||
// Add ivar if we have one
|
||||
if (ivar_type != nullptr) {
|
||||
// Register a single ivar for this class
|
||||
Type *ivar_base = ivar_type->Named.base;
|
||||
|
||||
// @note(harold): The alignment is supposed to be passed as log2(alignment): https://developer.apple.com/documentation/objectivec/class_addivar(_:_:_:_:_:)?language=objc
|
||||
const i64 size = type_size_of(ivar_base);
|
||||
const i64 alignment = (i64)floor_log2((u64)type_align_of(ivar_base));
|
||||
|
||||
// NOTE(harold): I've opted to not emit the type encoding for ivars in order to keep the data private.
|
||||
// If there is desire in the future to emit the type encoding for introspection through the Obj-C runtime,
|
||||
// then perhaps an option can be added for it then.
|
||||
// Should we pass the actual type encoding? Might not be ideal for obfuscation.
|
||||
String ivar_name = str_lit("__$ivar");
|
||||
String ivar_types = str_lit("{= }"); //lb_get_objc_type_encoding(ivar_type);
|
||||
args.count = 5;
|
||||
args[0] = class_value;
|
||||
args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name));
|
||||
args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size));
|
||||
args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment));
|
||||
args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types));
|
||||
lb_emit_runtime_call(p, "class_addIvar", args);
|
||||
}
|
||||
|
||||
// Complete the class registration
|
||||
args.count = 1;
|
||||
args[0] = class_value;
|
||||
lb_emit_runtime_call(p, "objc_registerClassPair", args);
|
||||
}
|
||||
|
||||
// Register ivar offsets for any `objc_ivar_get` expressions emitted.
|
||||
for (auto const& kv : ivar_map) {
|
||||
lbObjCGlobal const& g = kv.value;
|
||||
lbAddr ivar_addr = {};
|
||||
lbValue *found = string_map_get(&m->members, g.global_name);
|
||||
|
||||
if (found) {
|
||||
ivar_addr = lb_addr(*found);
|
||||
GB_ASSERT(ivar_addr.addr.type == t_int_ptr);
|
||||
} else {
|
||||
// Defined in an external package, define it now in the main package
|
||||
LLVMTypeRef t = lb_type(m, t_int);
|
||||
|
||||
lbValue global{};
|
||||
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
|
||||
global.type = t_int_ptr;
|
||||
|
||||
LLVMSetInitializer(global.value, LLVMConstInt(t, 0, true));
|
||||
|
||||
ivar_addr = lb_addr(global);
|
||||
}
|
||||
|
||||
String class_name = g.class_impl_type->Named.type_name->TypeName.objc_class_name;
|
||||
lbValue class_value = string_map_must_get(&global_class_map, class_name).class_value;
|
||||
|
||||
args.count = 2;
|
||||
args[0] = class_value;
|
||||
args[1] = lb_const_value(m, t_cstring, exact_value_string(str_lit("__$ivar")));
|
||||
lbValue ivar = lb_emit_runtime_call(p, "class_getInstanceVariable", args);
|
||||
|
||||
args.count = 1;
|
||||
args[0] = ivar;
|
||||
lbValue ivar_offset = lb_emit_runtime_call(p, "ivar_getOffset", args);
|
||||
lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int);
|
||||
|
||||
lb_addr_store(p, ivar_addr, ivar_offset_int);
|
||||
}
|
||||
|
||||
lb_end_procedure_body(p);
|
||||
@@ -1278,6 +1907,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
lb_add_attribute_to_proc(p->module, p->value, "optnone");
|
||||
lb_add_attribute_to_proc(p->module, p->value, "noinline");
|
||||
|
||||
// Make sure shared libraries call their own runtime startup on Linux.
|
||||
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
|
||||
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
lb_setup_type_info_data(main_module);
|
||||
@@ -1344,14 +1977,14 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
|
||||
gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
|
||||
var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
|
||||
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
|
||||
lbAddr g = lb_add_global_generated_with_name(main_module, var_type, {}, make_string_c(var_name));
|
||||
lb_addr_store(p, g, var.init);
|
||||
lbValue gp = lb_addr_get_ptr(p, g);
|
||||
|
||||
lbValue data = lb_emit_struct_ep(p, var.var, 0);
|
||||
lbValue ti = lb_emit_struct_ep(p, var.var, 1);
|
||||
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
|
||||
lb_emit_store(p, ti, lb_type_info(p, var_type));
|
||||
lb_emit_store(p, ti, lb_typeid(p->module, var_type));
|
||||
} else {
|
||||
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
|
||||
lbValue src0 = lb_emit_conv(p, var.init, t);
|
||||
@@ -1387,6 +2020,10 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
|
||||
lb_add_attribute_to_proc(p->module, p->value, "optnone");
|
||||
lb_add_attribute_to_proc(p->module, p->value, "noinline");
|
||||
|
||||
// Make sure shared libraries call their own runtime cleanup on Linux.
|
||||
LLVMSetVisibility(p->value, LLVMHiddenVisibility);
|
||||
LLVMSetLinkage(p->value, LLVMWeakAnyLinkage);
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
CheckerInfo *info = main_module->gen->info;
|
||||
@@ -2185,7 +2822,6 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
|
||||
p->is_done = true;
|
||||
m->curr_procedure = nullptr;
|
||||
}
|
||||
lb_end_procedure(p);
|
||||
|
||||
// Add Flags
|
||||
if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) {
|
||||
@@ -2494,6 +3130,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
LLVMSetInitializer(g, LLVMConstNull(lb_type(m, t)));
|
||||
LLVMSetLinkage(g, LLVMInternalLinkage);
|
||||
lb_make_global_private_const(g);
|
||||
lb_set_odin_rtti_section(g);
|
||||
return lb_addr({g, alloc_type_pointer(t)});
|
||||
};
|
||||
|
||||
@@ -2565,24 +3202,9 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
lbValue g = {};
|
||||
g.value = LLVMAddGlobal(m->mod, lb_type(m, e->type), alloc_cstring(permanent_allocator(), name));
|
||||
g.type = alloc_type_pointer(e->type);
|
||||
if (e->Variable.thread_local_model != "") {
|
||||
LLVMSetThreadLocal(g.value, true);
|
||||
|
||||
String m = e->Variable.thread_local_model;
|
||||
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
|
||||
if (m == "default") {
|
||||
mode = LLVMGeneralDynamicTLSModel;
|
||||
} else if (m == "localdynamic") {
|
||||
mode = LLVMLocalDynamicTLSModel;
|
||||
} else if (m == "initialexec") {
|
||||
mode = LLVMInitialExecTLSModel;
|
||||
} else if (m == "localexec") {
|
||||
mode = LLVMLocalExecTLSModel;
|
||||
} else {
|
||||
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
|
||||
}
|
||||
LLVMSetThreadLocalMode(g.value, mode);
|
||||
}
|
||||
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
|
||||
|
||||
if (is_foreign) {
|
||||
LLVMSetLinkage(g.value, LLVMExternalLinkage);
|
||||
LLVMSetDLLStorageClass(g.value, LLVMDLLImportStorageClass);
|
||||
|
||||
@@ -196,6 +196,7 @@ struct lbModule {
|
||||
|
||||
StringMap<lbAddr> objc_classes;
|
||||
StringMap<lbAddr> objc_selectors;
|
||||
StringMap<lbAddr> objc_ivars;
|
||||
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
@@ -219,6 +220,7 @@ struct lbObjCGlobal {
|
||||
gbString global_name;
|
||||
String name;
|
||||
Type * type;
|
||||
Type * class_impl_type; // This is set when the class has the objc_implement attribute set to true.
|
||||
};
|
||||
|
||||
struct lbGenerator : LinkerData {
|
||||
@@ -240,6 +242,7 @@ struct lbGenerator : LinkerData {
|
||||
MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
|
||||
MPSCQueue<lbObjCGlobal> objc_selectors;
|
||||
MPSCQueue<lbObjCGlobal> objc_classes;
|
||||
MPSCQueue<lbObjCGlobal> objc_ivars;
|
||||
MPSCQueue<String> raddebug_section_strings;
|
||||
};
|
||||
|
||||
@@ -410,7 +413,6 @@ gb_internal LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef c
|
||||
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
|
||||
gb_internal void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
|
||||
gb_internal lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
|
||||
gb_internal void lb_end_procedure(lbProcedure *p);
|
||||
|
||||
|
||||
gb_internal LLVMTypeRef lb_type(lbModule *m, Type *type);
|
||||
|
||||
@@ -533,7 +533,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
|
||||
Entity *e = entity_from_expr(expr);
|
||||
res = lb_find_procedure_value_from_entity(m, e);
|
||||
}
|
||||
GB_ASSERT(res.value != nullptr);
|
||||
if (res.value == nullptr) {
|
||||
// This is an unspecialized polymorphic procedure, return nil or dummy value
|
||||
return lb_const_nil(m, original_type);
|
||||
}
|
||||
GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind);
|
||||
|
||||
if (LLVMGetIntrinsicID(res.value) == 0) {
|
||||
|
||||
@@ -1374,3 +1374,59 @@ gb_internal void lb_add_debug_info_for_global_constant_from_entity(lbGenerator *
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void lb_add_debug_label(lbProcedure *p, Ast *label, lbBlock *target) {
|
||||
// NOTE(tf2spi): LLVM-C DILabel API used only existed for major versions 20+
|
||||
#if LLVM_VERSION_MAJOR >= 20
|
||||
if (p == nullptr || p->debug_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (target == nullptr || label == nullptr || label->kind != Ast_Label) {
|
||||
return;
|
||||
}
|
||||
Token label_token = label->Label.token;
|
||||
if (is_blank_ident(label_token.string)) {
|
||||
return;
|
||||
}
|
||||
lbModule *m = p->module;
|
||||
if (m == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
AstFile *file = label->file();
|
||||
LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
|
||||
if (llvm_file == nullptr) {
|
||||
debugf("llvm file not found for label\n");
|
||||
return;
|
||||
}
|
||||
LLVMMetadataRef llvm_scope = p->debug_info;
|
||||
if(llvm_scope == nullptr) {
|
||||
debugf("llvm scope not found for label\n");
|
||||
return;
|
||||
}
|
||||
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, label_token.pos);
|
||||
LLVMBasicBlockRef llvm_block = target->block;
|
||||
if (llvm_block == nullptr || llvm_debug_loc == nullptr) {
|
||||
return;
|
||||
}
|
||||
LLVMMetadataRef llvm_label = LLVMDIBuilderCreateLabel(
|
||||
m->debug_builder,
|
||||
llvm_scope,
|
||||
(const char *)label_token.string.text,
|
||||
(size_t)label_token.string.len,
|
||||
llvm_file,
|
||||
label_token.pos.line,
|
||||
|
||||
// NOTE(tf2spi): Defaults to false in LLVM API, but I'd rather not take chances
|
||||
// Always preserve the label no matter what when debugging
|
||||
true
|
||||
);
|
||||
GB_ASSERT(llvm_label != nullptr);
|
||||
(void)LLVMDIBuilderInsertLabelAtEnd(
|
||||
m->debug_builder,
|
||||
llvm_label,
|
||||
llvm_debug_loc,
|
||||
llvm_block
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4844,7 +4844,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
if (cl->elems.count == 0) {
|
||||
break;
|
||||
}
|
||||
GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals);
|
||||
GB_ASSERT(expr->file()->feature_flags & OptInFeatureFlag_DynamicLiterals || build_context.dynamic_literals);
|
||||
|
||||
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
|
||||
gb_unused(err);
|
||||
@@ -5154,8 +5154,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
return lb_build_addr(p, unparen_expr(se->selector));
|
||||
}
|
||||
|
||||
|
||||
Type *type = base_type(tav.type);
|
||||
if (tav.mode == Addressing_Type) { // Addressing_Type
|
||||
Selection sel = lookup_field(tav.type, selector, true);
|
||||
if (sel.pseudo_field) {
|
||||
@@ -5190,18 +5188,29 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
return lb_addr_swizzle(a, type, swizzle_count, swizzle_indices);
|
||||
}
|
||||
|
||||
Selection sel = lookup_field(type, selector, false);
|
||||
Selection sel = lookup_field(tav.type, selector, false);
|
||||
GB_ASSERT(sel.entity != nullptr);
|
||||
if (sel.pseudo_field) {
|
||||
GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
|
||||
if (sel.pseudo_field && (sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup)) {
|
||||
Entity *e = entity_of_node(sel_node);
|
||||
GB_ASSERT(e->kind == Entity_Procedure);
|
||||
return lb_addr(lb_find_value_from_entity(p->module, e));
|
||||
}
|
||||
|
||||
if (sel.is_bit_field) {
|
||||
lbAddr addr = lb_build_addr(p, se->expr);
|
||||
lbAddr addr = lb_build_addr(p, se->expr);
|
||||
|
||||
// NOTE(harold): Only allow ivar pseudo field access on indirect selectors.
|
||||
// It is incoherent otherwise as Objective-C objects are zero-sized.
|
||||
Type *deref_type = type_deref(tav.type);
|
||||
if (tav.type->kind == Type_Pointer && deref_type->kind == Type_Named && deref_type->Named.type_name->TypeName.objc_ivar) {
|
||||
// NOTE(harold): We need to load the ivar from the current address and
|
||||
// replace addr with the loaded ivar addr to apply the selector load properly.
|
||||
addr = lb_addr(lb_emit_load(p, addr.addr));
|
||||
|
||||
lbValue ivar_ptr = lb_handle_objc_ivar_for_objc_object_pointer(p, addr.addr);
|
||||
addr = lb_addr(ivar_ptr);
|
||||
}
|
||||
|
||||
if (sel.is_bit_field) {
|
||||
Selection sub_sel = sel;
|
||||
sub_sel.index.count -= 1;
|
||||
|
||||
@@ -5227,7 +5236,6 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
{
|
||||
lbAddr addr = lb_build_addr(p, se->expr);
|
||||
if (addr.kind == lbAddr_Map) {
|
||||
lbValue v = lb_addr_load(p, addr);
|
||||
lbValue a = lb_address_from_load_or_generate_local(p, v);
|
||||
|
||||
+100
-25
@@ -101,6 +101,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
|
||||
|
||||
string_map_init(&m->objc_classes);
|
||||
string_map_init(&m->objc_selectors);
|
||||
string_map_init(&m->objc_ivars);
|
||||
|
||||
map_init(&m->map_info_map, 0);
|
||||
map_init(&m->map_cell_info_map, 0);
|
||||
@@ -173,8 +174,9 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
|
||||
mpsc_init(&gen->objc_selectors, heap_allocator());
|
||||
mpsc_init(&gen->objc_classes, heap_allocator());
|
||||
mpsc_init(&gen->objc_ivars, heap_allocator());
|
||||
mpsc_init(&gen->raddebug_section_strings, heap_allocator());
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -886,8 +888,8 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
|
||||
Type *t = base_type(type_deref(addr.addr.type));
|
||||
GB_ASSERT(t->kind == Type_Struct && t->Struct.soa_kind != StructSoa_None);
|
||||
lbValue len = lb_soa_struct_len(p, addr.addr);
|
||||
if (addr.soa.index_expr != nullptr) {
|
||||
lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), index, len);
|
||||
if (addr.soa.index_expr != nullptr && (!lb_is_const(addr.soa.index) || t->Struct.soa_kind != StructSoa_Fixed)) {
|
||||
lb_emit_bounds_check(p, ast_token(addr.soa.index_expr), addr.soa.index, len);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2213,6 +2215,14 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
|
||||
case Type_BitField:
|
||||
return lb_type_internal(m, type->BitField.backing_type);
|
||||
|
||||
case Type_Generic:
|
||||
if (type->Generic.specialized) {
|
||||
return lb_type_internal(m, type->Generic.specialized);
|
||||
} else {
|
||||
// For unspecialized generics, use a pointer type as a placeholder
|
||||
return LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("Invalid type %s", type_to_string(type));
|
||||
@@ -2378,6 +2388,29 @@ gb_internal void lb_add_attribute_to_proc_with_string(lbModule *m, LLVMValueRef
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) {
|
||||
if (model != "") {
|
||||
LLVMSetThreadLocal(value, true);
|
||||
|
||||
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
|
||||
if (model == "default") {
|
||||
mode = LLVMGeneralDynamicTLSModel;
|
||||
} else if (model == "localdynamic") {
|
||||
mode = LLVMLocalDynamicTLSModel;
|
||||
} else if (model == "initialexec") {
|
||||
mode = LLVMInitialExecTLSModel;
|
||||
} else if (model == "localexec") {
|
||||
mode = LLVMLocalExecTLSModel;
|
||||
} else {
|
||||
GB_PANIC("Unhandled thread local mode %.*s", LIT(model));
|
||||
}
|
||||
LLVMSetThreadLocalMode(value, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal void lb_add_edge(lbBlock *from, lbBlock *to) {
|
||||
LLVMValueRef instr = LLVMGetLastInstruction(from->block);
|
||||
@@ -2516,10 +2549,13 @@ general_end:;
|
||||
}
|
||||
}
|
||||
|
||||
src_size = align_formula(src_size, src_align);
|
||||
dst_size = align_formula(dst_size, dst_align);
|
||||
// NOTE(laytan): even though this logic seems sound, the Address Sanitizer does not
|
||||
// want you to load/store the space of a value that is there for alignment.
|
||||
#if 0
|
||||
i64 aligned_src_size = align_formula(src_size, src_align);
|
||||
i64 aligned_dst_size = align_formula(dst_size, dst_align);
|
||||
|
||||
if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) {
|
||||
if (LLVMIsALoadInst(val) && (aligned_src_size >= aligned_dst_size && src_align >= dst_align)) {
|
||||
LLVMValueRef val_ptr = LLVMGetOperand(val, 0);
|
||||
val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), "");
|
||||
LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr);
|
||||
@@ -2527,8 +2563,57 @@ general_end:;
|
||||
// LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align));
|
||||
|
||||
return loaded_val;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (src_size > dst_size) {
|
||||
GB_ASSERT(p->decl_block != p->curr_block);
|
||||
// NOTE(laytan): src is bigger than dst, need to memcpy the part of src we want.
|
||||
|
||||
LLVMValueRef val_ptr;
|
||||
if (LLVMIsALoadInst(val)) {
|
||||
val_ptr = LLVMGetOperand(val, 0);
|
||||
} else if (LLVMIsAAllocaInst(val)) {
|
||||
val_ptr = LLVMBuildPointerCast(p->builder, val, LLVMPointerType(src_type, 0), "");
|
||||
} else {
|
||||
// NOTE(laytan): we need a pointer to memcpy from.
|
||||
LLVMValueRef val_copy = llvm_alloca(p, src_type, src_align);
|
||||
val_ptr = LLVMBuildPointerCast(p->builder, val_copy, LLVMPointerType(src_type, 0), "");
|
||||
LLVMBuildStore(p->builder, val, val_ptr);
|
||||
}
|
||||
|
||||
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
|
||||
max_align = gb_max(max_align, 16);
|
||||
|
||||
LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
|
||||
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(dst_type, 0), "");
|
||||
|
||||
LLVMTypeRef types[3] = {
|
||||
lb_type(p->module, t_rawptr),
|
||||
lb_type(p->module, t_rawptr),
|
||||
lb_type(p->module, t_int)
|
||||
};
|
||||
|
||||
LLVMValueRef args[4] = {
|
||||
nptr,
|
||||
val_ptr,
|
||||
LLVMConstInt(LLVMIntTypeInContext(p->module->ctx, 8*cast(unsigned)build_context.int_size), dst_size, 0),
|
||||
LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, 0),
|
||||
};
|
||||
|
||||
lb_call_intrinsic(
|
||||
p,
|
||||
"llvm.memcpy.inline",
|
||||
args,
|
||||
gb_count_of(args),
|
||||
types,
|
||||
gb_count_of(types)
|
||||
);
|
||||
|
||||
return OdinLLVMBuildLoad(p, dst_type, ptr);
|
||||
} else {
|
||||
GB_ASSERT(p->decl_block != p->curr_block);
|
||||
GB_ASSERT(dst_size >= src_size);
|
||||
|
||||
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
|
||||
max_align = gb_max(max_align, 16);
|
||||
@@ -2729,6 +2814,14 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
|
||||
ignore_body = other_module != m;
|
||||
|
||||
lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body);
|
||||
if (missing_proc == nullptr) {
|
||||
// This is an unspecialized polymorphic procedure, which should not be codegen'd
|
||||
lbValue dummy = {};
|
||||
dummy.value = nullptr;
|
||||
dummy.type = nullptr;
|
||||
return dummy;
|
||||
}
|
||||
|
||||
if (ignore_body) {
|
||||
mutex_lock(&gen->anonymous_proc_lits_mutex);
|
||||
defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
|
||||
@@ -2921,25 +3014,7 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
|
||||
|
||||
lb_set_entity_from_other_modules_linkage_correctly(other_module, e, name);
|
||||
|
||||
if (e->Variable.thread_local_model != "") {
|
||||
LLVMSetThreadLocal(g.value, true);
|
||||
|
||||
String m = e->Variable.thread_local_model;
|
||||
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
|
||||
if (m == "default") {
|
||||
mode = LLVMGeneralDynamicTLSModel;
|
||||
} else if (m == "localdynamic") {
|
||||
mode = LLVMLocalDynamicTLSModel;
|
||||
} else if (m == "initialexec") {
|
||||
mode = LLVMInitialExecTLSModel;
|
||||
} else if (m == "localexec") {
|
||||
mode = LLVMLocalExecTLSModel;
|
||||
} else {
|
||||
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
|
||||
}
|
||||
LLVMSetThreadLocalMode(g.value, mode);
|
||||
}
|
||||
|
||||
lb_apply_thread_local_model(g.value, e->Variable.thread_local_model);
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,14 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu
|
||||
gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
|
||||
GB_ASSERT(entity != nullptr);
|
||||
GB_ASSERT(entity->kind == Entity_Procedure);
|
||||
// Skip codegen for unspecialized polymorphic procedures
|
||||
if (is_type_polymorphic(entity->type) && !entity->Procedure.is_foreign) {
|
||||
Type *bt = base_type(entity->type);
|
||||
if (bt->kind == Type_Proc && bt->Proc.is_polymorphic && !bt->Proc.is_poly_specialized) {
|
||||
// Do not generate code for unspecialized polymorphic procedures
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (!entity->Procedure.is_foreign) {
|
||||
if ((entity->flags & EntityFlag_ProcBodyChecked) == 0) {
|
||||
GB_PANIC("%.*s :: %s (was parapoly: %d %d)", LIT(entity->token.string), type_to_string(entity->type), is_type_polymorphic(entity->type, true), is_type_polymorphic(entity->type, false));
|
||||
@@ -783,8 +791,7 @@ gb_internal void lb_end_procedure_body(lbProcedure *p) {
|
||||
|
||||
p->curr_block = nullptr;
|
||||
p->state_flags = 0;
|
||||
}
|
||||
gb_internal void lb_end_procedure(lbProcedure *p) {
|
||||
|
||||
LLVMDisposeBuilder(p->builder);
|
||||
}
|
||||
|
||||
@@ -817,6 +824,10 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
lbProcedure *nested_proc = lb_create_procedure(p->module, e);
|
||||
if (nested_proc == nullptr) {
|
||||
// This is an unspecialized polymorphic procedure, skip codegen
|
||||
return;
|
||||
}
|
||||
e->code_gen_procedure = nested_proc;
|
||||
|
||||
lbValue value = {};
|
||||
@@ -2235,6 +2246,68 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
return lb_emit_load(p, tuple);
|
||||
}
|
||||
|
||||
case BuiltinProc_compress_values: {
|
||||
isize value_count = 0;
|
||||
for (Ast *arg : ce->args) {
|
||||
Type *t = arg->tav.type;
|
||||
if (is_type_tuple(t)) {
|
||||
value_count += t->Tuple.variables.count;
|
||||
} else {
|
||||
value_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (value_count == 1) {
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
x = lb_emit_conv(p, x, tv.type);
|
||||
return x;
|
||||
}
|
||||
|
||||
Type *dt = base_type(tv.type);
|
||||
lbAddr addr = lb_add_local_generated(p, tv.type, true);
|
||||
if (is_type_struct(dt) || is_type_tuple(dt)) {
|
||||
i32 index = 0;
|
||||
for (Ast *arg : ce->args) {
|
||||
lbValue x = lb_build_expr(p, arg);
|
||||
if (is_type_tuple(x.type)) {
|
||||
for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
|
||||
lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
|
||||
lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
|
||||
y = lb_emit_conv(p, y, type_deref(ptr.type));
|
||||
lb_emit_store(p, ptr, y);
|
||||
}
|
||||
} else {
|
||||
lbValue ptr = lb_emit_struct_ep(p, addr.addr, index++);
|
||||
x = lb_emit_conv(p, x, type_deref(ptr.type));
|
||||
lb_emit_store(p, ptr, x);
|
||||
}
|
||||
}
|
||||
GB_ASSERT(index == value_count);
|
||||
} else if (is_type_array_like(dt)) {
|
||||
i32 index = 0;
|
||||
for (Ast *arg : ce->args) {
|
||||
lbValue x = lb_build_expr(p, arg);
|
||||
if (is_type_tuple(x.type)) {
|
||||
for (isize i = 0; i < x.type->Tuple.variables.count; i++) {
|
||||
lbValue y = lb_emit_tuple_ev(p, x, cast(i32)i);
|
||||
lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
|
||||
y = lb_emit_conv(p, y, type_deref(ptr.type));
|
||||
lb_emit_store(p, ptr, y);
|
||||
}
|
||||
} else {
|
||||
lbValue ptr = lb_emit_array_epi(p, addr.addr, index++);
|
||||
x = lb_emit_conv(p, x, type_deref(ptr.type));
|
||||
lb_emit_store(p, ptr, x);
|
||||
}
|
||||
}
|
||||
GB_ASSERT(index == value_count);
|
||||
} else {
|
||||
GB_PANIC("TODO(bill): compress_values -> %s", type_to_string(tv.type));
|
||||
}
|
||||
|
||||
return lb_addr_load(p, addr);
|
||||
}
|
||||
|
||||
case BuiltinProc_min: {
|
||||
Type *t = type_of_expr(expr);
|
||||
if (ce->args.count == 2) {
|
||||
@@ -3375,6 +3448,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
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_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
|
||||
|
||||
|
||||
case BuiltinProc_constant_utf16_cstring:
|
||||
|
||||
+97
-25
@@ -136,7 +136,6 @@ gb_internal lbBranchBlocks lb_lookup_branch_blocks(lbProcedure *p, Ast *ident) {
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
gb_internal lbTargetList *lb_push_target_list(lbProcedure *p, Ast *label, lbBlock *break_, lbBlock *continue_, lbBlock *fallthrough_) {
|
||||
lbTargetList *tl = gb_alloc_item(permanent_allocator(), lbTargetList);
|
||||
tl->prev = p->target_list;
|
||||
@@ -688,6 +687,18 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
|
||||
lbBlock *body = lb_create_block(p, "for.interval.body");
|
||||
lbBlock *done = lb_create_block(p, "for.interval.done");
|
||||
|
||||
// TODO(tf2spi): This is inlined in more than several places.
|
||||
// Putting this in a function might be preferred.
|
||||
// LLVMSetCurrentDebugLocation2 has side effects,
|
||||
// so I didn't want to hide that before it got reviewed.
|
||||
if (rs->label != nullptr && p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "for.interval.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
|
||||
lb_add_debug_label(p, rs->label, label);
|
||||
}
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
@@ -893,6 +904,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
|
||||
|
||||
lbAddr index = lb_add_local_generated(p, t_int, false);
|
||||
|
||||
if (rs->label != nullptr && p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "for.soa.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
|
||||
lb_add_debug_label(p, rs->label, label);
|
||||
}
|
||||
if (!is_reverse) {
|
||||
/*
|
||||
for x, i in array {
|
||||
@@ -970,7 +989,6 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
|
||||
lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
|
||||
}
|
||||
|
||||
|
||||
lb_push_target_list(p, rs->label, done, loop, nullptr);
|
||||
|
||||
lb_build_stmt(p, rs->body);
|
||||
@@ -1029,6 +1047,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
|
||||
lbBlock *done = nullptr;
|
||||
bool is_map = false;
|
||||
|
||||
if (rs->label != nullptr && p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "for.range.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, rs->label));
|
||||
lb_add_debug_label(p, rs->label, label);
|
||||
}
|
||||
|
||||
if (tav.mode == Addressing_Type) {
|
||||
lb_build_range_enum(p, type_deref(tav.type), val0_type, &val, &key, &loop, &done);
|
||||
} else {
|
||||
@@ -1530,6 +1557,14 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
|
||||
gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
|
||||
lb_open_scope(p, scope);
|
||||
|
||||
if (ss->label != nullptr && p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "switch.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, ss->label));
|
||||
lb_add_debug_label(p, ss->label, label);
|
||||
}
|
||||
if (ss->init != nullptr) {
|
||||
lb_build_stmt(p, ss->init);
|
||||
}
|
||||
@@ -1736,6 +1771,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
|
||||
gb_internal void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, lbBlock *done) {
|
||||
ast_node(cc, CaseClause, clause);
|
||||
|
||||
// NOTE(tf2spi): Debug info for label not generated here on purpose
|
||||
lb_push_target_list(p, label, done, nullptr, nullptr);
|
||||
lb_build_stmt_list(p, cc->stmts);
|
||||
lb_close_scope(p, lbDeferExit_Default, body, clause);
|
||||
@@ -1986,33 +2022,43 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
|
||||
LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name);
|
||||
LLVMSetAlignment(global, cast(u32)type_align_of(e->type));
|
||||
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
|
||||
if (value.value != nullptr) {
|
||||
LLVMSetInitializer(global, value.value);
|
||||
}
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(global, true);
|
||||
}
|
||||
if (e->Variable.thread_local_model != "") {
|
||||
LLVMSetThreadLocal(global, true);
|
||||
|
||||
String m = e->Variable.thread_local_model;
|
||||
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
|
||||
if (m == "default") {
|
||||
mode = LLVMGeneralDynamicTLSModel;
|
||||
} else if (m == "localdynamic") {
|
||||
mode = LLVMLocalDynamicTLSModel;
|
||||
} else if (m == "initialexec") {
|
||||
mode = LLVMInitialExecTLSModel;
|
||||
} else if (m == "localexec") {
|
||||
mode = LLVMLocalExecTLSModel;
|
||||
} else {
|
||||
GB_PANIC("Unhandled thread local mode %.*s", LIT(m));
|
||||
}
|
||||
LLVMSetThreadLocalMode(global, mode);
|
||||
} else {
|
||||
if (!lb_apply_thread_local_model(global, e->Variable.thread_local_model)) {
|
||||
LLVMSetLinkage(global, LLVMInternalLinkage);
|
||||
}
|
||||
|
||||
if (value.value != nullptr) {
|
||||
if (is_type_any(e->type)) {
|
||||
Type *var_type = default_type(value.type);
|
||||
|
||||
gbString var_name = gb_string_make(temporary_allocator(), "__$static_any::");
|
||||
var_name = gb_string_append_length(var_name, mangled_name.text, mangled_name.len);
|
||||
|
||||
lbAddr var_global = lb_add_global_generated_with_name(p->module, var_type, value, make_string_c(var_name), nullptr);
|
||||
LLVMValueRef var_global_ref = var_global.addr.value;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var_global_ref, true);
|
||||
}
|
||||
|
||||
if (!lb_apply_thread_local_model(var_global_ref, e->Variable.thread_local_model)) {
|
||||
LLVMSetLinkage(var_global_ref, LLVMInternalLinkage);
|
||||
}
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_emit_conv(p, var_global.addr, t_rawptr).value,
|
||||
lb_typeid(p->module, var_type).value,
|
||||
};
|
||||
LLVMValueRef init = llvm_const_named_struct(p->module, e->type, vals, gb_count_of(vals));
|
||||
LLVMSetInitializer(global, init);
|
||||
} else {
|
||||
LLVMSetInitializer(global, value.value);
|
||||
}
|
||||
}
|
||||
|
||||
lbValue global_val = {global, alloc_type_pointer(e->type)};
|
||||
lb_add_entity(p->module, e, global_val);
|
||||
@@ -2307,6 +2353,14 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
|
||||
else_ = lb_create_block(p, "if.else");
|
||||
}
|
||||
if (is->label != nullptr) {
|
||||
if (p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "if.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, is->label));
|
||||
lb_add_debug_label(p, is->label, label);
|
||||
}
|
||||
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
|
||||
tl->is_block = true;
|
||||
}
|
||||
@@ -2399,12 +2453,19 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
|
||||
|
||||
lb_push_target_list(p, fs->label, done, post, nullptr);
|
||||
|
||||
if (fs->label != nullptr && p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "for.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->label));
|
||||
lb_add_debug_label(p, fs->label, label);
|
||||
}
|
||||
if (fs->init != nullptr) {
|
||||
#if 1
|
||||
lbBlock *init = lb_create_block(p, "for.init");
|
||||
lb_emit_jump(p, init);
|
||||
lb_start_block(p, init);
|
||||
#endif
|
||||
|
||||
lb_build_stmt(p, fs->init);
|
||||
}
|
||||
|
||||
@@ -2420,7 +2481,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
|
||||
lb_start_block(p, body);
|
||||
}
|
||||
|
||||
|
||||
lb_build_stmt(p, fs->body);
|
||||
|
||||
lb_pop_target_list(p);
|
||||
@@ -2694,9 +2754,21 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
|
||||
|
||||
|
||||
case_ast_node(bs, BlockStmt, node);
|
||||
lbBlock *body = nullptr;
|
||||
lbBlock *done = nullptr;
|
||||
if (bs->label != nullptr) {
|
||||
if (p->debug_info != nullptr) {
|
||||
lbBlock *label = lb_create_block(p, "block.label");
|
||||
lb_emit_jump(p, label);
|
||||
lb_start_block(p, label);
|
||||
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, bs->label));
|
||||
lb_add_debug_label(p, bs->label, label);
|
||||
}
|
||||
body = lb_create_block(p, "block.body");
|
||||
done = lb_create_block(p, "block.done");
|
||||
lb_emit_jump(p, body);
|
||||
lb_start_block(p, body);
|
||||
lbTargetList *tl = lb_push_target_list(p, bs->label, done, nullptr, nullptr);
|
||||
tl->is_block = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
|
||||
gb_internal void lb_set_odin_rtti_section(LLVMValueRef value) {
|
||||
if (build_context.metrics.os != TargetOs_darwin) {
|
||||
LLVMSetSection(value, ".odinti");
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal isize lb_type_info_index(CheckerInfo *info, TypeInfoPair pair, bool err_on_not_found=true) {
|
||||
isize index = type_info_index(info, pair, err_on_not_found);
|
||||
if (index >= 0) {
|
||||
@@ -221,6 +227,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
gb_snprintf(name, 63, "__$ti-%lld", cast(long long)index);
|
||||
LLVMValueRef g = LLVMAddGlobal(m->mod, type, name);
|
||||
lb_make_global_private_const(g);
|
||||
lb_set_odin_rtti_section(g);
|
||||
return g;
|
||||
};
|
||||
|
||||
@@ -716,6 +723,8 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
LLVMSetInitializer(value_array.value, value_init);
|
||||
LLVMSetGlobalConstant(name_array.value, true);
|
||||
LLVMSetGlobalConstant(value_array.value, true);
|
||||
lb_set_odin_rtti_section(name_array.value);
|
||||
lb_set_odin_rtti_section(value_array.value);
|
||||
|
||||
lbValue v_count = lb_const_int(m, t_int, fields.count);
|
||||
|
||||
@@ -1056,6 +1065,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
|
||||
LLVMValueRef giant_array = lb_global_type_info_data_ptr(m).value;
|
||||
LLVMSetInitializer(giant_array, giant_const);
|
||||
lb_make_global_private_const(giant_array);
|
||||
lb_set_odin_rtti_section(giant_array);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2125,7 +2125,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, Stri
|
||||
return addr;
|
||||
}
|
||||
|
||||
gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
|
||||
gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name, Type *class_impl_type) {
|
||||
lbModule *m = p->module;
|
||||
lbAddr *found = string_map_get(&m->objc_classes, name);
|
||||
if (found) {
|
||||
@@ -2148,13 +2148,75 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String
|
||||
} else {
|
||||
LLVMSetLinkage(g.value, LLVMExternalLinkage);
|
||||
}
|
||||
mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class});
|
||||
mpsc_enqueue(&m->gen->objc_classes, lbObjCGlobal{m, global_name, name, t_objc_Class, class_impl_type});
|
||||
|
||||
lbAddr addr = lb_addr(g);
|
||||
string_map_set(&m->objc_classes, name, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) {
|
||||
|
||||
String name = self_type->Named.type_name->TypeName.objc_class_name;
|
||||
GB_ASSERT(name != "");
|
||||
|
||||
lbAddr *found = string_map_get(&m->objc_ivars, name);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
|
||||
lbModule *default_module = &m->gen->default_module;
|
||||
|
||||
gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::");
|
||||
global_name = gb_string_append_length(global_name, name.text, name.len);
|
||||
|
||||
// Create a global variable to store offset of the ivar in an instance of an object
|
||||
LLVMTypeRef t = lb_type(m, t_int);
|
||||
|
||||
lbValue g = {};
|
||||
g.value = LLVMAddGlobal(m->mod, t, global_name);
|
||||
g.type = t_int_ptr;
|
||||
|
||||
if (default_module == m) {
|
||||
LLVMSetInitializer(g.value, LLVMConstInt(t, 0, true));
|
||||
lb_add_member(m, make_string_c(global_name), g);
|
||||
} else {
|
||||
LLVMSetLinkage(g.value, LLVMExternalLinkage);
|
||||
}
|
||||
|
||||
mpsc_enqueue(&m->gen->objc_ivars, lbObjCGlobal{m, global_name, name, t_int, self_type});
|
||||
|
||||
lbAddr addr = lb_addr(g);
|
||||
string_map_set(&m->objc_ivars, name, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p, lbValue self) {
|
||||
GB_ASSERT(self.type->kind == Type_Pointer && self.type->Pointer.elem->kind == Type_Named);
|
||||
|
||||
Type *self_type = self.type->Pointer.elem;
|
||||
|
||||
lbValue self_uptr = lb_emit_conv(p, self, t_uintptr);
|
||||
|
||||
lbValue ivar_offset = lb_addr_load(p, lb_handle_objc_find_or_register_ivar(p->module, self_type));
|
||||
lbValue ivar_offset_uptr = lb_emit_conv(p, ivar_offset, t_uintptr);
|
||||
|
||||
|
||||
lbValue ivar_uptr = lb_emit_arith(p, Token_Add, self_uptr, ivar_offset_uptr, t_uintptr);
|
||||
|
||||
Type *ivar_type = self_type->Named.type_name->TypeName.objc_ivar;
|
||||
return lb_emit_conv(p, ivar_uptr, alloc_type_pointer(ivar_type));
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
|
||||
ast_node(ce, CallExpr, expr);
|
||||
|
||||
GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer);
|
||||
lbValue self = lb_build_expr(p, ce->args[0]);
|
||||
|
||||
return lb_handle_objc_ivar_for_objc_object_pointer(p, self);
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
|
||||
ast_node(ce, CallExpr, expr);
|
||||
|
||||
@@ -2188,7 +2250,7 @@ gb_internal lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
|
||||
auto tav = ce->args[0]->tav;
|
||||
GB_ASSERT(tav.value.kind == ExactValue_String);
|
||||
String name = tav.value.value_string;
|
||||
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
|
||||
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, nullptr));
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
|
||||
@@ -2198,7 +2260,7 @@ gb_internal lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
|
||||
auto tav = ce->args[0]->tav;
|
||||
GB_ASSERT(tav.value.kind == ExactValue_String);
|
||||
String name = tav.value.value_string;
|
||||
lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
|
||||
lbAddr dst = lb_handle_objc_find_or_register_class(p, name, nullptr);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
args[0] = lb_const_nil(m, t_objc_Class);
|
||||
@@ -2220,7 +2282,9 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
|
||||
GB_ASSERT(e->kind == Entity_TypeName);
|
||||
String name = e->TypeName.objc_class_name;
|
||||
|
||||
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
|
||||
Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr;
|
||||
|
||||
return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type));
|
||||
}
|
||||
|
||||
return lb_build_expr(p, expr);
|
||||
@@ -2266,9 +2330,6 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
|
||||
return lb_emit_call(p, the_proc, args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
|
||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||
i64 v = exact_value_to_i64(value);
|
||||
|
||||
+150
-72
@@ -278,10 +278,10 @@ gb_internal void usage(String argv0, String argv1 = {}) {
|
||||
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, "bundle Bundles a directory in a specific layout for that platform.");
|
||||
print_usage_line(1, "check Parses, and type checks a directory of .odin files.");
|
||||
print_usage_line(1, "check Parses and type checks a directory of .odin files.");
|
||||
print_usage_line(1, "strip-semicolon Parses, type checks, and removes unneeded semicolons from the entire program.");
|
||||
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
|
||||
print_usage_line(1, "doc Generates documentation on a directory of .odin files.");
|
||||
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
|
||||
print_usage_line(1, "version Prints version.");
|
||||
print_usage_line(1, "report Prints information useful to reporting a bug.");
|
||||
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
|
||||
@@ -319,6 +319,7 @@ enum BuildFlagKind {
|
||||
BuildFlag_NoBoundsCheck,
|
||||
BuildFlag_NoTypeAssert,
|
||||
BuildFlag_NoDynamicLiterals,
|
||||
BuildFlag_DynamicLiterals,
|
||||
BuildFlag_NoCRT,
|
||||
BuildFlag_NoRPath,
|
||||
BuildFlag_NoEntryPoint,
|
||||
@@ -538,6 +539,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_NoTypeAssert, str_lit("no-type-assert"), BuildFlagParam_None, Command__does_check);
|
||||
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_DynamicLiterals, str_lit("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);
|
||||
@@ -1207,6 +1209,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_NoDynamicLiterals:
|
||||
gb_printf_err("Warning: Use of -no-dynamic-literals is now redundant\n");
|
||||
break;
|
||||
case BuildFlag_DynamicLiterals:
|
||||
build_context.dynamic_literals = true;
|
||||
break;
|
||||
case BuildFlag_NoCRT:
|
||||
build_context.no_crt = true;
|
||||
break;
|
||||
@@ -2221,20 +2226,30 @@ gb_internal void remove_temp_files(lbGenerator *gen) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal void print_show_help(String const arg0, String command, String optional_flag = {}) {
|
||||
gb_internal int print_show_help(String const arg0, String command, String optional_flag = {}) {
|
||||
bool help_resolved = false;
|
||||
bool printed_usage_header = false;
|
||||
bool printed_flags_header = false;
|
||||
|
||||
if (command == "help" && optional_flag.len != 0 && optional_flag[0] != '-') {
|
||||
command = optional_flag;
|
||||
optional_flag = {};
|
||||
}
|
||||
|
||||
print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
|
||||
print_usage_line(0, "Usage:");
|
||||
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
|
||||
print_usage_line(0, "");
|
||||
defer (print_usage_line(0, ""));
|
||||
|
||||
auto const print_usage_header_once = [&help_resolved, &printed_usage_header, arg0, command]() {
|
||||
if (printed_usage_header) {
|
||||
return;
|
||||
}
|
||||
print_usage_line(0, "%.*s is a tool for managing Odin source code.", LIT(arg0));
|
||||
print_usage_line(0, "Usage:");
|
||||
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
|
||||
print_usage_line(0, "");
|
||||
help_resolved = true;
|
||||
printed_usage_header = true;
|
||||
};
|
||||
|
||||
if (command == "build") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "build Compiles directory of .odin files as an executable.");
|
||||
print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
|
||||
print_usage_line(2, "Use `-file` to build a single file instead.");
|
||||
@@ -2243,6 +2258,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
print_usage_line(3, "odin build <dir> Builds package in <dir>.");
|
||||
print_usage_line(3, "odin build filename.odin -file Builds single-file package, must contain entry point.");
|
||||
} else if (command == "run") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
|
||||
print_usage_line(2, "Append an empty flag and then the args, '-- <args>', to specify args for the output.");
|
||||
print_usage_line(2, "Examples:");
|
||||
@@ -2250,28 +2266,40 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
print_usage_line(3, "odin run <dir> Builds and runs package in <dir>.");
|
||||
print_usage_line(3, "odin run filename.odin -file Builds and runs single-file package, must contain entry point.");
|
||||
} else if (command == "check") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "check Parses and type checks directory of .odin files.");
|
||||
print_usage_line(2, "Examples:");
|
||||
print_usage_line(3, "odin check . Type checks package in current directory.");
|
||||
print_usage_line(3, "odin check <dir> Type checks package in <dir>.");
|
||||
print_usage_line(3, "odin check filename.odin -file Type checks single-file package, must contain entry point.");
|
||||
} else if (command == "test") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "test Builds and runs procedures with the attribute @(test) in the initial package.");
|
||||
} else if (command == "doc") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "doc Generates documentation from a directory of .odin files.");
|
||||
print_usage_line(2, "Examples:");
|
||||
print_usage_line(3, "odin doc . Generates documentation on package in current directory.");
|
||||
print_usage_line(3, "odin doc <dir> Generates documentation on package in <dir>.");
|
||||
print_usage_line(3, "odin doc filename.odin -file Generates documentation on single-file package.");
|
||||
} else if (command == "version") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "version Prints version.");
|
||||
} else if (command == "strip-semicolon") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "strip-semicolon");
|
||||
print_usage_line(2, "Parses and type checks .odin file(s) and then removes unneeded semicolons from the entire project.");
|
||||
} else if (command == "bundle") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "bundle <platform> Bundles a directory in a specific layout for that platform");
|
||||
print_usage_line(2, "Supported platforms:");
|
||||
print_usage_line(3, "android");
|
||||
} else if (command == "report") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "report Prints information useful to reporting a bug.");
|
||||
} else if (command == "root") {
|
||||
print_usage_header_once();
|
||||
print_usage_line(1, "root Prints the root path where Odin looks for the builtin collections.");
|
||||
}
|
||||
|
||||
bool doc = command == "doc";
|
||||
@@ -2293,13 +2321,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
check = true;
|
||||
}
|
||||
|
||||
print_usage_line(0, "");
|
||||
print_usage_line(1, "Flags");
|
||||
print_usage_line(0, "");
|
||||
|
||||
|
||||
|
||||
auto const print_flag = [&optional_flag](char const *flag) -> bool {
|
||||
auto const print_flag = [&optional_flag, &help_resolved, &printed_flags_header, print_usage_header_once](char const *flag) -> bool {
|
||||
if (optional_flag.len != 0) {
|
||||
String f = make_string_c(flag);
|
||||
isize i = string_index_byte(f, ':');
|
||||
@@ -2310,6 +2335,14 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
return false;
|
||||
}
|
||||
}
|
||||
print_usage_header_once();
|
||||
if (!printed_flags_header) {
|
||||
print_usage_line(0, "");
|
||||
print_usage_line(1, "Flags");
|
||||
print_usage_line(0, "");
|
||||
printed_flags_header = true;
|
||||
}
|
||||
help_resolved = true;
|
||||
print_usage_line(0, "");
|
||||
print_usage_line(1, flag);
|
||||
return true;
|
||||
@@ -2331,20 +2364,20 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-build-mode:<mode>")) {
|
||||
print_usage_line(2, "Sets the build mode.");
|
||||
print_usage_line(2, "Available options:");
|
||||
print_usage_line(3, "-build-mode:exe Builds as an executable.");
|
||||
print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
|
||||
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
|
||||
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
|
||||
print_usage_line(3, "-build-mode:obj Builds as an object file.");
|
||||
print_usage_line(3, "-build-mode:object Builds as an object file.");
|
||||
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
|
||||
print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
|
||||
print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
|
||||
print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
|
||||
print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
|
||||
print_usage_line(3, "-build-mode:exe Builds as an executable.");
|
||||
print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
|
||||
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library.");
|
||||
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
|
||||
print_usage_line(3, "-build-mode:static Builds as a statically linked library.");
|
||||
print_usage_line(3, "-build-mode:obj Builds as an object file.");
|
||||
print_usage_line(3, "-build-mode:object Builds as an object file.");
|
||||
print_usage_line(3, "-build-mode:assembly Builds as an assembly file.");
|
||||
print_usage_line(3, "-build-mode:assembler Builds as an assembly file.");
|
||||
print_usage_line(3, "-build-mode:asm Builds as an assembly file.");
|
||||
print_usage_line(3, "-build-mode:llvm-ir Builds as an LLVM IR file.");
|
||||
print_usage_line(3, "-build-mode:llvm Builds as an LLVM IR file.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2353,16 +2386,16 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
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(3, "import \"shared:foo\"");
|
||||
}
|
||||
|
||||
if (print_flag("-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(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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2385,7 +2418,7 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
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(3, "#config(SPAM, default_value)");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2420,9 +2453,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (check) {
|
||||
if (print_flag("-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(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'.)");
|
||||
}
|
||||
|
||||
if (print_flag("-export-defineables:<filename>")) {
|
||||
@@ -2433,8 +2466,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-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(3, "-export-dependencies:make Exports in Makefile format");
|
||||
print_usage_line(3, "-export-dependencies:json Exports in JSON format");
|
||||
}
|
||||
|
||||
if (print_flag("-export-dependencies-file:<filename>")) {
|
||||
@@ -2445,8 +2478,8 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-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(3, "-export-timings:json Exports compile time stats to JSON.");
|
||||
print_usage_line(3, "-export-timings:csv Exports compile time stats to CSV.");
|
||||
}
|
||||
|
||||
if (print_flag("-export-timings-file:<filename>")) {
|
||||
@@ -2536,9 +2569,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-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(3, "-microarch:sandybridge");
|
||||
print_usage_line(3, "-microarch:native");
|
||||
print_usage_line(3, "-microarch:\"?\" for a list");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2595,10 +2628,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-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");
|
||||
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)");
|
||||
}
|
||||
@@ -2649,10 +2682,10 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-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(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");
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
@@ -2667,9 +2700,9 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-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(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.");
|
||||
}
|
||||
}
|
||||
@@ -2730,17 +2763,32 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
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(3, "-subsystem:console");
|
||||
print_usage_line(3, "-subsystem:windows");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (build) {
|
||||
if (print_flag("-subtarget:<subtarget>")) {
|
||||
print_usage_line(2, "[Darwin and Linux only]");
|
||||
print_usage_line(2, "Available subtargets:");
|
||||
String prefix = str_lit("-subtarget:");
|
||||
for (u32 i = 1; i < Subtarget_COUNT; i++) {
|
||||
String name = subtarget_strings[i];
|
||||
String help_string = concatenate_strings(temporary_allocator(), prefix, name);
|
||||
print_usage_line(3, (const char *)help_string.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (run_or_build) {
|
||||
if (print_flag("-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(3, "-target-features:atomics");
|
||||
print_usage_line(3, "-target-features:\"sse2,aes\"");
|
||||
print_usage_line(3, "-target-features:\"?\" for a list");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2777,11 +2825,11 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
if (print_flag("-vet")) {
|
||||
print_usage_line(2, "Does extra checks on the code.");
|
||||
print_usage_line(2, "Extra checks include:");
|
||||
print_usage_line(3, "-vet-unused");
|
||||
print_usage_line(3, "-vet-unused-variables");
|
||||
print_usage_line(3, "-vet-unused-imports");
|
||||
print_usage_line(3, "-vet-shadowing");
|
||||
print_usage_line(3, "-vet-using-stmt");
|
||||
print_usage_line(3, "-vet-unused");
|
||||
print_usage_line(3, "-vet-unused-variables");
|
||||
print_usage_line(3, "-vet-unused-imports");
|
||||
print_usage_line(3, "-vet-shadowing");
|
||||
print_usage_line(3, "-vet-using-stmt");
|
||||
}
|
||||
|
||||
if (print_flag("-vet-cast")) {
|
||||
@@ -2867,6 +2915,21 @@ gb_internal void print_show_help(String const arg0, String command, String optio
|
||||
print_usage_line(2, "If this is omitted, the terminal will prompt you to provide it.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!help_resolved) {
|
||||
usage(arg0);
|
||||
print_usage_line(0, "");
|
||||
if (command == "help") {
|
||||
print_usage_line(0, "'%.*s' is not a recognized flag.", LIT(optional_flag));
|
||||
} else {
|
||||
print_usage_line(0, "'%.*s' is not a recognized command.", LIT(command));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_usage_line(0, "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gb_internal void print_show_unused(Checker *c) {
|
||||
@@ -3239,6 +3302,16 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
String run_args_string = {};
|
||||
isize last_non_run_arg = args.count;
|
||||
|
||||
for_array(i, args) {
|
||||
if (args[i] == "--") {
|
||||
break;
|
||||
}
|
||||
if (args[i] == "-help" || args[i] == "--help") {
|
||||
build_context.show_help = true;
|
||||
return print_show_help(args[0], command);
|
||||
}
|
||||
}
|
||||
|
||||
bool run_output = false;
|
||||
if (command == "run" || command == "test") {
|
||||
if (args.count < 3) {
|
||||
@@ -3332,6 +3405,10 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
return 1;
|
||||
#endif
|
||||
} else if (command == "version") {
|
||||
if (args.count != 2) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
build_context.command_kind = Command_version;
|
||||
gb_printf("%.*s version %.*s", LIT(args[0]), LIT(ODIN_VERSION));
|
||||
|
||||
@@ -3346,6 +3423,10 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
gb_printf("\n");
|
||||
return 0;
|
||||
} else if (command == "report") {
|
||||
if (args.count != 2) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
build_context.command_kind = Command_bug_report;
|
||||
print_bug_report_help();
|
||||
return 0;
|
||||
@@ -3354,8 +3435,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
} else {
|
||||
print_show_help(args[0], args[1], args[2]);
|
||||
return 0;
|
||||
return print_show_help(args[0], args[1], args[2]);
|
||||
}
|
||||
} else if (command == "bundle") {
|
||||
if (args.count < 4) {
|
||||
@@ -3371,6 +3451,10 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
init_filename = args[3];
|
||||
} else if (command == "root") {
|
||||
if (args.count != 2) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
gb_printf("%.*s", LIT(odin_root_dir()));
|
||||
return 0;
|
||||
} else if (command == "clear-cache") {
|
||||
@@ -3386,11 +3470,6 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
|
||||
init_filename = copy_string(permanent_allocator(), init_filename);
|
||||
|
||||
if (init_filename == "-help" ||
|
||||
init_filename == "--help") {
|
||||
build_context.show_help = true;
|
||||
}
|
||||
|
||||
if (init_filename.len > 0 && !build_context.show_help) {
|
||||
// The command must be build, run, test, check, or another that takes a directory or filename.
|
||||
if (!path_is_directory(init_filename)) {
|
||||
@@ -3441,8 +3520,7 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
|
||||
if (build_context.show_help) {
|
||||
print_show_help(args[0], command);
|
||||
return 0;
|
||||
return print_show_help(args[0], command);
|
||||
}
|
||||
|
||||
if (command == "bundle") {
|
||||
|
||||
@@ -756,8 +756,12 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
|
||||
type_writer_appendc(w, "/");
|
||||
write_type_to_canonical_string(w, type->Generic.specialized);
|
||||
}
|
||||
} else if (type->Generic.specialized) {
|
||||
// If we have a specialized type, use that instead of panicking
|
||||
write_type_to_canonical_string(w, type->Generic.specialized);
|
||||
} else {
|
||||
GB_PANIC("Type_Generic should never be hit");
|
||||
// For unspecialized generics, use a generic placeholder string
|
||||
type_writer_appendc(w, "rawptr");
|
||||
}
|
||||
return;
|
||||
|
||||
|
||||
@@ -729,10 +729,12 @@ gb_global Type *t_map_set_proc = nullptr;
|
||||
gb_global Type *t_objc_object = nullptr;
|
||||
gb_global Type *t_objc_selector = nullptr;
|
||||
gb_global Type *t_objc_class = nullptr;
|
||||
gb_global Type *t_objc_ivar = nullptr;
|
||||
|
||||
gb_global Type *t_objc_id = nullptr;
|
||||
gb_global Type *t_objc_SEL = nullptr;
|
||||
gb_global Type *t_objc_Class = nullptr;
|
||||
gb_global Type *t_objc_Ivar = nullptr;
|
||||
|
||||
enum OdinAtomicMemoryOrder : i32 {
|
||||
OdinAtomicMemoryOrder_relaxed = 0, // unordered
|
||||
@@ -872,6 +874,29 @@ gb_internal Type *base_type(Type *t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
gb_internal Type *base_named_type(Type *t) {
|
||||
if (t->kind != Type_Named) {
|
||||
return t_invalid;
|
||||
}
|
||||
|
||||
Type *prev_named = t;
|
||||
t = t->Named.base;
|
||||
for (;;) {
|
||||
if (t == nullptr) {
|
||||
break;
|
||||
}
|
||||
if (t->kind != Type_Named) {
|
||||
break;
|
||||
}
|
||||
if (t == t->Named.base) {
|
||||
return t_invalid;
|
||||
}
|
||||
prev_named = t;
|
||||
t = t->Named.base;
|
||||
}
|
||||
return prev_named;
|
||||
}
|
||||
|
||||
gb_internal Type *base_enum_type(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
if (bt != nullptr &&
|
||||
@@ -2932,6 +2957,10 @@ gb_internal Type *default_type(Type *type) {
|
||||
case Basic_UntypedString: return t_string;
|
||||
case Basic_UntypedRune: return t_rune;
|
||||
}
|
||||
} else if (type->kind == Type_Generic) {
|
||||
if (type->Generic.specialized) {
|
||||
return default_type(type->Generic.specialized);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
@@ -3327,6 +3356,15 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type *objc_ivar_type = e->TypeName.objc_ivar;
|
||||
if (objc_ivar_type != nullptr) {
|
||||
sel = lookup_field_with_selection(objc_ivar_type, field_name, false, sel, allow_blank_ident);
|
||||
if (sel.entity != nullptr) {
|
||||
sel.pseudo_field = true;
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_polymorphic(type)) {
|
||||
|
||||
Reference in New Issue
Block a user