Why is Clang confused by @try{} in a block with no return statement?

Under normal conditions, when a block is declared to return a value, but no return statement actually appears in the block, Clang fails to compile it with an error (of a missing return value).

However, this breaks when that block contains @try{} @catch(...){} or @try{} @finally{}.

  • Description of NSDictionary - why are some key names with quotes?
  • Dynamically change TableView Cell height
  • How to detect change in network with Reachability?
  • persistentstorecoordinator sqlite error code:522 'not an error'
  • Clicking on NSURL in a UITextView
  • iPhone ivar naming convention
  • Does anyone know why?

    The way I found this was when using @weakify() and @strongify() in RACExtScope in ReactiveCocoa, in one block I forgot to return a signal. But the compiler didn’t warn me and crashed on runtime, which lead me to dig into it, preprocess the code and find that this causes it. Any explanation would be very much appreciated, I honestly don’t know why this would happen, thanks!

    I also created a gist, in case someone had a comment/suggestion: https://gist.github.com/czechboy0/11358741

    int main(int argc, const char * argv[])
    {
        id (^iReturnStuff)() = ^id() {
            @try{} @finally{}
            //if you comment out line 4, Clang will not compile this.
            //if you leave it like this, Clang will compile and run this, even though
            //there's no value being returned.
            //is there something special in @try{} that turns off compiler errors?
        };
        return 0;
    }
    

    Solutions Collect From Internet About “Why is Clang confused by @try{} in a block with no return statement?”

    Clang’s block specification makes brief mention of control flow in a block. I’ve reproduced it here (emphasis mine)

    The compound statement of a Block is treated much like a function body
    with respect to control flow in that goto, break, and continue do not
    escape the Block. Exceptions are treated normally in that when thrown
    they pop stack frames until a catch clause is found.

    Reading through a little further, you really get the sense that exceptions in Objective-C are downright weird. From the section on exceptions

    The standard Cocoa convention is that exceptions signal programmer
    error and are not intended to be recovered from. Making code
    exceptions-safe by default would impose severe runtime and code size
    penalties on code that typically does not actually care about
    exceptions safety. Therefore, ARC-generated code leaks by default on
    exceptions, which is just fine if the process is going to be
    immediately terminated anyway. Programs which do care about recovering
    from exceptions should enable the option.

    From the above, one could reasonably deduce that the ObjC exceptions specification is so fragile or malleable that not even the compiler writers can guarantee stable code against it, therefore they just disabled all reasonable termination checks in once @try-@catch are encountered.

    This can also be seen in the code generated by Clang with and without the try-catches. First, without

    ___main_block_invoke:
        pushq   %rbp
        movq    %rsp, %rbp
        movabsq $0, %rax
        movq    %rdi, -8(%rbp)
        movq    %rdi, -16(%rbp)
        popq    %rbp
        ret
    

    This is pretty simple x86 that pushes a new stack frame, moves 0 (nil) into the return register, then returns. Now, with the try-catch block:

    ___main_block_invoke:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $64, %rsp
        movq    %rdi, -16(%rbp)
        movq    %rdi, -24(%rbp)
        movb    $0, -25(%rbp)
        movl    -32(%rbp), %eax
        testb   $1, -25(%rbp)
        movl    %eax, -48(%rbp)         ## 4-byte Spill
        jne LBB1_1
        jmp LBB1_3
        LBB1_1:
        callq   _objc_exception_rethrow
        jmp LBB1_2
        LBB1_2:
        LBB1_3:
        movl    -48(%rbp), %eax         ## 4-byte Reload
        movl    %eax, -32(%rbp)
        movq    -8(%rbp), %rdi
        addq    $64, %rsp
        popq    %rbp
        jmp _objc_autoreleaseReturnValue ## TAILCALL
        LBB1_4:
        movl    %edx, %ecx
        movq    %rax, -40(%rbp)
        movl    %ecx, -44(%rbp)
        testb   $1, -25(%rbp)
        jne LBB1_5
        jmp LBB1_7
        LBB1_5:
        callq   _objc_end_catch
        jmp LBB1_6
        LBB1_6:
        jmp LBB1_7
        LBB1_7:
        jmp LBB1_8
        LBB1_8:
        movq    -40(%rbp), %rdi
        callq   __Unwind_Resume
        LBB1_9:
        movq    %rdx, -56(%rbp)         ## 8-byte Spill
        movq    %rax, -64(%rbp)         ## 8-byte Spill
        callq   _objc_terminate
    

    Besides the more complicated function proem, notice the lack of a proper ret. The function still has two exit points,

    jmp   _objc_autoreleaseReturnValue
    

    and

    call  _objc_terminate
    

    The first is a relatively new feature of the language where, when in the tailcall position, it can be used to omit -autoreleases in favor of drawing upon thread-local variables by examining the code that came before it. The second begins immediate termination of the process and jumps into the C++ exception handling mechanism. What this means is that the function does, in fact, have the requisite exit points to keep CLANG from complaining about missing return statements. Unfortunately, what it also means is that CLANG’s forgoing of messing with the ObjC exception mechanism can potentially message garbage, as you’ve seen. This is one of the reasons EXTScope has switched to using the @autoreleasepool directive to eat that sigil.