Skip to content

Commit 4d01253

Browse files
authored
Merge pull request #76429 from swiftlang/gaborh/safe-cxx-interop-mode
[cxx-interop] Introduce a safe C++ interop mode
2 parents fb28133 + 929c0ca commit 4d01253

File tree

7 files changed

+86
-2
lines changed

7 files changed

+86
-2
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ SUPPRESSIBLE_EXPERIMENTAL_FEATURE(AllowUnsafeAttribute, true)
408408
/// Warn on use of unsafe constructs.
409409
EXPERIMENTAL_FEATURE(WarnUnsafe, true)
410410

411+
/// Import unsafe C and C++ constructs as @unsafe.
412+
EXPERIMENTAL_FEATURE(SafeInterop, true)
413+
411414
// Enable values in generic signatures, e.g. <let N: Int>
412415
EXPERIMENTAL_FEATURE(ValueGenerics, true)
413416

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ static bool usesFeatureAllowUnsafeAttribute(Decl *decl) {
301301
}
302302

303303
UNINTERESTING_FEATURE(WarnUnsafe)
304+
UNINTERESTING_FEATURE(SafeInterop)
304305

305306
static bool usesFeatureValueGenerics(Decl *decl) {
306307
auto genericContext = decl->getAsGenericContext();

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7391,6 +7391,10 @@ bool importer::hasNonEscapableAttr(const clang::RecordDecl *decl) {
73917391
return hasSwiftAttribute(decl, "~Escapable");
73927392
}
73937393

7394+
bool importer::hasEscapableAttr(const clang::RecordDecl *decl) {
7395+
return hasSwiftAttribute(decl, "Escapable");
7396+
}
7397+
73947398
/// Recursively checks that there are no pointers in any fields or base classes.
73957399
/// Does not check C++ records with specific API annotations.
73967400
static bool hasPointerInSubobjects(const clang::CXXRecordDecl *decl) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "swift/AST/PrettyStackTrace.h"
3838
#include "swift/AST/ProtocolConformance.h"
3939
#include "swift/AST/Stmt.h"
40+
#include "swift/AST/Type.h"
4041
#include "swift/AST/TypeCheckRequests.h"
4142
#include "swift/AST/Types.h"
4243
#include "swift/Basic/Assertions.h"
@@ -8154,6 +8155,21 @@ bool swift::importer::isMutabilityAttr(const clang::SwiftAttrAttr *swiftAttr) {
81548155
swiftAttr->getAttribute() == "nonmutating";
81558156
}
81568157

8158+
static bool importAsUnsafe(const ASTContext &context,
8159+
const clang::RecordDecl *decl,
8160+
const Decl *MappedDecl) {
8161+
if (!context.LangOpts.hasFeature(Feature::SafeInterop) ||
8162+
!context.LangOpts.hasFeature(Feature::AllowUnsafeAttribute) || !decl)
8163+
return false;
8164+
8165+
if (isa<ClassDecl>(MappedDecl))
8166+
return false;
8167+
8168+
// TODO: Add logic to cover structural rules.
8169+
return !importer::hasNonEscapableAttr(decl) &&
8170+
!importer::hasEscapableAttr(decl);
8171+
}
8172+
81578173
void
81588174
ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
81598175
auto ClangDecl =
@@ -8178,6 +8194,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
81788194
//
81798195
// __attribute__((swift_attr("attribute")))
81808196
//
8197+
bool seenUnsafe = false;
81818198
for (auto swiftAttr : ClangDecl->specific_attrs<clang::SwiftAttrAttr>()) {
81828199
// FIXME: Hard-code @MainActor and @UIActor, because we don't have a
81838200
// point at which to do name lookup for imported entities.
@@ -8287,8 +8304,7 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
82878304
if (swiftAttr->getAttribute() == "unsafe") {
82888305
if (!SwiftContext.LangOpts.hasFeature(Feature::AllowUnsafeAttribute))
82898306
continue;
8290-
auto attr = new (SwiftContext) UnsafeAttr(/*implicit=*/false);
8291-
MappedDecl->getAttrs().add(attr);
8307+
seenUnsafe = true;
82928308
continue;
82938309
}
82948310

@@ -8332,6 +8348,13 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
83328348
swiftAttr->getAttribute());
83338349
}
83348350
}
8351+
8352+
if (seenUnsafe ||
8353+
importAsUnsafe(SwiftContext, dyn_cast<clang::RecordDecl>(ClangDecl),
8354+
MappedDecl)) {
8355+
auto attr = new (SwiftContext) UnsafeAttr(/*implicit=*/!seenUnsafe);
8356+
MappedDecl->getAttrs().add(attr);
8357+
}
83358358
};
83368359
importAttrsFromDecl(ClangDecl);
83378360

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,8 @@ bool hasUnsafeAPIAttr(const clang::Decl *decl);
20312031

20322032
bool hasNonEscapableAttr(const clang::RecordDecl *decl);
20332033

2034+
bool hasEscapableAttr(const clang::RecordDecl *decl);
2035+
20342036
bool isViewType(const clang::CXXRecordDecl *decl);
20352037

20362038
} // end namespace importer

lib/ClangImporter/SwiftBridging/swift/bridging

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@
164164
#define SWIFT_NONESCAPABLE \
165165
__attribute__((swift_attr("~Escapable")))
166166

167+
/// Specifies that a specific c++ type such class or struct should be imported
168+
/// as a escapable Swift value. While this matches the default behavior,
169+
/// in safe mode interop mode it ensures that the type is not marked as
170+
/// unsafe.
171+
#define SWIFT_ESCAPABLE \
172+
__attribute__((swift_attr("Escapable")))
173+
167174
/// Specifies that the return value is passed as owned for C++ functions and
168175
/// methods returning types annotated as `SWIFT_SHARED_REFERENCE`
169176
#define SWIFT_RETURNS_RETAINED __attribute__((swift_attr("returns_retained")))
@@ -187,6 +194,7 @@
187194
#define SWIFT_UNCHECKED_SENDABLE
188195
#define SWIFT_NONCOPYABLE
189196
#define SWIFT_NONESCAPABLE
197+
#define SWIFT_ESCAPABLE
190198
#define SWIFT_RETURNS_RETAINED
191199
#define SWIFT_RETURNS_UNRETAINED
192200

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
// RUN: rm -rf %t
3+
// RUN: split-file %s %t
4+
// RUN: %target-swift-frontend -typecheck -verify -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t/Inputs %t/test.swift -enable-experimental-feature AllowUnsafeAttribute -enable-experimental-feature WarnUnsafe -enable-experimental-feature NonescapableTypes -enable-experimental-feature SafeInterop -cxx-interoperability-mode=default -diagnostic-style llvm 2>&1
5+
6+
// REQUIRES: objc_interop
7+
8+
//--- Inputs/module.modulemap
9+
module Test {
10+
header "nonescapable.h"
11+
requires cplusplus
12+
}
13+
14+
//--- Inputs/nonescapable.h
15+
#include "swift/bridging"
16+
17+
struct SWIFT_NONESCAPABLE View {
18+
__attribute__((swift_attr("@lifetime(immortal)")))
19+
View() : member(nullptr) {}
20+
__attribute__((swift_attr("@lifetime(p)")))
21+
View(const int *p [[clang::lifetimebound]]) : member(p) {}
22+
View(const View&) = default;
23+
private:
24+
const int *member;
25+
};
26+
27+
struct SWIFT_ESCAPABLE Owner {};
28+
29+
struct Unannotated {};
30+
31+
//--- test.swift
32+
33+
import Test
34+
import CoreFoundation
35+
36+
func useUnsafeParam(x: Unannotated) { // expected-warning{{reference to unsafe struct 'Unannotated'}}
37+
}
38+
39+
func useSafeParams(x: Owner, y: View) {
40+
}
41+
42+
func useCfType(x: CFArray) {
43+
}

0 commit comments

Comments
 (0)