Skip to content

[interop] update names and add docs for the interop C++ helper macros #64911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ NOTE(record_is_dependent, none, "record '%0' is dependent", (StringRef))
NOTE(record_parent_unimportable, none, "record %0's parent is not importable", (StringRef))
NOTE(reference_passed_by_value, none, "function uses foreign reference type "
"'%0' as a value in %1 types which breaks "
"'import_reference' contract (outlined in "
"'swift_shared_reference' contract (outlined in "
"C++ Interop User Manual).",
(StringRef, StringRef))
NOTE(record_not_automatically_importable, none, "record '%0' is not "
Expand All @@ -186,11 +186,11 @@ NOTE(projection_reference_not_imported, none, "C++ method '%0' that returns a re
NOTE(projection_may_return_interior_ptr, none, "C++ method '%0' may return an "
"interior pointer. ",
(StringRef))
NOTE(mark_self_contained, none, "Mark type '%0' as 'SELF_CONTAINED' in C++ to "
NOTE(mark_self_contained, none, "Mark type '%0' as 'SWIFT_SELF_CONTAINED' in C++ to "
"make methods that use it available in Swift. ",
(StringRef))
NOTE(mark_safe_to_import, none, "Mark method '%0' as 'SAFE_TO_IMPORT' in C++ to "
"make it available in Swift. ",
NOTE(mark_safe_to_import, none, "annotate method '%0' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to "
"make it available in Swift",
(StringRef))

NOTE(at_to_subscript, none, "Do you want to replace it with a call "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non blocking, but if it's easy, there's a reference to "import_reference" in this file that should probably be updated. Not a big deal, though.

Expand Down
5 changes: 4 additions & 1 deletion lib/ClangImporter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@ set(SWIFTINC_DIR

add_custom_command(
OUTPUT "${SWIFTINC_DIR}/bridging"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/bridging"
COMMAND "${CMAKE_COMMAND}" "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/bridging" "${SWIFTINC_DIR}")

add_custom_target("copy_cxxInterop_support_header"
DEPENDS "${SWIFTINC_DIR}/bridging"
COMMENT "Copying C++ interop support header to ${SWIFTINC_DIR}")

swift_install_in_component(FILES "${CMAKE_CURRENT_SOURCE_DIR}/bridging"
swift_install_in_component(FILES
"${CMAKE_CURRENT_SOURCE_DIR}/bridging"
"${CMAKE_CURRENT_SOURCE_DIR}/module.modulemap"
DESTINATION "include/swift"
COMPONENT compiler)

Expand Down
4 changes: 2 additions & 2 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4408,8 +4408,8 @@ static void diagnoseForeignReferenceTypeFixit(ClangImporter::Implementation &Imp
HeaderLoc loc, Diagnostic diag) {
auto importedLoc =
Impl.SwiftContext.getClangModuleLoader()->importSourceLocation(loc.clangLoc);
Impl.diagnose(loc, diag)
.fixItInsert(importedLoc, "SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>) ");
Impl.diagnose(loc, diag).fixItInsert(
importedLoc, "SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>) ");
}

bool ClangImporter::Implementation::emitDiagnosticsForTarget(
Expand Down
94 changes: 87 additions & 7 deletions lib/ClangImporter/bridging
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,98 @@
#ifndef SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H
#define SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H

#define SELF_CONTAINED __attribute__((swift_attr("import_owned")))
#define SAFE_TO_IMPORT __attribute__((swift_attr("import_unsafe")))
/// Specifies that a C++ `class` or `struct` owns and controls the lifetime of all
/// of the objects it references. Such type should not reference any objects whose
/// lifetime is controlled externally. This annotation allows Swift to import methods
/// that return a `class` or `struct` type that is annotated with this macro.
#define SWIFT_SELF_CONTAINED __attribute__((swift_attr("import_owned")))

/// Specifies that a C++ method returns a value that is presumed to contain
/// objects whose lifetime is not dependent on `this` or other parameters passed
/// to the method.
#define SWIFT_RETURNS_INDEPENDENT_VALUE __attribute__((swift_attr("import_unsafe")))

#define _CXX_INTEROP_STRINGIFY(_x) #_x
#define SWIFT_REFERENCE_TYPE(_retain, _release) \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \

/// Specifies that a C++ `class` or `struct` is reference-counted using
/// the given `retain` and `release` functions. This annotation lets Swift import
/// such a type as reference counted type in Swift, taking advantage of Swift's
/// automatic reference counting.
///
/// This example shows how to use this macro to let Swift know that
/// a non-copyable reference counted C++ class can be imported as a reference counted type in Swift:
/// ```c++
/// class SWIFT_SHARED_REFERENCE(retainSharedObject, releaseSharedObject)
/// SharedObject : NonCopyable, IntrusiveReferenceCounted<SharedObject> {
/// public:
/// static SharedObject* create();
/// void doSomething();
/// };
///
/// void retainSharedObject(SharedObject *);
/// void releaseSharedObject(SharedObject *);
/// ```
///
/// Then, the Swift programmer would be able to use it in the following manner:
///
/// ```swift
/// let object = SharedObject.create()
/// object.doSomething()
/// // The Swift compiler will release object here.
/// ```
#define SWIFT_SHARED_REFERENCE(_retain, _release) \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:_retain)))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:_release))))

/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime
/// is presumed to be immortal, i.e. the reference to such object is presumed to
/// always be valid. This annotation lets Swift import such a type as a reference
/// type in Swift.
////
/// This example shows how to use this macro to let Swift know that
/// a non-copyable singleton C++ class can be imported as a reference type in Swift:
/// ```c++
/// class SWIFT_IMMORTAL_REFERENCE
/// LoggerSingleton : NonCopyable {
/// public:
/// static LoggerSingleton &getInstance();
/// void log(int x);
/// };
/// ```
///
/// Then, the Swift programmer would be able to use it in the following manner:
///
/// ```swift
/// let logger = LoggerSingleton.getInstance()
/// logger.log(123)
/// ```
#define SWIFT_IMMORTAL_REFERENCE \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal))))

/// Specifies that a C++ `class` or `struct` is a reference type whose lifetime
/// is not managed automatically. The programmer must validate that any reference
/// to such object is valid themselves. This annotation lets Swift import such a type as a reference type in Swift.
#define SWIFT_UNSAFE_REFERENCE \
__attribute__((swift_attr("import_reference"))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(retain:immortal)))) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(release:immortal))))

/// Specifies a name that will be used in Swift for this declaration instead of its original name.
#define SWIFT_NAME(_name) __attribute__((swift_name(#_name)))

#define CONFORMS_TO(_name) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(conforms_to:_name))))
/// Specifies that a specific C++ `class` or `struct` conforms to a
/// a specific Swift protocol.
///
/// This example shows how to use this macro to conform a class template to a Swift protocol:
/// ```
/// template<class T>
/// class SWIFT_CONFORMS_TO_PROTOCOL(SwiftModule.ProtocolName)
/// CustomClass {};
/// ```
#define SWIFT_CONFORMS_TO_PROTOCOL(_moduleName_protocolName) \
__attribute__((swift_attr(_CXX_INTEROP_STRINGIFY(conforms_to:_moduleName_protocolName))))

#endif // SWIFT_CLANGIMPORTER_SWIFT_INTEROP_SUPPORT_H
17 changes: 17 additions & 0 deletions lib/ClangImporter/module.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//===------------------ module.modulemap - C++ and Swift module -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

module SwiftBridging {
header "bridging"

export *
}
12 changes: 4 additions & 8 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3910,8 +3910,7 @@ void MissingMemberFailure::diagnoseUnsafeCxxMethod(SourceLoc loc,
ctx.Diags
.diagnose(methodSwiftLoc, diag::mark_safe_to_import,
name.getBaseIdentifier().str())
.fixItInsert(methodSwiftLoc,
" SAFE_TO_IMPORT ");
.fixItInsert(methodSwiftLoc, " SWIFT_RETURNS_INDEPENDENT_VALUE ");
} else if (cxxMethod->getReturnType()->isReferenceType()) {
// Rewrite a call to .at(42) as a subscript.
if (name.getBaseIdentifier().is("at") &&
Expand Down Expand Up @@ -3941,8 +3940,7 @@ void MissingMemberFailure::diagnoseUnsafeCxxMethod(SourceLoc loc,
ctx.Diags
.diagnose(methodSwiftLoc, diag::mark_safe_to_import,
name.getBaseIdentifier().str())
.fixItInsert(methodSwiftLoc,
" SAFE_TO_IMPORT ");
.fixItInsert(methodSwiftLoc, " SWIFT_RETURNS_INDEPENDENT_VALUE ");
}
} else if (cxxMethod->getReturnType()->isRecordType()) {
if (auto cxxRecord = dyn_cast<clang::CXXRecordDecl>(
Expand All @@ -3967,12 +3965,10 @@ void MissingMemberFailure::diagnoseUnsafeCxxMethod(SourceLoc loc,
ctx.Diags
.diagnose(methodSwiftLoc, diag::mark_safe_to_import,
name.getBaseIdentifier().str())
.fixItInsert(methodSwiftLoc,
" SAFE_TO_IMPORT ");
.fixItInsert(methodSwiftLoc, " SWIFT_RETURNS_INDEPENDENT_VALUE ");
ctx.Diags
.diagnose(baseSwiftLoc, diag::mark_self_contained, returnTypeStr)
.fixItInsert(baseSwiftLoc,
"SELF_CONTAINED ");
.fixItInsert(baseSwiftLoc, "SWIFT_SELF_CONTAINED ");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ struct X {
import Test

public func test(x: X) {
// CHECK: note: Mark method 'test' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
// CHECK: note: annotate method 'test' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
// CHECK: int *test() { }
// CHECK: ^
// CHECK: SAFE_TO_IMPORT
// CHECK: SWIFT_RETURNS_INDEPENDENT_VALUE

x.test()

// CHECK: note: Mark method 'other' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
// CHECK: note: annotate method 'other' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
// CHECK: Ptr other() { }
// CHECK: ^
// CHECK: SAFE_TO_IMPORT
// CHECK: SWIFT_RETURNS_INDEPENDENT_VALUE

// CHECK: note: Mark type 'Ptr' as 'SELF_CONTAINED' in C++ to make methods that use it available in Swift.
// CHECK: note: Mark type 'Ptr' as 'SWIFT_SELF_CONTAINED' in C++ to make methods that use it available in Swift.
// CHECK: struct Ptr {
// CHECK: ^
// CHECK: SELF_CONTAINED
// CHECK: SWIFT_SELF_CONTAINED
x.other()
}
6 changes: 3 additions & 3 deletions test/Interop/Cxx/class/invalid-class-errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ import Test
// CHECK: note: record 'A' is not automatically available: does not have a copy constructor or destructor. Does this type have reference semantics?
// CHECK: struct A {
// CHECK: ^
// CHECK: SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>)
// CHECK: SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>)
public func test(x: A) { }
// CHECK: note: record 'B' is not automatically available: does not have a copy constructor or destructor. Does this type have reference semantics?
// CHECK: struct {{.*}}B {
// CHECK: ^
// CHECK: SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>)
// CHECK: SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>)
public func test(x: B) { }
// CHECK: note: record 'Nested' is not automatically available: does not have a copy constructor or destructor. Does this type have reference semantics?
// CHECK: struct Nested {
// CHECK: ^
// CHECK: SWIFT_REFERENCE_TYPE(<#retain#>, <#release#>)
// CHECK: SWIFT_SHARED_REFERENCE(<#retain#>, <#release#>)
public func test(x: Namespace.Nested) { }
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ import Test
public func test(x: M) {
// CHECK: note: C++ method 'test1' that returns a pointer of type 'UnsafeMutablePointer' is unavailable.
// CHECK: note: C++ method 'test1' may return an interior pointer.
// CHECK: note: Mark method 'test1' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
// CHECK: note: annotate method 'test1' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
x.test1()
// CHECK: note: C++ method 'test2' that returns a reference of type 'UnsafeMutablePointer' is unavailable.
// CHECK: note: C++ method 'test2' may return an interior pointer.
// CHECK: note: Mark method 'test2' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
// CHECK: note: annotate method 'test2' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
x.test2()
// CHECK: note: C++ method 'test3' that returns a value of type 'Ptr' is unavailable.
// CHECK: note: C++ method 'test3' may return an interior pointer.
// CHECK: note: Mark method 'test3' as 'SAFE_TO_IMPORT' in C++ to make it available in Swift.
// CHECK: note: Mark type 'Ptr' as 'SELF_CONTAINED' in C++ to make methods that use it available in Swift.
// CHECK: note: annotate method 'test3' with 'SWIFT_RETURNS_INDEPENDENT_VALUE' in C++ to make it available in Swift
// CHECK: note: Mark type 'Ptr' as 'SWIFT_SELF_CONTAINED' in C++ to make methods that use it available in Swift.
x.test3()
// CHECK: note: C++ method 'begin' that returns an iterator is unavailable
// CHECK: note: C++ methods that return iterators are potentially unsafe. Try re-writing to use Swift iterator APIs.
Expand Down
88 changes: 88 additions & 0 deletions test/Interop/Cxx/ergonomics/swift-bridging-annotations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// RUN: rm -rf %t
// RUN: split-file %s %t

// RUN: %target-swift-frontend %t/SwiftMod.swift -module-name SwiftMod -emit-module -o %t/SwiftMod.swiftmodule -I %t -enable-experimental-cxx-interop -Xcc -DFIRSTPASS

// 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

// 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 -Xcc -DINCMOD | %FileCheck %s

//--- SwiftMod.swift

public protocol Proto {
}

//--- Inputs/module.modulemap
module CxxModule {
header "header.h"
requires cplusplus
}

//--- Inputs/header.h

// Note: in actuality, this will be included
// as <swift/bridging>, but in this test we include
// it directly.
#ifndef INCMOD
#include "bridging"
#else
#pragma clang module import SwiftBridging
#endif

class SWIFT_SELF_CONTAINED SelfContained {
public:
int *pointer;

SelfContained();

const int *returnsIndependent() const SWIFT_RETURNS_INDEPENDENT_VALUE;
};

class SWIFT_SHARED_REFERENCE(retainSharedObject, releaseSharedObject)
SharedObject {
public:
static SharedObject *create();
};

void retainSharedObject(SharedObject *);
void releaseSharedObject(SharedObject *);

class SWIFT_IMMORTAL_REFERENCE LoggerSingleton {
public:
LoggerSingleton(const LoggerSingleton &) = delete;
static LoggerSingleton *getInstance();
};

class SWIFT_UNSAFE_REFERENCE UnsafeNonCopyable {
public:
UnsafeNonCopyable(UnsafeNonCopyable &) = delete;
};

UnsafeNonCopyable *returnsPointerToUnsafeReference();
void takesPointerToUnsafeNonCopyable(UnsafeNonCopyable *);

class SWIFT_CONFORMS_TO_PROTOCOL(SwiftMod.Proto) ConformsTo {
public:
};


// CHECK: struct SelfContained {

// CHECK: func returnsIndependent() -> UnsafePointer<Int32>!

// CHECK: class SharedObject {
// CHECK: class func create() -> SharedObject!
// CHECK: func retainSharedObject(_: SharedObject!)
// CHECK: func releaseSharedObject(_: SharedObject!)

// CHECK: class LoggerSingleton {
// CHECK: class func getInstance() -> LoggerSingleton!
// CHECK: }

// CHECK: class UnsafeNonCopyable {
// CHECK: }
// CHECK: func returnsPointerToUnsafeReference() -> UnsafeNonCopyable!
// CHECK: func takesPointerToUnsafeNonCopyable(_: UnsafeNonCopyable!)

// CHECK: struct ConformsTo : Proto {

6 changes: 3 additions & 3 deletions test/Interop/Cxx/foreign-reference/value-type-errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ void takesRef(Ref r);
import Test

public func test(x: Ref) {
// CHECK: note: function uses foreign reference type 'Ref' as a value in the return types which breaks 'import_reference' contract (outlined in C++ Interop User Manual).
// CHECK: note: function uses foreign reference type 'Ref' as a value in the return types which breaks 'swift_shared_reference' contract (outlined in C++ Interop User Manual).
returnsRef()
// CHECK: note: function uses foreign reference type 'Ref' as a value in a parameter types which breaks 'import_reference' contract (outlined in C++ Interop User Manual).
// CHECK: note: function uses foreign reference type 'Ref' as a value in a parameter types which breaks 'swift_shared_reference' contract (outlined in C++ Interop User Manual).
takesRef(x)
}
}