Skip to content

Commit 3b4c6bf

Browse files
committed
[interop][SwiftToCxx] remap bool is/has property getters directly as is/has methods in C++
1 parent c9bd89b commit 3b4c6bf

File tree

3 files changed

+81
-7
lines changed

3 files changed

+81
-7
lines changed

docs/CppInteroperability/CppInteroperabilityStatus.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,6 @@ This status table describes which of the following Swift language features have
165165

166166
| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** |
167167
|--------------------------------|----------------------------------------------------------|
168-
| Getter accessors | Yes, via `get<name>`. For structs only |
168+
| Getter accessors | Yes, via `get<name>`. Boolean properties that start with `is` or `has` are remapped directly to a getter method using their original name. For structs only |
169169
| Setter accessors | Yes, via `set<name>`. For structs only |
170170
| Mutation accessors | No |

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -504,13 +504,28 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
504504
os << " }\n";
505505
}
506506

507-
static std::string remapPropertyName(const AccessorDecl *accessor) {
508-
StringRef propertyName;
507+
/// Returns true if the given property name like `isEmpty` can be remapped
508+
/// directly to a C++ method.
509+
static bool canRemapBoolPropertyNameDirectly(StringRef name) {
510+
auto startsWithAndLonger = [&](StringRef prefix) -> bool {
511+
return name.startswith(prefix) && name.size() > prefix.size();
512+
};
513+
return startsWithAndLonger("is") || startsWithAndLonger("has");
514+
}
515+
516+
static std::string remapPropertyName(const AccessorDecl *accessor,
517+
Type resultTy) {
509518
// For a getter or setter, go through the variable or subscript decl.
510-
propertyName = accessor->getStorage()->getBaseIdentifier().str();
519+
StringRef propertyName = accessor->getStorage()->getBaseIdentifier().str();
520+
521+
// Boolean property getters can be remapped directly in certain cases.
522+
if (accessor->isGetter() && resultTy->isBool() &&
523+
canRemapBoolPropertyNameDirectly(propertyName)) {
524+
return propertyName.str();
525+
}
526+
511527
std::string name;
512528
llvm::raw_string_ostream nameOS(name);
513-
// FIXME: some names are remapped differently. (e.g. isX).
514529
nameOS << (accessor->isSetter() ? "set" : "get")
515530
<< char(std::toupper(propertyName[0])) << propertyName.drop_front();
516531
nameOS.flush();
@@ -528,8 +543,9 @@ void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
528543
modifiers.qualifierContext = typeDeclContext;
529544
modifiers.isInline = true;
530545
modifiers.isConst = accessor->isGetter();
531-
printFunctionSignature(accessor, remapPropertyName(accessor), resultTy,
532-
FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
546+
printFunctionSignature(accessor, remapPropertyName(accessor, resultTy),
547+
resultTy, FunctionSignatureKind::CxxInlineThunk, {},
548+
modifiers);
533549
if (!isDefinition) {
534550
os << ";\n";
535551
return;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %s -typecheck -module-name Properties -clang-header-expose-public-decls -emit-clang-header-path %t/properties.h
3+
// RUN: %FileCheck %s < %t/properties.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/properties.h)
6+
7+
public struct IsHasProperties {
8+
public var isEmpty: Bool { return true }
9+
10+
public let hasFlavor: Bool = false
11+
12+
public var isSolid: Bool = true
13+
14+
public var flag: Bool = false
15+
16+
public let has: Bool = true
17+
18+
public let isOption: Int = 0
19+
20+
// make it a large struct for easier ABI matching.
21+
let x1, x2, x3, x4, x5, x6: Int
22+
}
23+
24+
// CHECK: class IsHasProperties
25+
26+
// CHECK: inline bool isEmpty() const;
27+
// CHECK-NEXT: inline bool hasFlavor() const;
28+
// CHECK-NEXT: inline bool isSolid() const;
29+
// CHECK-NEXT: inline void setIsSolid(bool value);
30+
// CHECK-NEXT: inline bool getFlag() const;
31+
// CHECK-NEXT: inline void setFlag(bool value);
32+
// CHECK-NEXT: inline bool getHas() const;
33+
// CHECK-NEXT: inline swift::Int getIsOption() const;
34+
35+
// CHECK: inline bool IsHasProperties::isEmpty() const {
36+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V7isEmptySbvg(_getOpaquePointer());
37+
// CHECK-NEXT: }
38+
// CHECK-NEXT: inline bool IsHasProperties::hasFlavor() const {
39+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V9hasFlavorSbvg(_getOpaquePointer());
40+
// CHECK-NEXT: }
41+
// CHECK-NEXT: inline bool IsHasProperties::isSolid() const {
42+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V7isSolidSbvg(_getOpaquePointer());
43+
// CHECK-NEXT: }
44+
// CHECK-NEXT: inline void IsHasProperties::setIsSolid(bool value) {
45+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V7isSolidSbvs(value, _getOpaquePointer());
46+
// CHECK-NEXT: }
47+
// CHECK-NEXT: inline bool IsHasProperties::getFlag() const {
48+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V4flagSbvg(_getOpaquePointer());
49+
// CHECK-NEXT: }
50+
// CHECK-NEXT: inline void IsHasProperties::setFlag(bool value) {
51+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V4flagSbvs(value, _getOpaquePointer());
52+
// CHECK-NEXT: }
53+
// CHECK-NEXT: inline bool IsHasProperties::getHas() const {
54+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V3hasSbvg(_getOpaquePointer());
55+
// CHECK-NEXT: }
56+
// CHECK-NEXT: inline swift::Int IsHasProperties::getIsOption() const {
57+
// CHECK-NEXT: return _impl::$s10Properties05IsHasA0V8isOptionSivg(_getOpaquePointer());
58+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)