diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -487,6 +487,20 @@ /// True if the method is tagged as objc_direct bool isDirectMethod() const; + /// True if method is meant to be visible. + /// + /// TODO: For now this is only true for a very narrow case where the method + /// must be direct and must also be explicitly marked as + /// __attribute__((visibility("default"))). In the future it may be possible + /// to assume that all direct (or even both direct and indirect) methods are + /// visible but for the time being it is prudent to provide this default + /// visibility behavior as an opt-in only. + bool hasMethodVisibilityDefault() const { + return isDirectMethod() && + getExplicitVisibility(VisibilityForValue) + .value_or(HiddenVisibility) == DefaultVisibility; + } + /// True if the method has a parameter that's destroyed in the callee. bool hasParamDestroyedInCallee() const; diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp --- a/clang/lib/AST/Mangle.cpp +++ b/clang/lib/AST/Mangle.cpp @@ -359,10 +359,16 @@ } // \01+[ContainerName(CategoryName) SelectorName] + // Note: If we are mangling for an objc_direct method with external visibility + // then the standard mangling scheme will not work. We must replace the + // square braces with angle braces so in the case of visible direct objc + // methods it results in: + + // This will include a prefix _ on Darwin. if (includePrefixByte) { OS << '\01'; } - OS << (MD->isInstanceMethod() ? '-' : '+') << '['; + OS << (MD->isInstanceMethod() ? '-' : '+'); + OS << (MD->hasMethodVisibilityDefault() ? '<' : '['); if (const auto *CID = MD->getCategory()) { OS << CID->getClassInterface()->getName(); if (includeCategoryNamespace) { @@ -376,7 +382,7 @@ } OS << ' '; MD->getSelector().print(OS); - OS << ']'; + OS << (MD->hasMethodVisibilityDefault() ? '>' : ']'); } void MangleContext::mangleObjCMethodNameAsSourceName(const ObjCMethodDecl *MD, diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -760,7 +760,9 @@ const CGFunctionInfo &FI = CGM.getTypes().arrangeObjCMethodDeclaration(OMD); if (OMD->isDirectMethod()) { - Fn->setVisibility(llvm::Function::HiddenVisibility); + Fn->setVisibility(OMD->hasMethodVisibilityDefault() + ? llvm::Function::DefaultVisibility + : llvm::Function::HiddenVisibility); CGM.SetLLVMFunctionAttributes(OMD, FI, Fn, /*IsThunk=*/false); CGM.SetLLVMFunctionAttributesForDefinition(OMD, Fn); } else { diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -474,7 +474,8 @@ std::string buffer; llvm::raw_string_ostream out(buffer); CGM.getCXXABI().getMangleContext().mangleObjCMethodName(OMD, out, - /*includePrefixByte=*/true, + /*includePrefixByte=*/ + !OMD->hasMethodVisibilityDefault(), includeCategoryName); return buffer; } diff --git a/clang/test/CodeGenObjC/objc-direct-wrapper.m b/clang/test/CodeGenObjC/objc-direct-wrapper.m new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenObjC/objc-direct-wrapper.m @@ -0,0 +1,86 @@ +// RUN: %clang -fobjc-arc -Wno-objc-root-class -ObjC -fobjc-runtime=ios -FFoundation \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 -c -o - %s | \ +// RUN: llvm-nm - | FileCheck -check-prefix=CHECK-WRAPPER %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s | llvm-nm - | \ +// RUN: FileCheck -check-prefix=CHECK-DEFAULT %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DEF %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class -DNO_OBJC_IMPL \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DECLARE %s + +// RUN: not %clang -fobjc-arc -Wno-objc-root-class -DENABLE_PROTOCOL_DIRECT_FAIL \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=1 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-PROTOCOL-DIRECT-FAIL %s + +//////////////////////////////////////////////////////////////////////////////// + +// RUN: %clang -fobjc-arc -Wno-objc-root-class -ObjC -fobjc-runtime=ios -FFoundation \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=0 \ +// RUN: -target x86_64-apple-macosx10.15.0 -c -o - %s | \ +// RUN: llvm-nm - | FileCheck -check-prefix=CHECK-WRAPPER-INDIRECT %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=0 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DEF-INDIRECT %s + +// RUN: %clang -fobjc-arc -Wno-objc-root-class -DNO_OBJC_IMPL \ +// RUN: -DENABLE_VISIBLE_OBJC_DIRECT=0 \ +// RUN: -target x86_64-apple-macosx10.15.0 \ +// RUN: -ObjC -fobjc-runtime=ios -FFoundation -c -o - %s -S -emit-llvm | \ +// RUN: FileCheck -check-prefix=CHECK-WRAPPER-IR-DECLARE-INDIRECT %s + +// CHECK-WRAPPER: T _- + // TODO: Fix this +// CHECK-DEFAULT: t -[C testMethod:bar:] +// CHECK-WRAPPER-IR-DEF: define {{(dso_local )?}}void @"-" +// CHECK-WRAPPER-IR-DECLARE: declare {{(dso_local )?}}void @"-" +// CHECK-PROTOCOL-DIRECT-FAIL: error: 'objc_direct' attribute cannot be applied to methods declared in an Objective-C protocol + +// CHECK-WRAPPER-INDIRECT-NOT: T _- +// CHECK-WRAPPER-IR-DEF-INDIRECT-NOT: define {{(dso_local )?}}void @"-" +// CHECK-WRAPPER-IR-DECLARE-INDIRECT-NOT: declare {{(dso_local )?}}void @"-" + +#if ENABLE_VISIBLE_OBJC_DIRECT +#define OBJC_DIRECT __attribute__((objc_direct)) __attribute__((visibility("default"))) +#else +#define OBJC_DIRECT +#endif + +@interface C +- (void)testMethod:(int)arg1 bar:(float)arg2 OBJC_DIRECT; +@end + +#ifndef NO_OBJC_IMPL +@implementation C +- (void)testMethod:(int)arg1 bar:(float)arg2 OBJC_DIRECT { +} +@end +#endif + +#ifdef ENABLE_PROTOCOL_DIRECT_FAIL +@protocol ProtoDirectVisibleFail +- (void)protoMethod OBJC_DIRECT; // expected-error {{'objc_direct' attribute cannot be applied to methods declared in an Objective-C protocol}} +@end +#endif + +C *c; + +void f() { + [c testMethod:1 bar:1.0]; +}