没有 return 语句的 Obj-C 方法如果包含 @try @catch 块则编译

Obj-C method without return statement compiles if it includes a @try @catch block

问题:如何让 Xcode 报告失踪的 return?

下面是一个新的 Xcode 11 测试应用程序。我添加了带有空@try/@catch 块和no return 值shouldReturnAnObject 方法。此编译和分析没有警告或错误。

#import "AppDelegate.h"

@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate
- (nonnull id)shouldReturnAnObject {
    @try {
    } @catch (NSException *exception) {
    } @finally {
    }
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    id obj = [self shouldReturnAnObject];
    NSLog(@"obj: %@", obj);
}
@end

如果我从中删除@try 块,它将无法编译并出现错误:Control reaches end of non-void function。对于没有 return return.

的任何方法,这是我期望的行为

版本:Xcode11.5,macOS 10.15.5。没有更改构建设置。 添加 -Wall 不会改变行为。

我理解 Cocoa 最佳做法是仅将异常用作异常。真正的应用程序很少使用异常,只是为了防止真正的异常行为。我提供了一个简单的测试应用程序来关注这个问题。

我相信这种行为在 Xcode/Clang 中已经存在很长时间了。如果行为是故意的,有人可以解释为什么存在这种行为吗?whi

相关信息:我还构建了快速测试应用程序:

对我来说,这绝对是一个编译器错误。 (您可以在 https://bugs.llvm.org/ 提交错误)证据如下。

我尝试了以下方法,但问题仍然存在:

  • @try {} @catch (NSException *e) {} 没有 @finally
  • @try {} @finally {} 没有 @catch
  • + 而不是 -
  • 地址清理器
  • 未定义的行为消毒程序
  • -Weverything

尽管编译器拒绝生成警告,但您可以使用 LLDB 看到它生成了一个实际上完全空的方法体(启用了优化):

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x0000000100000eb4 asdf`-[MyObj shouldReturnAnObject](self=0x0000000103108440, _cmd="shouldReturnAnObject") at main.m:23:1 [opt]
    frame #1: 0x0000000100000eee asdf`main(argc=<unavailable>, argv=<unavailable>) at main.m:31:79 [opt]
    frame #2: 0x00007fff684f6cc9 libdyld.dylib`start + 1
    frame #3: 0x00007fff684f6cc9 libdyld.dylib`start + 1
(lldb) disassemble
asdf`-[MyObj shouldReturnAnObject]:
    0x100000eb0 <+0>: pushq  %rbp
    0x100000eb1 <+1>: movq   %rsp, %rbp
->  0x100000eb4 <+4>: popq   %rbp
    0x100000eb5 <+5>: retq   
    0x100000eb6 <+6>: nopw   %cs:(%rax,%rax)

https://godbolt.org/z/ZkF48u 显示相同,尽管程序无法 link 因为那里没有可用的 Obj-C 运行时。你可以尝试不同的编译器版本——事实上,这个问题似乎至少从 Clang 3.0.0 开始就存在了。

最后,对于某些 luck/care,您可以观察到缺少 return 值会导致未定义的行为。在这里,代替缺失的 return 值,程序只是抓取栈上已经存在的任何内容,例如字符串:

后续注意事项:

在 clang 中找到案例的功劳归功于@jtbandes。 看起来这个案例还没有实现。 https://github.com/llvm/llvm-project/blob/master/clang/lib/Analysis/CFG.cpp#L3666

他还追踪到了 11 年前最初的“FIXME”提交。 https://twitter.com/jtbandes/status/1279870326784929792

我已将 ~radar~ 反馈助手错误提交给 Apple:FB7851551

我没有 llvm 贡献者帐户,而且由于垃圾邮件,他们不接受新成员,所以不能在那里提交错误。如果别人愿意,那就太棒了。如果他们确实给我一个帐户,我也会在那里跟进错误报告。

至此,我认为案子已经结案了。也许运气好的话,它可以在今年秋天成为 SDK 的最终版本。谢谢大家帮我找到这个。

这是一个错误。在 https://llvm.org/PR46693

上有一个 LLVM 错误