Skip to content

Commit 30d0c17

Browse files
committed
[interop] update names and add docs for the interop C++ helper macros
1 parent 8fd028c commit 30d0c17

File tree

9 files changed

+186
-26
lines changed

9 files changed

+186
-26
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ NOTE(projection_may_return_interior_ptr, none, "C++ method '%0' may return an "
189189
NOTE(mark_self_contained, none, "Mark type '%0' as 'SELF_CONTAINED' in C++ to "
190190
"make methods that use it available in Swift. ",
191191
(StringRef))
192-
NOTE(mark_safe_to_import, none, "Mark method '%0' as 'SAFE_TO_IMPORT' in C++ to "
193-
"make it available in Swift. ",
192+
NOTE(mark_safe_to_import, none, "annotate method '%0' with 'RETURNS_INDEPENDENT_VALUE' in C++ to "
193+
"make it available in Swift",
194194
(StringRef))
195195

196196
NOTE(at_to_subscript, none, "Do you want to replace it with a call "

lib/ClangImporter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ set(SWIFTINC_DIR
5050

5151
add_custom_command(
5252
OUTPUT "${SWIFTINC_DIR}/bridging"
53+
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/bridging"
5354
COMMAND "${CMAKE_COMMAND}" "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/bridging" "${SWIFTINC_DIR}")
5455

5556
add_custom_target("copy_cxxInterop_support_header"

lib/ClangImporter/ClangImporter.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4408,8 +4408,8 @@ static void diagnoseForeignReferenceTypeFixit(ClangImporter::Implementation &Imp
44084408
HeaderLoc loc, Diagnostic diag) {
44094409
auto importedLoc =
44104410
Impl.SwiftContext.getClangModuleLoader()->importSourceLocation(loc.clangLoc);
4411-
Impl.diagnose(loc, diag)
4412-
.fixItInsert(importedLoc, "SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>) ");
4411+
Impl.diagnose(loc, diag).fixItInsert(
4412+
importedLoc, "SHARED_REFERENCE(<#retain#>, <#release#>) ");
44134413
}
44144414

44154415
bool ClangImporter::Implementation::emitDiagnosticsForTarget(

lib/ClangImporter/bridging

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,98 @@
1717
#ifndef SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H
1818
#define SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H
1919

20+
/// Specifies that a C++ `class` or `struct` owns and controls the lifetime of all
21+
/// of the objects it references. Such type should not reference any objects whose
22+
/// lifetime is controlled externally. This annotation allows Swift to import methods
23+
/// that return a `class` or `struct` type that is annotated with this macro.
2024
#define SELF_CONTAINED __attribute__((swift_attr("import_owned")))
21-
#define SAFE_TO_IMPORT __attribute__((swift_attr("import_unsafe")))
25+
26+
/// Specifies that a C++ method returns a value that is presumed to contain
27+
/// objects whose lifetime is not dependent on `this` or other parameters passed
28+
/// to the method.
29+
#define RETURNS_INDEPENDENT_VALUE __attribute__((swift_attr("import_unsafe")))
2230

2331
#define _CXX_INTEROP_STRINGIFY(_x) #_x
24-
#define SWIFT_REFERENCE_TYPE(_retain, _release) \
25-
__attribute__((swift_attr("import_reference"))) \
26-
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \
32+
33+
/// Specifies that a C++ `class` or `struct` is reference-counted using
34+
/// the given `retain` and `release` functions. This annotation lets Swift import
35+
/// such a type as reference counted type in Swift, taking advantage of Swift's
36+
/// automatic reference counting.
37+
///
38+
/// This example shows how to use this macro to let Swift know that
39+
/// a non-copyable reference counted C++ class can be imported as a reference counted type in Swift:
40+
/// ```c++
41+
/// class SHARED_REFERENCE(retainSharedObject, releaseSharedObject)
42+
/// SharedObject : NonCopyable, IntrusiveReferenceCounted<SharedObject> {
43+
/// public:
44+
/// static SharedObject* create();
45+
/// void doSomething();
46+
/// };
47+
///
48+
/// void retainSharedObject(SharedObject *);
49+
/// void releaseSharedObject(SharedObject *);
50+
/// ```
51+
///
52+
/// Then, the Swift programmer would be able to use it in the following manner:
53+
///
54+
/// ```swift
55+
/// let object = SharedObject.create()
56+
/// object.doSomething()
57+
/// // The Swift compiler will release object here.
58+
/// ```
59+
#define SHARED_REFERENCE(_retain, _release) \
60+
__attribute__((swift_attr("import_reference"))) \
61+
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \
2762
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:_release))))
2863

64+
/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime
65+
/// is presumed to be immortal, i.e. the reference to such object is presumed to
66+
/// always be valid. This annotation lets Swift import such a type as a reference
67+
/// type in Swift.
68+
////
69+
/// This example shows how to use this macro to let Swift know that
70+
/// a non-copyable singleton C++ class can be imported as a reference type in Swift:
71+
/// ```c++
72+
/// class IMMORTAL_REFERENCE
73+
/// LoggerSingleton : NonCopyable {
74+
/// public:
75+
/// static LoggerSingleton &getInstance();
76+
/// void log(int x);
77+
/// };
78+
/// ```
79+
///
80+
/// Then, the Swift programmer would be able to use it in the following manner:
81+
///
82+
/// ```swift
83+
/// let logger = LoggerSingleton.getInstance()
84+
/// logger.log(123)
85+
/// ```
86+
#define IMMORTAL_REFERENCE \
87+
__attribute__((swift_attr("import_reference"))) \
88+
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \
89+
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal))))
90+
91+
/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime
92+
/// is not managed automatically. The programmer must validate that any reference
93+
/// to such object is valid themselves. This annotation lets Swift import such a type as a reference type in Swift.
94+
#define UNSAFE_REFERENCE \
95+
__attribute__((swift_attr("import_reference"))) \
96+
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \
97+
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal))))
98+
99+
/// Specifies a name that will be used in Swift for this declaration instead of its original name.
29100
#define SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
30101

31-
#define CONFORMS_TO(_name) \
32-
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(conforms_to:_name))))
102+
/// Specifies that a specific C++ `class` or `struct` conforms to a
103+
/// a specific Swift protocol.
104+
///
105+
/// This example shows how to use this macro to conform a class template to a Swift protocol:
106+
/// ```
107+
/// template<class T>
108+
/// class CONFORMS_TO_PROTOCOL(SwiftModule.ProtocolName)
109+
/// CustomClass {};
110+
/// ```
111+
#define CONFORMS_TO_PROTOCOL(_moduleName_protocolName) \
112+
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(conforms_to:_moduleName_protocolName))))
33113

34114
#endif // SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H

lib/Sema/CSDiagnostics.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3910,8 +3910,7 @@ void MissingMemberFailure::diagnoseUnsafeCxxMethod(SourceLoc loc,
39103910
ctx.Diags
39113911
.diagnose(methodSwiftLoc, diag::mark_safe_to_import,
39123912
name.getBaseIdentifier().str())
3913-
.fixItInsert(methodSwiftLoc,
3914-
" SAFE_TO_IMPORT ");
3913+
.fixItInsert(methodSwiftLoc, " RETURNS_INDEPENDENT_VALUE ");
39153914
} else if (cxxMethod->getReturnType()->isReferenceType()) {
39163915
// Rewrite a call to .at(42) as a subscript.
39173916
if (name.getBaseIdentifier().is("at") &&
@@ -3941,8 +3940,7 @@ void MissingMemberFailure::diagnoseUnsafeCxxMethod(SourceLoc loc,
39413940
ctx.Diags
39423941
.diagnose(methodSwiftLoc, diag::mark_safe_to_import,
39433942
name.getBaseIdentifier().str())
3944-
.fixItInsert(methodSwiftLoc,
3945-
" SAFE_TO_IMPORT ");
3943+
.fixItInsert(methodSwiftLoc, " RETURNS_INDEPENDENT_VALUE ");
39463944
}
39473945
} else if (cxxMethod->getReturnType()->isRecordType()) {
39483946
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(
@@ -3967,8 +3965,7 @@ void MissingMemberFailure::diagnoseUnsafeCxxMethod(SourceLoc loc,
39673965
ctx.Diags
39683966
.diagnose(methodSwiftLoc, diag::mark_safe_to_import,
39693967
name.getBaseIdentifier().str())
3970-
.fixItInsert(methodSwiftLoc,
3971-
" SAFE_TO_IMPORT ");
3968+
.fixItInsert(methodSwiftLoc, " RETURNS_INDEPENDENT_VALUE ");
39723969
ctx.Diags
39733970
.diagnose(baseSwiftLoc, diag::mark_self_contained, returnTypeStr)
39743971
.fixItInsert(baseSwiftLoc,

test/Interop/Cxx/class/fixit-add-safe-to-import-self-contained.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ struct X {
2121
import Test
2222

2323
public func test(x: X) {
24-
// CHECK: note: Mark method 'test' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
24+
// CHECK: note: annotate method 'test' with 'RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
2525
// CHECK: int *test() { }
2626
// CHECK: ^
27-
// CHECK: SAFE_TO_IMPORT
27+
// CHECK: RETURNS_INDEPENDENT_VALUE
2828

2929
x.test()
3030

31-
// CHECK: note: Mark method 'other' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
31+
// CHECK: note: annotate method 'other' with 'RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
3232
// CHECK: Ptr other() { }
3333
// CHECK: ^
34-
// CHECK: SAFE_TO_IMPORT
34+
// CHECK: RETURNS_INDEPENDENT_VALUE
3535

3636
// CHECK: note: Mark type 'Ptr' as 'SELF_CONTAINED' in C++ to make methods that use it available in Swift.
3737
// CHECK: struct Ptr {

test/Interop/Cxx/class/invalid-class-errors.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ import Test
3030
// CHECK: note: record 'A' is not automatically available: does not have a copy constructor or destructor. Does this type have reference semantics?
3131
// CHECK: struct A {
3232
// CHECK: ^
33-
// CHECK: SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>)
33+
// CHECK: SHARED_REFERENCE(<#retain#>, <#release#>)
3434
public func test(x: A) { }
3535
// CHECK: note: record 'B' is not automatically available: does not have a copy constructor or destructor. Does this type have reference semantics?
3636
// CHECK: struct {{.*}}B {
3737
// CHECK: ^
38-
// CHECK: SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>)
38+
// CHECK: SHARED_REFERENCE(<#retain#>, <#release#>)
3939
public func test(x: B) { }
4040
// CHECK: note: record 'Nested' is not automatically available: does not have a copy constructor or destructor. Does this type have reference semantics?
4141
// CHECK: struct Nested {
4242
// CHECK: ^
43-
// CHECK: SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>)
43+
// CHECK: SHARED_REFERENCE(<#retain#>, <#release#>)
4444
public func test(x: Namespace.Nested) { }

test/Interop/Cxx/class/invalid-unsafe-projection-errors.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ import Test
2929
public func test(x: M) {
3030
// CHECK: note: C++ method 'test1' that returns a pointer of type 'UnsafeMutablePointer' is unavailable.
3131
// CHECK: note: C++ method 'test1' may return an interior pointer.
32-
// CHECK: note: Mark method 'test1' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
32+
// CHECK: note: annotate method 'test1' with 'RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
3333
x.test1()
3434
// CHECK: note: C++ method 'test2' that returns a reference of type 'UnsafeMutablePointer' is unavailable.
3535
// CHECK: note: C++ method 'test2' may return an interior pointer.
36-
// CHECK: note: Mark method 'test2' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
36+
// CHECK: note: annotate method 'test2' with 'RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
3737
x.test2()
3838
// CHECK: note: C++ method 'test3' that returns a value of type 'Ptr' is unavailable.
3939
// CHECK: note: C++ method 'test3' may return an interior pointer.
40-
// CHECK: note: Mark method 'test3' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
40+
// CHECK: note: annotate method 'test3' with 'RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
4141
// CHECK: note: Mark type 'Ptr' as 'SELF_CONTAINED' in C++ to make methods that use it available in Swift.
4242
x.test3()
4343
// CHECK: note: C++ method 'begin' that returns an iterator is unavailable
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %target-swift-frontend %t/SwiftMod.swift -module-name SwiftMod -emit-module -o %t/SwiftMod.swiftmodule -I %t -enable-experimental-cxx-interop -Xcc -DFIRSTPASS
5+
6+
// RUN: %target-swift-ide-test -print-module -module-to-print=SwiftMod -module-to-print=CxxModule -I %t -I %t/Inputs -I %swift_src_root/lib/ClangImporter -source-filename=x -enable-experimental-cxx-interop | %FileCheck %s
7+
8+
//--- SwiftMod.swift
9+
10+
public protocol Proto {
11+
}
12+
13+
//--- Inputs/module.modulemap
14+
module CxxModule {
15+
header "header.h"
16+
requires cplusplus
17+
}
18+
19+
//--- Inputs/header.h
20+
21+
// Note: in actuality, this will be included
22+
// as <swift/bridging>, but in this test we include
23+
// it directly.
24+
#include "bridging"
25+
26+
class SELF_CONTAINED SelfContained {
27+
public:
28+
int *pointer;
29+
30+
SelfContained();
31+
32+
const int *returnsIndependent() const RETURNS_INDEPENDENT_VALUE;
33+
};
34+
35+
class SHARED_REFERENCE(retainSharedObject, releaseSharedObject)
36+
SharedObject {
37+
public:
38+
static SharedObject *create();
39+
};
40+
41+
void retainSharedObject(SharedObject *);
42+
void releaseSharedObject(SharedObject *);
43+
44+
class IMMORTAL_REFERENCE LoggerSingleton {
45+
public:
46+
LoggerSingleton(const LoggerSingleton &) = delete;
47+
static LoggerSingleton *getInstance();
48+
};
49+
50+
class UNSAFE_REFERENCE UnsafeNonCopyable {
51+
public:
52+
UnsafeNonCopyable(UnsafeNonCopyable &) = delete;
53+
};
54+
55+
UnsafeNonCopyable *returnsPointerToUnsafeReference();
56+
void takesPointerToUnsafeNonCopyable(UnsafeNonCopyable *);
57+
58+
class CONFORMS_TO_PROTOCOL(SwiftMod.Proto) ConformsTo {
59+
public:
60+
};
61+
62+
63+
// CHECK: struct SelfContained {
64+
65+
// CHECK: func returnsIndependent() -> UnsafePointer<Int32>!
66+
67+
// CHECK: class SharedObject {
68+
// CHECK: class func create() -> SharedObject!
69+
// CHECK: func retainSharedObject(_: SharedObject!)
70+
// CHECK: func releaseSharedObject(_: SharedObject!)
71+
72+
// CHECK: class LoggerSingleton {
73+
// CHECK: class func getInstance() -> LoggerSingleton!
74+
// CHECK: }
75+
76+
// CHECK: class UnsafeNonCopyable {
77+
// CHECK: }
78+
// CHECK: func returnsPointerToUnsafeReference() -> UnsafeNonCopyable!
79+
// CHECK: func takesPointerToUnsafeNonCopyable(_: UnsafeNonCopyable!)
80+
81+
// CHECK: struct ConformsTo : Proto {
82+

0 commit comments

Comments
 (0)