Skip to content

Commit da0d428

Browse files
committed
[C++] Fix double pointers to foreign ref types
When a C++ type `ForeignTy` is imported as a foreign reference type, Swift should strip a level of indirection from parameters of that type, so e.g. `ForeignTy *` and `ForeignTy &` should be imported to Swift as just `ForeignTy`. However, it should only strip *one* level of indirection. importType() was instead stripping *all* levels, so things like `ForeignTy **` and `ForeignTy *&` were *also* being incorrectly imported as `ForeignTy`. Narrow this behavior so it only applies to a single level of indirection and add a few test cases to pin down the specifics. Fixes rdar://123905345.
1 parent 0bbe26d commit da0d428

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

lib/ClangImporter/ImportType.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,13 @@ namespace {
201201
: ImportHint::OtherPointer};
202202
}
203203

204+
static bool
205+
isDirectUseOfForeignReferenceType(clang::QualType clangPointeeType,
206+
Type swiftPointeeType) {
207+
return swiftPointeeType && swiftPointeeType->isForeignReferenceType() &&
208+
!clangPointeeType->isPointerType();
209+
}
210+
204211
class SwiftTypeConverter :
205212
public clang::TypeVisitor<SwiftTypeConverter, ImportResult>
206213
{
@@ -494,9 +501,10 @@ namespace {
494501
pointeeQualType, ImportTypeKind::Value, addImportDiagnostic,
495502
AllowNSUIntegerAsInt, Bridgeability::None, ImportTypeAttrs());
496503

497-
// If this is imported as a reference type, ignore the pointer.
498-
if (pointeeType && pointeeType->isForeignReferenceType())
499-
return {pointeeType, ImportHint::OtherPointer};
504+
// If this is imported as a reference type, ignore the innermost pointer.
505+
// (`T *` becomes `T`, but `T **` becomes `UnsafeMutablePointer<T>`.)
506+
if (isDirectUseOfForeignReferenceType(pointeeQualType, pointeeType))
507+
return {pointeeType, ImportHint::OtherPointer};
500508

501509
// If the pointed-to type is unrepresentable in Swift, or its C
502510
// alignment is greater than the maximum Swift alignment, import as
@@ -592,7 +600,7 @@ namespace {
592600
if (!pointeeType)
593601
return Type();
594602

595-
if (pointeeType->isForeignReferenceType())
603+
if (isDirectUseOfForeignReferenceType(pointeeQualType, pointeeType))
596604
return {pointeeType, ImportHint::None};
597605

598606
if (pointeeQualType->isFunctionType()) {
@@ -2531,6 +2539,15 @@ ClangImporter::Implementation::importParameterType(
25312539
swiftParamTy = importedType.getType();
25322540
}
25332541

2542+
// `isInOut` is set above if we stripped off a mutable `&` before importing
2543+
// the type. Normally, we want to use an `inout` parameter in this situation.
2544+
// However, if the parameter belongs to a foreign reference type *and* the
2545+
// reference we stripped out was directly to that type (rather than to a
2546+
// pointer to that type), the foreign reference type should "eat" the
2547+
// indirection of the `&`, so we *don't* want to use an `inout` parameter.
2548+
if (isInOut && isDirectUseOfForeignReferenceType(paramTy, swiftParamTy))
2549+
isInOut = false;
2550+
25342551
return ImportParameterTypeResult{swiftParamTy, isInOut,
25352552
isParamTypeImplicitlyUnwrapped};
25362553
}
@@ -2611,9 +2628,8 @@ static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
26112628

26122629
// Foreign references are already references so they don't need to be passed
26132630
// as inout.
2614-
paramInfo->setSpecifier(isInOut && !swiftParamTy->isForeignReferenceType()
2615-
? ParamSpecifier::InOut
2616-
: ParamSpecifier::Default);
2631+
paramInfo->setSpecifier(isInOut ? ParamSpecifier::InOut
2632+
: ParamSpecifier::Default);
26172633
paramInfo->setInterfaceType(swiftParamTy);
26182634
impl->recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped);
26192635

test/Interop/Cxx/ergonomics/swift-bridging-annotations.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ public protocol Proto {
2727
#if BRIDGING_HEADER_TEST
2828
func f() -> SharedObject { return SharedObject.create() }
2929

30+
func g() {
31+
var logger: LoggerSingleton?
32+
var loggerPtr: UnsafeMutablePointer<LoggerSingleton?>?
33+
var loggerPtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<LoggerSingleton?>?>?
34+
35+
takeLoggersByPointer(logger, &logger, &loggerPtr)
36+
takeLoggersByPointer(logger, loggerPtr, loggerPtrPtr)
37+
takeLoggersByPointer(nil, nil, nil)
38+
39+
takeLoggersByReference(logger!, &logger, &loggerPtr)
40+
takeLoggersByReference(logger!, &loggerPtr!.pointee, &loggerPtrPtr!.pointee)
41+
}
42+
3043
func releaseSharedObject(_: SharedObject) { }
3144
#endif
3245

@@ -71,6 +84,12 @@ public:
7184
static LoggerSingleton *getInstance();
7285
};
7386

87+
void takeLoggersByPointer(LoggerSingleton *ptr, LoggerSingleton **ptr_ptr, LoggerSingleton ***ptr_ptr_ptr);
88+
void takeLoggersByReference(LoggerSingleton &ref, LoggerSingleton *&ref_ptr, LoggerSingleton **&ref_ptr_ptr);
89+
90+
void takeLoggersByConstPointer(const LoggerSingleton **pointee0, LoggerSingleton const **pointee1, LoggerSingleton *const *pointer);
91+
void takeLoggersByConstReference(const LoggerSingleton *&pointee0, LoggerSingleton const *&pointee1, LoggerSingleton *const &pointer);
92+
7493
class SWIFT_UNSAFE_REFERENCE UnsafeNonCopyable {
7594
public:
7695
UnsafeNonCopyable(UnsafeNonCopyable &) = delete;
@@ -110,6 +129,26 @@ private:
110129
// CHECK: class func getInstance() -> LoggerSingleton!
111130
// CHECK: }
112131

132+
// CHECK-LABEL: func takeLoggersByPointer(
133+
// CHECK-SAME: _ ptr: LoggerSingleton!,
134+
// CHECK-SAME: _ ptr_ptr: UnsafeMutablePointer<LoggerSingleton?>!,
135+
// CHECK-SAME: _ ptr_ptr_ptr: UnsafeMutablePointer<UnsafeMutablePointer<LoggerSingleton?>?>!)
136+
137+
// CHECK-LABEL: func takeLoggersByReference(
138+
// CHECK-SAME: _ ref: LoggerSingleton,
139+
// CHECK-SAME: _ ref_ptr: inout LoggerSingleton!,
140+
// CHECK-SAME: _ ref_ptr_ptr: inout UnsafeMutablePointer<LoggerSingleton?>!)
141+
142+
// CHECK-LABEL: func takeLoggersByConstPointer(
143+
// CHECK-SAME: _ pointee0: UnsafeMutablePointer<LoggerSingleton?>!,
144+
// CHECK-SAME: _ pointee1: UnsafeMutablePointer<LoggerSingleton?>!,
145+
// CHECK-SAME: _ pointer: UnsafePointer<LoggerSingleton?>!)
146+
147+
// CHECK-LABEL: func takeLoggersByConstReference(
148+
// CHECK-SAME: _ pointee0: inout LoggerSingleton!,
149+
// CHECK-SAME: _ pointee1: inout LoggerSingleton!,
150+
// CHECK-SAME: _ pointer: LoggerSingleton!)
151+
113152
// CHECK: class UnsafeNonCopyable {
114153
// CHECK: }
115154
// CHECK: func returnsPointerToUnsafeReference() -> UnsafeNonCopyable!

0 commit comments

Comments
 (0)