Skip to content

Commit 2e4837b

Browse files
committed
Merge branch 'master' of https://github.com/odin-lang/Odin
2 parents b6944b8 + d612f6e commit 2e4837b

File tree

14 files changed

+936
-84
lines changed

14 files changed

+936
-84
lines changed

base/intrinsics/intrinsics.odin

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
384384
objc_find_class :: proc($name: string) -> objc_Class ---
385385
objc_register_class :: proc($name: string) -> objc_Class ---
386386
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
387+
objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) ---
387388

388389
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
389390

base/runtime/core_builtin.odin

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import "base:intrinsics"
55
@builtin
66
Maybe :: union($T: typeid) {T}
77

8+
/*
9+
Represents an Objective-C block with a given procedure signature T
10+
*/
11+
@builtin
12+
Objc_Block :: struct($T: typeid) where intrinsics.type_is_proc(T) { using _: intrinsics.objc_object }
813

914
/*
1015
Recovers the containing/parent struct from a pointer to one of its fields.

base/runtime/procs_darwin.odin

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
#+private
22
package runtime
33

4-
@(priority_index=-1e6)
4+
@(priority_index=-1e5)
55
foreign import ObjC "system:objc"
66

7+
@(priority_index=-1e6)
8+
foreign import libSystem "system:System"
9+
710
import "base:intrinsics"
811

912
objc_id :: ^intrinsics.objc_object
@@ -34,3 +37,10 @@ foreign ObjC {
3437
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
3538
}
3639

40+
foreign libSystem {
41+
_NSConcreteGlobalBlock: intrinsics.objc_class
42+
_NSConcreteStackBlock: intrinsics.objc_class
43+
44+
_Block_object_assign :: proc "c" (rawptr, rawptr, i32) ---
45+
_Block_object_dispose :: proc "c" (rawptr, i32) ---
46+
}

src/check_builtin.cpp

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,229 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
457457
return true;
458458

459459
} break;
460+
461+
case BuiltinProc_objc_block:
462+
{
463+
// NOTE(harold): The last argument specified in the call is the handler proc,
464+
// any other arguments before it are capture by-copy arguments.
465+
auto param_operands = slice_make<Operand>(permanent_allocator(), ce->args.count);
466+
467+
isize capture_arg_count = ce->args.count - 1;
468+
469+
// NOTE(harold): The first parameter is already checked at check_builtin_procedure().
470+
// Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc.
471+
param_operands[0] = *operand;
472+
473+
for (isize i = 0; i < ce->args.count-1; i++) {
474+
Operand x = {};
475+
check_expr(c, &x, ce->args[i]);
476+
477+
switch (x.mode) {
478+
case Addressing_Value:
479+
case Addressing_Context:
480+
case Addressing_Variable:
481+
case Addressing_Constant:
482+
param_operands[i] = x;
483+
break;
484+
485+
default:
486+
gbString e = expr_to_string(x.expr);
487+
gbString t = type_to_string(x.type);
488+
error(x.expr, "'%.*s' capture arguments must be values, but got %s of type %s", LIT(builtin_name), e, t);
489+
gb_string_free(t);
490+
gb_string_free(e);
491+
return false;
492+
}
493+
}
494+
495+
// Validate handler proc
496+
Operand handler = {};
497+
498+
if (capture_arg_count == 0) {
499+
// It's already been checked and assigned
500+
handler = param_operands[0];
501+
} else {
502+
check_expr_or_type(c, &handler, ce->args[capture_arg_count]);
503+
param_operands[capture_arg_count] = handler;
504+
}
505+
506+
if (!is_operand_value(handler) || handler.type->kind != Type_Proc) {
507+
gbString e = expr_to_string(handler.expr);
508+
gbString t = type_to_string(handler.type);
509+
error(handler.expr, "'%.*s' expected a procedure, but got '%s' of type %s", LIT(builtin_name), e, t);
510+
gb_string_free(t);
511+
gb_string_free(e);
512+
return false;
513+
}
514+
515+
Ast *handler_node = unparen_expr(handler.expr);
516+
517+
// Only direct reference to procs are allowed
518+
switch (handler_node->kind) {
519+
case Ast_ProcLit: break; // ok
520+
case Ast_Ident: {
521+
auto& ident = handler_node->Ident;
522+
523+
if (ident.entity == nullptr) {
524+
error(handler.expr, "'%.*s' failed to resolve entity from expression", LIT(builtin_name));
525+
return false;
526+
}
527+
528+
if (ident.entity->kind != Entity_Procedure) {
529+
gbString e = expr_to_string(handler_node);
530+
531+
ERROR_BLOCK();
532+
error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name));
533+
if(ident.entity->kind == Entity_Variable) {
534+
error_line("\tSuggestion: Variables referencing a procedure are not allowed, they are not a direct procedure reference.");
535+
} else {
536+
error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e); // NOTE(harold): Is this case possible to hit?
537+
}
538+
error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name));
539+
540+
gb_string_free(e);
541+
return false;
542+
}
543+
} break;
544+
545+
default: {
546+
gbString e = expr_to_string(handler_node);
547+
ERROR_BLOCK();
548+
error(handler.expr, "'%.*s' expected a direct reference to a procedure", LIT(builtin_name));
549+
if( handler_node->kind == Ast_CallExpr) {
550+
error_line("\tSuggestion: Do not use a procedure returned from another procedure.");
551+
} else {
552+
error_line("\tSuggestion: Ensure '%s' is not a runtime-evaluated expression.", e);
553+
}
554+
error_line("\n\t Refer to a procedure directly by its name or declare it anonymously: %.*s(proc(){})", LIT(builtin_name));
555+
556+
gb_string_free(e);
557+
} return false;
558+
} // End switch
559+
560+
auto& handler_type_proc = handler.type->Proc;
561+
562+
if (capture_arg_count > handler_type_proc.param_count) {
563+
error(handler.expr, "'%.*s' captured arguments exceeded the handler's parameter count", LIT(builtin_name));
564+
return false;
565+
}
566+
567+
// If the handler proc is odin calling convention, but there must be a context defined in this scope.
568+
if (handler_type_proc.calling_convention == ProcCC_Odin) {
569+
if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
570+
ERROR_BLOCK();
571+
error(handler.expr, "The handler procedure for '%.*s' requires a context, but no context is defined in the current scope", LIT(builtin_name));
572+
error_line("\tSuggestion: 'context = runtime.default_context()', or use the \"c\" calling convention for the handler procedure");
573+
return false;
574+
}
575+
}
576+
577+
// At most a single return value is supported
578+
if (handler_type_proc.result_count > 1) {
579+
error(handler_type_proc.node->ProcType.results, "Handler procedures for '%.*s' cannot have multiple return values", LIT(builtin_name));
580+
return false;
581+
}
582+
583+
// Ensure that captured args are assignable to the handler's corresponding capture params
584+
if (handler_type_proc.param_count > 0) {
585+
auto& handler_param_types = handler.type->Proc.params->Tuple.variables;
586+
Slice<Entity *> handler_capture_param_types = slice(handler_param_types, handler_param_types.count - capture_arg_count, handler_param_types.count);
587+
588+
for (isize i = 0; i < capture_arg_count; i++) {
589+
Operand op = param_operands[i];
590+
if (!check_is_assignable_to(c, &op, handler_capture_param_types[i]->type)) {
591+
gbString e = expr_to_string(op.expr);
592+
gbString src = type_to_string(op.type);
593+
gbString dst = type_to_string(handler_capture_param_types[i]->type);
594+
error(op.expr, "'%.*s' captured value '%s' of type '%s' is not assignable to type '%s'", LIT(builtin_name), e, src, dst);
595+
gb_string_free(e);
596+
gb_string_free(src);
597+
gb_string_free(dst);
598+
return false;
599+
}
600+
}
601+
}
602+
603+
ProcCallingConvention cc = handler_type_proc.calling_convention;
604+
switch (cc) {
605+
case ProcCC_Odin:
606+
case ProcCC_Contextless:
607+
case ProcCC_CDecl:
608+
break; // ok
609+
default:
610+
ERROR_BLOCK();
611+
612+
error(handler.expr, "'%.*s' Invalid calling convention for block procedure.", LIT(builtin_name));
613+
error_line("\tSuggestion: Do not specify a calling convention ot else use \"c\" or \"cotextless\"");
614+
return false;
615+
}
616+
617+
if (handler_type_proc.is_polymorphic) {
618+
error(handler.expr, "'%.*s' Unspecialized polymorphic procedures are not allowed.", LIT(builtin_name));
619+
return false;
620+
}
621+
622+
// Create the specialized Objc_Block type that this intrinsic will return
623+
Token ident = {};
624+
ident.kind = Token_Ident;
625+
ident.string = str_lit("Objc_Block");
626+
ident.pos = ast_token(call).pos;
627+
628+
Token l_paren = {};
629+
l_paren.kind = Token_OpenParen;
630+
l_paren.string = str_lit("(");
631+
l_paren.pos = ident.pos;
632+
633+
Token r_paren = {};
634+
r_paren.kind = Token_CloseParen;
635+
l_paren.string = str_lit(")");
636+
r_paren.pos = ident.pos;
637+
638+
// Remove the capture args from the resulting Objc_Block type signature
639+
Ast* handler_proc_type_copy = clone_ast(handler_type_proc.node);
640+
handler_proc_type_copy->ProcType.params->FieldList.list.count -= capture_arg_count;
641+
642+
// Make sure the Objc_Block's specialized proc is always "c" calling conv,
643+
// even if we have a context, as the invoker is always "c".
644+
// This allows us to have compatibility with the target block types with either calling convention used.
645+
handler_proc_type_copy->ProcType.calling_convention = ProcCC_CDecl;
646+
647+
Array<Ast *> poly_args = {};
648+
array_init(&poly_args, permanent_allocator(), 1, 1);
649+
poly_args[0] = handler_proc_type_copy;
650+
651+
652+
Type *t_Objc_Block = find_core_type(c->checker, str_lit("Objc_Block"));
653+
Operand poly_op = {};
654+
poly_op.type = t_Objc_Block;
655+
poly_op.mode = Addressing_Type;
656+
657+
Ast *poly_call = ast_call_expr(nullptr, ast_ident(nullptr, ident), poly_args, l_paren, r_paren, {});
658+
659+
auto err = check_polymorphic_record_type(c, &poly_op, poly_call);
660+
661+
if (err != 0) {
662+
operand->mode = Addressing_Invalid;
663+
operand->type = t_invalid;
664+
error(handler.expr, "'%.*s' failed to determine resulting Objc_Block handler procedure", LIT(builtin_name));
665+
return false;
666+
}
667+
668+
GB_ASSERT(poly_op.type != t_Objc_Block);
669+
GB_ASSERT(poly_op.mode == Addressing_Type);
670+
671+
bool is_global_block = capture_arg_count == 0 && handler_type_proc.calling_convention != ProcCC_Odin;
672+
if (is_global_block) {
673+
try_to_add_package_dependency(c, "runtime", "_NSConcreteGlobalBlock");
674+
} else {
675+
try_to_add_package_dependency(c, "runtime", "_NSConcreteStackBlock");
676+
}
677+
678+
*operand = poly_op;
679+
operand->type = alloc_type_pointer(operand->type);
680+
operand->mode = Addressing_Value;
681+
return true;
682+
} break;
460683
}
461684
}
462685

@@ -2291,6 +2514,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
22912514
case BuiltinProc_objc_register_selector:
22922515
case BuiltinProc_objc_register_class:
22932516
case BuiltinProc_objc_ivar_get:
2517+
case BuiltinProc_objc_block:
22942518
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
22952519

22962520
case BuiltinProc___entry_point:

src/checker.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
14601460
mpsc_destroy(&i->foreign_decls_to_check);
14611461

14621462
map_destroy(&i->objc_msgSend_types);
1463+
string_set_destroy(&i->obcj_class_name_set);
1464+
mpsc_destroy(&i->objc_class_implementations);
1465+
map_destroy(&i->objc_method_implementations);
1466+
14631467
string_map_destroy(&i->load_file_cache);
14641468
string_map_destroy(&i->load_directory_cache);
14651469
map_destroy(&i->load_directory_map);

src/checker_builtin_procs.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ BuiltinProc__type_end,
353353
BuiltinProc_objc_register_selector,
354354
BuiltinProc_objc_register_class,
355355
BuiltinProc_objc_ivar_get,
356+
BuiltinProc_objc_block,
356357

357358
BuiltinProc_constant_utf16_cstring,
358359

@@ -714,6 +715,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
714715
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
715716
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
716717
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
718+
{STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
717719

718720
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
719721

src/llvm_backend.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ struct lbModule {
198198
StringMap<lbAddr> objc_classes;
199199
StringMap<lbAddr> objc_selectors;
200200
StringMap<lbAddr> objc_ivars;
201+
isize objc_next_block_id; // Used to name objective-c blocks, per module
201202

202203
PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
203204
PtrMap<u64/*type hash*/, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
@@ -483,7 +484,10 @@ gb_internal void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, l
483484
gb_internal void lb_start_block(lbProcedure *p, lbBlock *b);
484485

485486
gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr);
486-
487+
gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type);
488+
gb_internal void lb_begin_procedure_body(lbProcedure *p);
489+
gb_internal void lb_end_procedure_body(lbProcedure *p);
490+
gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining);
487491

488492
gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p);
489493
gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);

src/llvm_backend_general.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,6 +2944,7 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
29442944
gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
29452945
lbGenerator *gen = m->gen;
29462946

2947+
GB_ASSERT(e != nullptr);
29472948
GB_ASSERT(is_type_proc(e->type));
29482949
e = strip_entity_wrapping(e);
29492950
GB_ASSERT(e != nullptr);

src/llvm_backend_proc.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3746,6 +3746,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
37463746
case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
37473747
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
37483748
case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
3749+
case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr);
37493750

37503751

37513752
case BuiltinProc_constant_utf16_cstring:

0 commit comments

Comments
 (0)