Skip to content

Commit e5f0a7b

Browse files
committed
[PrintAsObjC] Special-case <os/object.h> types, like Dispatch.
These have historically been defined as protocols in Objective-C (under a pile of macros), but when imported into Swift they're classes instead. Reverse this bit of magic by hard-coding the prefix "OS_" and the header <os/object.h>, and emitting the classic 'foo_bar_t'-style type names. rdar://problem/29790636
1 parent 88ecaad commit e5f0a7b

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "clang/AST/DeclObjC.h"
3232
#include "clang/Basic/CharInfo.h"
3333
#include "clang/Basic/Module.h"
34+
#include "clang/Lex/Lexer.h"
3435
#include "llvm/ADT/SetVector.h"
3536
#include "llvm/ADT/StringSwitch.h"
3637
#include "llvm/ADT/STLExtras.h"
@@ -125,6 +126,38 @@ static bool looksLikeInitMethod(ObjCSelector selector) {
125126
return !(firstPiece.size() > 4 && clang::isLowercase(firstPiece[4]));
126127
}
127128

129+
/// Returns the name of an <os/object.h> type minus the leading "OS_",
130+
/// or an empty string if \p decl is not an <os/object.h> type.
131+
static StringRef maybeGetOSObjectBaseName(const clang::NamedDecl *decl) {
132+
StringRef name = decl->getName();
133+
if (!name.consume_front("OS_"))
134+
return StringRef();
135+
136+
clang::SourceLocation loc = decl->getLocation();
137+
if (!loc.isMacroID())
138+
return StringRef();
139+
140+
// Hack: check to see if the name came from a macro in <os/object.h>.
141+
clang::SourceManager &sourceMgr = decl->getASTContext().getSourceManager();
142+
clang::SourceLocation expansionLoc =
143+
sourceMgr.getImmediateExpansionRange(loc).first;
144+
clang::SourceLocation spellingLoc = sourceMgr.getSpellingLoc(expansionLoc);
145+
146+
if (!sourceMgr.getFilename(spellingLoc).endswith("/os/object.h"))
147+
return StringRef();
148+
149+
return name;
150+
}
151+
152+
/// Returns true if \p decl represents an <os/object.h> type.
153+
static bool isOSObjectType(const clang::Decl *decl) {
154+
auto *named = dyn_cast_or_null<clang::NamedDecl>(decl);
155+
if (!named)
156+
return false;
157+
return !maybeGetOSObjectBaseName(named).empty();
158+
}
159+
160+
128161
namespace {
129162
using DelayedMemberSet = llvm::SmallSetVector<const ValueDecl *, 32>;
130163

@@ -1320,18 +1353,21 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
13201353
assert(CD->isObjC());
13211354
auto clangDecl = dyn_cast_or_null<clang::NamedDecl>(CD->getClangDecl());
13221355
if (clangDecl) {
1323-
if (isa<clang::ObjCInterfaceDecl>(clangDecl)) {
1356+
// Hack for <os/object.h> types, which use classes in Swift but
1357+
// protocols in Objective-C, and a typedef to hide the difference.
1358+
StringRef osObjectName = maybeGetOSObjectBaseName(clangDecl);
1359+
if (!osObjectName.empty()) {
1360+
os << osObjectName << "_t";
1361+
} else if (isa<clang::ObjCInterfaceDecl>(clangDecl)) {
13241362
os << clangDecl->getName() << " *";
1325-
printNullability(optionalKind);
13261363
} else {
13271364
maybePrintTagKeyword(CD);
13281365
os << clangDecl->getName();
1329-
printNullability(optionalKind);
13301366
}
13311367
} else {
13321368
os << getNameForObjC(CD) << " *";
1333-
printNullability(optionalKind);
13341369
}
1370+
printNullability(optionalKind);
13351371
}
13361372

13371373
void visitProtocolType(ProtocolType *PT,
@@ -1799,7 +1835,8 @@ class ModuleWriter {
17991835

18001836
bool forwardDeclare(const ClassDecl *CD) {
18011837
if (!CD->isObjC() ||
1802-
CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
1838+
CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType ||
1839+
isOSObjectType(CD->getClangDecl())) {
18031840
return false;
18041841
}
18051842
forwardDeclare(CD, [&]{ os << "@class " << getNameForObjC(CD) << ";\n"; });

test/PrintAsObjC/dispatch.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-swift-frontend -typecheck %s -parse-as-library -emit-objc-header-path %t/swift.h
3+
// RUN: %FileCheck %s < %t/swift.h
4+
5+
// REQUIRES: objc_interop
6+
7+
import Foundation
8+
9+
// CHECK: @import Dispatch;
10+
11+
// CHECK-LABEL: @interface Test : NSObject{{$}}
12+
public class Test : NSObject {
13+
// CHECK-NEXT: - (void)thank:(dispatch_queue_t _Nonnull)queue;
14+
public func thank(_ queue: DispatchQueue) {}
15+
// CHECK-NEXT: init
16+
} // CHECK-NEXT: @end

0 commit comments

Comments
 (0)