Skip to content

Commit d9969ad

Browse files
authored
Merge pull request #60243 from hyp/eng/ifixes2
[interop][SwiftToCxx] support `is/has` property getters and fix pass/return support for `Unsafe{Mutable}Pointer` types.
2 parents 56d0399 + 0edddde commit d9969ad

8 files changed

+212
-28
lines changed

docs/CppInteroperability/CppInteroperabilityStatus.md

+1-1
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

+90-25
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ getKnownTypeInfo(const TypeDecl *typeDecl, PrimitiveTypeMapping &typeMapping,
4242
bool isKnownType(Type t, PrimitiveTypeMapping &typeMapping,
4343
OutputLanguageMode languageMode) {
4444
const TypeDecl *typeDecl;
45+
if (auto *bgt = dyn_cast<BoundGenericStructType>(
46+
t->isOptional() ? t->getOptionalObjectType()->getDesugaredType()
47+
: t->getDesugaredType())) {
48+
return bgt->isUnsafePointer() || bgt->isUnsafeMutablePointer();
49+
}
50+
4551
if (auto *typeAliasType = dyn_cast<TypeAliasType>(t.getPointer()))
4652
typeDecl = typeAliasType->getDecl();
4753
else if (auto *structDecl = t->getStructOrBoundGenericStruct())
@@ -93,6 +99,10 @@ class CFunctionSignatureTypePrinter
9399
languageMode(languageMode), modifiersDelegate(modifiersDelegate),
94100
moduleContext(moduleContext), typeUseKind(typeUseKind) {}
95101

102+
void printInoutTypeModifier() {
103+
os << (languageMode == swift::OutputLanguageMode::Cxx ? " &" : " *");
104+
}
105+
96106
bool printIfKnownSimpleType(const TypeDecl *typeDecl,
97107
Optional<OptionalTypeKind> optionalKind,
98108
bool isInOutParam) {
@@ -103,9 +113,8 @@ class CFunctionSignatureTypePrinter
103113
if (knownTypeInfo->canBeNullable) {
104114
printNullability(optionalKind);
105115
}
106-
if (isInOutParam) {
107-
os << (languageMode == swift::OutputLanguageMode::Cxx ? " &" : " *");
108-
}
116+
if (isInOutParam)
117+
printInoutTypeModifier();
109118
return true;
110119
}
111120

@@ -177,6 +186,37 @@ class CFunctionSignatureTypePrinter
177186
.printValueTypeReturnType(decl, languageMode, moduleContext);
178187
}
179188

189+
bool printIfKnownGenericStruct(const BoundGenericStructType *BGT,
190+
Optional<OptionalTypeKind> optionalKind,
191+
bool isInOutParam) {
192+
auto bgsTy = Type(const_cast<BoundGenericStructType *>(BGT));
193+
bool isConst;
194+
if (bgsTy->isUnsafePointer())
195+
isConst = true;
196+
else if (bgsTy->isUnsafeMutablePointer())
197+
isConst = false;
198+
else
199+
return false;
200+
201+
auto args = BGT->getGenericArgs();
202+
assert(args.size() == 1);
203+
visitPart(args.front(), OTK_None, /*isInOutParam=*/false);
204+
if (isConst)
205+
os << " const";
206+
os << " *";
207+
printNullability(optionalKind);
208+
if (isInOutParam)
209+
printInoutTypeModifier();
210+
return true;
211+
}
212+
213+
void visitBoundGenericStructType(BoundGenericStructType *BGT,
214+
Optional<OptionalTypeKind> optionalKind,
215+
bool isInOutParam) {
216+
if (printIfKnownGenericStruct(BGT, optionalKind, isInOutParam))
217+
return;
218+
}
219+
180220
void visitPart(Type Ty, Optional<OptionalTypeKind> optionalKind,
181221
bool isInOutParam) {
182222
TypeVisitor::visit(Ty, optionalKind, isInOutParam);
@@ -220,6 +260,14 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
220260
}
221261
};
222262

263+
// Print any modifiers before the signature.
264+
if (modifiers.isStatic) {
265+
assert(!modifiers.isConst);
266+
os << "static ";
267+
}
268+
if (modifiers.isInline)
269+
os << "inline ";
270+
223271
// Print out the return type.
224272
bool isIndirectReturnType =
225273
kind == FunctionSignatureKind::CFunctionProto &&
@@ -314,6 +362,8 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
314362
os << "void";
315363
}
316364
os << ')';
365+
if (modifiers.isConst)
366+
os << " const";
317367
}
318368

319369
void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
@@ -463,21 +513,18 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
463513
StringRef swiftSymbolName, Type resultTy, bool isDefinition) {
464514
bool isConstructor = isa<ConstructorDecl>(FD);
465515
os << " ";
466-
if (isConstructor && !isDefinition)
467-
os << "static ";
468-
os << "inline ";
469-
// FIXME: Full qualifier.
516+
470517
FunctionSignatureModifiers modifiers;
471518
if (isDefinition)
472519
modifiers.qualifierContext = typeDeclContext;
520+
modifiers.isStatic = isConstructor && !isDefinition;
521+
modifiers.isInline = true;
522+
bool isMutating =
523+
isa<FuncDecl>(FD) ? cast<FuncDecl>(FD)->isMutating() : false;
524+
modifiers.isConst = !isMutating && !isConstructor;
473525
printFunctionSignature(
474526
FD, isConstructor ? "init" : FD->getName().getBaseIdentifier().get(),
475527
resultTy, FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
476-
bool isMutating = false;
477-
if (auto *funcDecl = dyn_cast<FuncDecl>(FD))
478-
isMutating = funcDecl->isMutating();
479-
if (!isMutating && !isConstructor)
480-
os << " const";
481528
if (!isDefinition) {
482529
os << ";\n";
483530
return;
@@ -497,30 +544,48 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod(
497544
os << " }\n";
498545
}
499546

500-
void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
501-
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
502-
StringRef swiftSymbolName, Type resultTy, bool isDefinition) {
503-
assert(accessor->isSetter() || accessor->getParameters()->size() == 0);
504-
os << " inline ";
547+
/// Returns true if the given property name like `isEmpty` can be remapped
548+
/// directly to a C++ method.
549+
static bool canRemapBoolPropertyNameDirectly(StringRef name) {
550+
auto startsWithAndLonger = [&](StringRef prefix) -> bool {
551+
return name.startswith(prefix) && name.size() > prefix.size();
552+
};
553+
return startsWithAndLonger("is") || startsWithAndLonger("has");
554+
}
505555

506-
StringRef propertyName;
556+
static std::string remapPropertyName(const AccessorDecl *accessor,
557+
Type resultTy) {
507558
// For a getter or setter, go through the variable or subscript decl.
508-
propertyName = accessor->getStorage()->getBaseIdentifier().str();
559+
StringRef propertyName = accessor->getStorage()->getBaseIdentifier().str();
560+
561+
// Boolean property getters can be remapped directly in certain cases.
562+
if (accessor->isGetter() && resultTy->isBool() &&
563+
canRemapBoolPropertyNameDirectly(propertyName)) {
564+
return propertyName.str();
565+
}
509566

510567
std::string name;
511568
llvm::raw_string_ostream nameOS(name);
512-
// FIXME: some names are remapped differently. (e.g. isX).
513569
nameOS << (accessor->isSetter() ? "set" : "get")
514570
<< char(std::toupper(propertyName[0])) << propertyName.drop_front();
571+
nameOS.flush();
572+
return name;
573+
}
574+
575+
void DeclAndTypeClangFunctionPrinter::printCxxPropertyAccessorMethod(
576+
const NominalTypeDecl *typeDeclContext, const AccessorDecl *accessor,
577+
StringRef swiftSymbolName, Type resultTy, bool isDefinition) {
578+
assert(accessor->isSetter() || accessor->getParameters()->size() == 0);
579+
os << " ";
515580

516581
FunctionSignatureModifiers modifiers;
517582
if (isDefinition)
518583
modifiers.qualifierContext = typeDeclContext;
519-
printFunctionSignature(accessor, nameOS.str(), resultTy,
520-
FunctionSignatureKind::CxxInlineThunk, {}, modifiers);
521-
if (accessor->isGetter()) {
522-
os << " const";
523-
}
584+
modifiers.isInline = true;
585+
modifiers.isConst = accessor->isGetter();
586+
printFunctionSignature(accessor, remapPropertyName(accessor, resultTy),
587+
resultTy, FunctionSignatureKind::CxxInlineThunk, {},
588+
modifiers);
524589
if (!isDefinition) {
525590
os << ";\n";
526591
return;

lib/PrintAsClang/PrintClangFunction.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ class DeclAndTypeClangFunctionPrinter {
6464
/// Optional modifiers that can be applied to function signature.
6565
struct FunctionSignatureModifiers {
6666
/// Additional qualifier to add before the function's name.
67-
const NominalTypeDecl *qualifierContext;
67+
const NominalTypeDecl *qualifierContext = nullptr;
68+
bool isStatic = false;
69+
bool isInline = false;
70+
bool isConst = false;
6871

69-
FunctionSignatureModifiers() : qualifierContext(nullptr) {}
72+
FunctionSignatureModifiers() {}
7073
};
7174

7275
/// Print the C function declaration or the C++ function thunk that

test/Interop/SwiftToCxx/functions/swift-primitive-functions-cxx-bridging.swift

+32
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,22 @@
131131
// CHECK-NEXT: return _impl::$s9Functions16passThroughUInt8ys0D0VADF(x);
132132
// CHECK-NEXT: }
133133

134+
// CHECK: inline int32_t * _Nullable passThroughUnsafeGenericMutableOptionalPointer(int32_t * _Nullable x) noexcept SWIFT_WARN_UNUSED_RESULT {
135+
// CHECK-NEXT: return _impl::$s9Functions46passThroughUnsafeGenericMutableOptionalPointerySpys5Int32VGSgAFF(x);
136+
// CHECK-NEXT: }
137+
138+
// CHECK: inline int32_t * _Nonnull passThroughUnsafeGenericMutablePointer(int32_t * _Nonnull x) noexcept SWIFT_WARN_UNUSED_RESULT {
139+
// CHECK-NEXT: return _impl::$s9Functions38passThroughUnsafeGenericMutablePointerySpys5Int32VGAEF(x);
140+
// CHECK-NEXT: }
141+
142+
// CHECK: inline int32_t const * _Nullable passThroughUnsafeGenericOptionalPointer(int32_t const * _Nullable x) noexcept SWIFT_WARN_UNUSED_RESULT {
143+
// CHECK-NEXT: return _impl::$s9Functions39passThroughUnsafeGenericOptionalPointerySPys5Int32VGSgAFF(x);
144+
// CHECK-NEXT: }
145+
146+
// CHECK: inline int32_t const * _Nonnull passThroughUnsafeGenericPointer(int32_t const * _Nonnull x) noexcept SWIFT_WARN_UNUSED_RESULT {
147+
// CHECK-NEXT: return _impl::$s9Functions31passThroughUnsafeGenericPointerySPys5Int32VGAEF(x);
148+
// CHECK-NEXT: }
149+
134150
// CHECK: inline void * _Nonnull passThroughUnsafeMutableRawPointer(void * _Nonnull x) noexcept SWIFT_WARN_UNUSED_RESULT {
135151
// CHECK-NEXT: return _impl::$s9Functions34passThroughUnsafeMutableRawPointeryS2vF(x);
136152
// CHECK-NEXT: }
@@ -189,3 +205,19 @@ public func passThroughUnsafeRawPointer(_ x: UnsafeRawPointer) -> UnsafeRawPoint
189205
public func passThroughUnsafeMutableRawPointer(_ x: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { return x }
190206

191207
public func roundTwoPassThroughUnsafeMutableRawPointer(_ x: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? { return x }
208+
209+
public func passThroughUnsafeGenericPointer(_ x: UnsafePointer<Int32>) -> UnsafePointer<Int32> {
210+
return x
211+
}
212+
213+
public func passThroughUnsafeGenericOptionalPointer(_ x: UnsafePointer<Int32>?) -> UnsafePointer<Int32>? {
214+
return x
215+
}
216+
217+
public func passThroughUnsafeGenericMutablePointer(_ x: UnsafeMutablePointer<Int32>) -> UnsafeMutablePointer<Int32> {
218+
return x
219+
}
220+
221+
public func passThroughUnsafeGenericMutableOptionalPointer(_ x: UnsafeMutablePointer<Int32>?) -> UnsafeMutablePointer<Int32>? {
222+
return x
223+
}

test/Interop/SwiftToCxx/functions/swift-primitive-functions-execution.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,13 @@ int main() {
6363
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeRawPointer, &x);
6464
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeMutableRawPointer, &x);
6565
VERIFY_PASSTHROUGH_VALUE(roundTwoPassThroughUnsafeMutableRawPointer, nullptr);
66+
67+
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeGenericMutableOptionalPointer, &x);
68+
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeGenericMutableOptionalPointer,
69+
nullptr);
70+
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeGenericMutablePointer, &x);
71+
const int y = 0;
72+
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeGenericOptionalPointer, &x);
73+
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeGenericOptionalPointer, nullptr);
74+
VERIFY_PASSTHROUGH_VALUE(passThroughUnsafeGenericPointer, &x);
6675
}

test/Interop/SwiftToCxx/functions/swift-primitive-inout-functions-cxx-bridging.swift

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// CHECK: SWIFT_EXTERN void $s9Functions11inOutTwoIntyySiz_SiztF(ptrdiff_t * x, ptrdiff_t * y) SWIFT_NOEXCEPT SWIFT_CALL; // inOutTwoInt(_:_:)
99
// CHECK: SWIFT_EXTERN void $s9Functions13inOutTwoParamyySbz_SdztF(bool * x, double * y) SWIFT_NOEXCEPT SWIFT_CALL; // inOutTwoParam(_:_:)
1010
// CHECK: SWIFT_EXTERN void $s9Functions24inoutTypeWithNullabilityyySVzF(void const * _Nonnull * x) SWIFT_NOEXCEPT SWIFT_CALL; // inoutTypeWithNullability(_:)
11+
// CHECK: SWIFT_EXTERN void $s9Functions25inoutUnsafeGenericPointeryySPys5Int32VGzF(int32_t const * _Nonnull * x) SWIFT_NOEXCEPT SWIFT_CALL; // inoutUnsafeGenericPointer(_:)
1112

1213
// CHECK: inline void inOutInt(swift::Int & x) noexcept {
1314
// CHECK-NEXT: return _impl::$s9Functions8inOutIntyySizF(&x);
@@ -25,6 +26,9 @@
2526
// CHECK-NEXT: return _impl::$s9Functions24inoutTypeWithNullabilityyySVzF(&x);
2627
// CHECK-NEXT: }
2728

29+
// CHECK: inline void inoutUnsafeGenericPointer(int32_t const * _Nonnull & x) noexcept {
30+
// CHECK-NEXT: return _impl::$s9Functions25inoutUnsafeGenericPointeryySPys5Int32VGzF(&x);
31+
// CHECK-NEXT: }
2832

2933
public func inOutInt(_ x: inout Int) { x = Int() }
3034

@@ -41,3 +45,7 @@ public func inOutTwoParam(_ x: inout Bool, _ y: inout Double) {
4145
public func inoutTypeWithNullability(_ x: inout UnsafeRawPointer) {
4246
x += 1
4347
}
48+
49+
public func inoutUnsafeGenericPointer(_ x: inout UnsafePointer<Int32>) {
50+
x += 1
51+
}

test/Interop/SwiftToCxx/functions/swift-primitive-inout-functions-execution.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,13 @@ int main() {
4747
inoutTypeWithNullability(p);
4848
assert(*static_cast<const char *>(p) == 'B');
4949
}
50+
51+
{
52+
int a[2] = {1,2};
53+
const int *p = a;
54+
assert(*p == 1);
55+
inoutUnsafeGenericPointer(p);
56+
assert(p == &a[1]);
57+
assert(*p == 2);
58+
}
5059
}
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)