Skip to content

Sema/IRGen: Add clang decl support to #_hasSymbol #62745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6730,6 +6730,12 @@ WARNING(has_symbol_decl_must_be_weak,none,
(DescriptiveDeclKind, DeclName))
ERROR(has_symbol_invalid_expr,none,
"'#_hasSymbol' condition must refer to a declaration", ())
ERROR(has_symbol_invalid_decl_use_responds_to_selector,none,
"'#_hasSymbol' cannot be used with Objective-C %select{method|property}0 "
"'%1'; use respondsToSelector() instead",
(unsigned, StringRef))
ERROR(has_symbol_invalid_decl,none,
"'#_hasSymbol' cannot be used with this declaration", ())

//------------------------------------------------------------------------------
// MARK: Type erasure
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILSymbolVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ class SILSymbolVisitor {

template <typename F>
void enumerateFunctionsForHasSymbol(SILModule &M, ValueDecl *D, F Handler) {
// Handle clang decls separately.
if (auto *clangDecl = D->getClangDecl()) {
if (isa<clang::FunctionDecl>(clangDecl))
Handler(SILDeclRef(D).asForeign());

return;
}

class SymbolVisitor : public SILSymbolVisitor {
F Handler;

Expand Down
1 change: 0 additions & 1 deletion lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7130,7 +7130,6 @@ swift::getInheritedForPrinting(
}

// Collect synthesized conformances.
auto &ctx = decl->getASTContext();
llvm::SetVector<ProtocolDecl *> protocols;
llvm::TinyPtrVector<ProtocolDecl *> uncheckedProtocols;
for (auto attr : decl->getAttrs().getAttributes<SynthesizedProtocolAttr>()) {
Expand Down
45 changes: 37 additions & 8 deletions lib/IRGen/GenHasSymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "swift/SIL/SILFunctionBuilder.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILSymbolVisitor.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/GlobalDecl.h"

#include "GenDecl.h"
#include "IRGenFunction.h"
Expand Down Expand Up @@ -68,9 +70,8 @@ class HasSymbolIRGenVisitor : public IRSymbolVisitor {
SILFunction *silFn = IGM.getSILModule().lookUpFunction(name);
// Definitions for each SIL function should have been emitted by SILGen.
assert(silFn && "missing SIL function?");
if (silFn) {
if (silFn)
addFunction(silFn);
}
}

public:
Expand Down Expand Up @@ -111,6 +112,39 @@ class HasSymbolIRGenVisitor : public IRSymbolVisitor {
}
};

static llvm::Constant *
getAddrOfLLVMVariableForClangDecl(IRGenModule &IGM, ValueDecl *decl,
const clang::Decl *clangDecl) {
if (isa<clang::FunctionDecl>(clangDecl)) {
SILFunction *silFn =
IGM.getSILModule().lookUpFunction(SILDeclRef(decl).asForeign());
assert(silFn && "missing SIL function?");
return silFn ? IGM.getAddrOfSILFunction(silFn, NotForDefinition) : nullptr;
}

if (auto var = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl))
return IGM.getAddrOfObjCClass(cast<ClassDecl>(decl), NotForDefinition);

llvm::report_fatal_error("Unexpected clang decl kind");
}

static void
getSymbolAddrsForDecl(IRGenModule &IGM, ValueDecl *decl,
llvm::SmallVector<llvm::Constant *, 4> &addrs) {
if (auto *clangDecl = decl->getClangDecl()) {
if (auto *addr = getAddrOfLLVMVariableForClangDecl(IGM, decl, clangDecl))
addrs.push_back(addr);
return;
}

SILSymbolVisitorOptions opts;
opts.VisitMembers = false;
auto silCtx = SILSymbolVisitorContext(IGM.getSwiftModule(), opts);
auto linkInfo = UniversalLinkageInfo(IGM);
auto symbolVisitorCtx = IRSymbolVisitorContext(linkInfo, silCtx);
HasSymbolIRGenVisitor(IGM, addrs).visit(decl, symbolVisitorCtx);
}

llvm::Function *IRGenModule::emitHasSymbolFunction(ValueDecl *decl) {

PrettyStackTraceDecl trace("emitting #_hasSymbol query for", decl);
Expand All @@ -119,14 +153,9 @@ llvm::Function *IRGenModule::emitHasSymbolFunction(ValueDecl *decl) {
auto func = cast<llvm::Function>(getOrCreateHelperFunction(
mangler.mangleHasSymbolQuery(decl), Int1Ty, {},
[decl, this](IRGenFunction &IGF) {
SILSymbolVisitorOptions opts;
opts.VisitMembers = false;
auto silCtx = SILSymbolVisitorContext(getSwiftModule(), opts);
auto linkInfo = UniversalLinkageInfo(*this);
auto symbolVisitorCtx = IRSymbolVisitorContext(linkInfo, silCtx);
auto &Builder = IGF.Builder;
llvm::SmallVector<llvm::Constant *, 4> addrs;
HasSymbolIRGenVisitor(*this, addrs).visit(decl, symbolVisitorCtx);
getSymbolAddrsForDecl(*this, decl, addrs);

llvm::Value *ret = nullptr;
for (llvm::Constant *addr : addrs) {
Expand Down
36 changes: 36 additions & 0 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "swift/Parse/Parser.h"
#include "swift/Sema/ConstraintSystem.h"
#include "swift/Sema/IDETypeChecking.h"
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/SaveAndRestore.h"
Expand Down Expand Up @@ -4470,6 +4471,35 @@ static bool diagnoseAvailabilityCondition(PoundAvailableInfo *info,
return false;
}

/// Diagnoses whether the given clang::Decl can be referenced by a
/// `if #_hasSymbol(...)` condition. Returns true if a diagnostic was emitted.
static bool diagnoseHasSymbolConditionClangDecl(SourceLoc loc,
const clang::Decl *clangDecl,
ASTContext &ctx) {
if (isa<clang::ObjCInterfaceDecl>(clangDecl) ||
isa<clang::FunctionDecl>(clangDecl))
return false;

if (auto *method = dyn_cast<clang::ObjCMethodDecl>(clangDecl)) {
// FIXME: Allow objc_direct methods when supported by IRGen.
ctx.Diags.diagnose(loc,
diag::has_symbol_invalid_decl_use_responds_to_selector,
/*isProperty*/ false, method->getNameAsString());
return true;
}

if (auto *property = dyn_cast<clang::ObjCPropertyDecl>(clangDecl)) {
// FIXME: Allow objc_direct properties when supported by IRGen.
ctx.Diags.diagnose(loc,
diag::has_symbol_invalid_decl_use_responds_to_selector,
/*isProperty*/ true, property->getNameAsString());
return true;
}

ctx.Diags.diagnose(loc, diag::has_symbol_invalid_decl);
return true;
}

/// Diagnoses a `if #_hasSymbol(...)` condition. Returns true if a diagnostic
/// was emitted.
static bool diagnoseHasSymbolCondition(PoundHasSymbolInfo *info,
Expand All @@ -4495,6 +4525,12 @@ static bool diagnoseHasSymbolCondition(PoundHasSymbolInfo *info,
return true;
}

if (auto *clangDecl = decl->getClangDecl()) {
if (diagnoseHasSymbolConditionClangDecl(symbolExpr->getLoc(), clangDecl,
ctx))
return true;
}

if (DC->getFragileFunctionKind().kind == FragileFunctionKind::None &&
!decl->isWeakImported(DC->getParentModule())) {
// `if #_hasSymbol(someStronglyLinkedSymbol)` is functionally a no-op
Expand Down
3 changes: 3 additions & 0 deletions test/IRGen/Inputs/has_symbol/has_symbol_helper_clang.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NOTE: This header is used on platforms that do not support Obj-C.

extern void clangFunc(int x);
4 changes: 4 additions & 0 deletions test/IRGen/Inputs/has_symbol/has_symbol_helper_objc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@import Foundation;

@interface ObjCClass : NSObject
@end
9 changes: 9 additions & 0 deletions test/IRGen/Inputs/has_symbol/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module has_symbol_helper_clang {
header "has_symbol_helper_clang.h"
export *
}

module has_symbol_helper_objc {
header "has_symbol_helper_objc.h"
export *
}
4 changes: 2 additions & 2 deletions test/IRGen/has_symbol.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol_helper.swift -enable-library-evolution -disable-availability-checking
// RUN: %target-swift-frontend -emit-irgen %s -I %t -module-name test | %FileCheck %s
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol/has_symbol_helper.swift -enable-library-evolution -disable-availability-checking
// RUN: %target-swift-frontend -emit-irgen %s -I %t -I %S/Inputs/has_symbol -module-name test | %FileCheck %s

// UNSUPPORTED: OS=windows-msvc

Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/has_symbol_async.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol_helper.swift -enable-library-evolution -disable-availability-checking -DCONCURRENCY
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol/has_symbol_helper.swift -enable-library-evolution -disable-availability-checking -DCONCURRENCY
// RUN: %target-swift-frontend -emit-irgen %s -I %t -module-name test | %FileCheck %s

// REQUIRES: concurrency
Expand Down
15 changes: 15 additions & 0 deletions test/IRGen/has_symbol_clang.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-irgen %s -I %t -I %S/Inputs/has_symbol -module-name test | %FileCheck %s

// UNSUPPORTED: OS=windows-msvc

@_weakLinked import has_symbol_helper_clang

public func testClangDecls() {
// CHECK: %{{[0-9]+}} = call i1 @"$sSo9clangFuncyys5Int32VFTwS"()
if #_hasSymbol(clangFunc(_:)) {}
}

// --- clangFunc(_:) ---
// CHECK: define linkonce_odr hidden i1 @"$sSo9clangFuncyys5Int32VFTwS"() #1 {
// CHECK: ret i1 icmp ne (void (i32)* @clangFunc, void (i32)* null)
14 changes: 14 additions & 0 deletions test/IRGen/has_symbol_objc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-irgen %s -I %t -I %S/Inputs/has_symbol -module-name test | %FileCheck %s

// REQUIRES: objc_interop

@_weakLinked import has_symbol_helper_objc

public func testClassTypes() {
// CHECK: %{{[0-9]+}} = call i1 @"$sSo9ObjCClassCTwS"()
if #_hasSymbol(ObjCClass.self) {}
}

// CHECK: define linkonce_odr hidden i1 @"$sSo9ObjCClassCTwS"()
// CHECK: ret i1 icmp ne (%objc_class* @"OBJC_CLASS_$_ObjCClass", %objc_class* null)
9 changes: 9 additions & 0 deletions test/Sema/Inputs/has_symbol/has_symbol_helper_clang.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// NOTE: This header is used on platforms that do not support Obj-C.

extern void clangFunc(int x);
extern int clangGlobalVar;
struct ClangStruct {
int x;
};
#define CONSTANT_MACRO 42
enum ClangEnum { ClangEnumMember = 0 };
15 changes: 15 additions & 0 deletions test/Sema/Inputs/has_symbol/has_symbol_helper_objc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@import Foundation;

extern NSString *const StringConstant;

@interface ObjCClass : NSObject
@property int classMemberProperty;
@property(direct) int directClassMemberProperty;
- (void)classMemberMethod;
- (void)directClassMemberMethod __attribute__((objc_direct));
@end

@protocol ObjCProtocol <NSObject>
@property NSObject *protocolMemberProperty;
- (void)protocolMemberMethod;
@end
9 changes: 9 additions & 0 deletions test/Sema/Inputs/has_symbol/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module has_symbol_helper_clang {
header "has_symbol_helper_clang.h"
export *
}

module has_symbol_helper_objc {
header "has_symbol_helper_objc.h"
export *
}
2 changes: 1 addition & 1 deletion test/Sema/has_symbol.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol_helper.swift -enable-library-evolution
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol/has_symbol_helper.swift -enable-library-evolution
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t -enable-experimental-feature ResultBuilderASTTransform

Expand Down
32 changes: 32 additions & 0 deletions test/Sema/has_symbol_clang.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %S/Inputs/has_symbol/

// UNSUPPORTED: OS=windows-msvc

@_weakLinked import has_symbol_helper_clang

func testFunctions() {
if #_hasSymbol(clangFunc) {}
if #_hasSymbol(clangFunc(_:)) {}
}

func testGlobalVars() {
// FIXME: Add support for clang global vars
if #_hasSymbol(clangGlobalVar) {} // expected-error {{'#_hasSymbol' cannot be used with this declaration}}
}

func testTypes() {
if #_hasSymbol(ClangStruct.self) {} // expected-error {{'#_hasSymbol' cannot be used with this declaration}}
if #_hasSymbol(ClangEnum.self) {} // expected-error {{'#_hasSymbol' cannot be used with this declaration}}
}

func testMacros() {
if #_hasSymbol(CONSTANT_MACRO) {} // FIXME: This should be diagnosed
}

func testEnum() {
if #_hasSymbol(ClangEnumMember) {} // expected-error {{'#_hasSymbol' cannot be used with this declaration}}
}

func testStruct(_ s: ClangStruct) {
if #_hasSymbol(s.x) {} // expected-error {{'#_hasSymbol' cannot be used with this declaration}}
}
23 changes: 23 additions & 0 deletions test/Sema/has_symbol_objc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %S/Inputs/has_symbol/

// REQUIRES: objc_interop

@_weakLinked import has_symbol_helper_objc

func testTypes() {
if #_hasSymbol(ObjCClass.self) {}
if #_hasSymbol(ObjCProtocol.self) {} // expected-error {{'#_hasSymbol' cannot be used with this declaration}}
}

func testClassMembers(_ c: ObjCClass) {
if #_hasSymbol(c.classMemberProperty) {} // expected-error {{'#_hasSymbol' cannot be used with Objective-C property 'classMemberProperty'; use respondsToSelector() instead}}
if #_hasSymbol(c.classMemberMethod) {} // expected-error {{'#_hasSymbol' cannot be used with Objective-C method 'classMemberMethod'; use respondsToSelector() instead}}

if #_hasSymbol(c.directClassMemberProperty) {} // expected-error {{'#_hasSymbol' cannot be used with Objective-C property 'directClassMemberProperty'; use respondsToSelector() instead}}
if #_hasSymbol(c.directClassMemberMethod) {} // expected-error {{'#_hasSymbol' cannot be used with Objective-C method 'directClassMemberMethod'; use respondsToSelector() instead}}
}

func testProtocolMembers(_ p: ObjCProtocol) {
if #_hasSymbol(p.protocolMemberProperty) {} // expected-error {{'#_hasSymbol' cannot be used with Objective-C property 'protocolMemberProperty'; use respondsToSelector() instead}}
if #_hasSymbol(p.protocolMemberMethod) {} // expected-error {{'#_hasSymbol' cannot be used with Objective-C method 'protocolMemberMethod'; use respondsToSelector() instead}}
}