Skip to content

Commit 7db5fe1

Browse files
authored
Merge pull request #67296 from zoecarver/only-methods-on-self-contained-types-are-unsafe
[cxx-interop] Only mark projections of self-contained types as unsafe.
2 parents 445a654 + e670a0e commit 7db5fe1

File tree

13 files changed

+270
-14
lines changed

13 files changed

+270
-14
lines changed

SwiftCompilerSources/Sources/Basic/SourceLoc.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ public struct SourceLoc {
2727
guard bridged.isValid() else {
2828
return nil
2929
}
30+
#if $NewCxxMethodSafetyHeuristics
31+
self.locationInFile = bridged.getOpaquePointerValue().assumingMemoryBound(to: UInt8.self)
32+
#else
3033
self.locationInFile = bridged.__getOpaquePointerValueUnsafe().assumingMemoryBound(to: UInt8.self)
34+
#endif
3135
}
3236

3337
public var bridged: swift.SourceLoc {
@@ -57,9 +61,15 @@ public struct CharSourceRange {
5761
}
5862

5963
public init?(bridged: swift.CharSourceRange) {
64+
#if $NewCxxMethodSafetyHeuristics
65+
guard let start = SourceLoc(bridged: bridged.getStart()) else {
66+
return nil
67+
}
68+
#else
6069
guard let start = SourceLoc(bridged: bridged.__getStartUnsafe()) else {
6170
return nil
6271
}
72+
#endif
6373
self.init(start: start, byteLength: bridged.getByteLength())
6474
}
6575

SwiftCompilerSources/Sources/Basic/Utils.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,34 @@ public struct StringRef : CustomStringConvertible, NoReflectionChildren {
6666
public var description: String { string }
6767

6868
public var count: Int {
69+
#if $NewCxxMethodSafetyHeuristics
70+
Int(_bridged.bytes_end() - _bridged.bytes_begin())
71+
#else
6972
Int(_bridged.__bytes_endUnsafe() - _bridged.__bytes_beginUnsafe())
73+
#endif
7074
}
7175

7276
public subscript(index: Int) -> UInt8 {
73-
let buffer = UnsafeBufferPointer<UInt8>(start: _bridged.__bytes_beginUnsafe(),
77+
#if $NewCxxMethodSafetyHeuristics
78+
let buffer = UnsafeBufferPointer<UInt8>(start: _bridged.bytes_begin(),
7479
count: count)
80+
#else
81+
let buffer = UnsafeBufferPointer<UInt8>(start: _bridged.bytes_begin(),
82+
count: count)
83+
#endif
7584
return buffer[index]
7685
}
7786

7887
public static func ==(lhs: StringRef, rhs: StaticString) -> Bool {
88+
#if $NewCxxMethodSafetyHeuristics
89+
let lhsBuffer = UnsafeBufferPointer<UInt8>(
90+
start: lhs._bridged.bytes_begin(),
91+
count: lhs.count)
92+
#else
7993
let lhsBuffer = UnsafeBufferPointer<UInt8>(
8094
start: lhs._bridged.__bytes_beginUnsafe(),
8195
count: lhs.count)
96+
#endif
8297
return rhs.withUTF8Buffer { (rhsBuffer: UnsafeBufferPointer<UInt8>) in
8398
if lhsBuffer.count != rhsBuffer.count { return false }
8499
return lhsBuffer.elementsEqual(rhsBuffer, by: ==)

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ LANGUAGE_FEATURE(BuiltinStackAlloc, 0, "Builtin.stackAlloc", true)
9191
LANGUAGE_FEATURE(BuiltinUnprotectedStackAlloc, 0, "Builtin.unprotectedStackAlloc", true)
9292
LANGUAGE_FEATURE(BuiltinTaskRunInline, 0, "Builtin.taskRunInline", true)
9393
LANGUAGE_FEATURE(BuiltinUnprotectedAddressOf, 0, "Builtin.unprotectedAddressOf", true)
94+
LANGUAGE_FEATURE(NewCxxMethodSafetyHeuristics, 0, "Only import C++ methods that return pointers (projections) on owned types as unsafe", true)
9495
SUPPRESSIBLE_LANGUAGE_FEATURE(SpecializeAttributeWithAvailability, 0, "@_specialize attribute with availability", true)
9596
LANGUAGE_FEATURE(BuiltinAssumeAlignment, 0, "Builtin.assumeAlignment", true)
9697
LANGUAGE_FEATURE(BuiltinCreateTaskGroupWithFlags, 0, "Builtin.createTaskGroupWithFlags", true)

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,6 +3484,10 @@ static bool usesFeaturePlaygroundExtendedCallbacks(Decl *decl) {
34843484
return false;
34853485
}
34863486

3487+
static bool usesFeatureNewCxxMethodSafetyHeuristics(Decl *decl) {
3488+
return decl->hasClangNode();
3489+
}
3490+
34873491
/// Suppress the printing of a particular feature.
34883492
static void suppressingFeature(PrintOptions &options, Feature feature,
34893493
llvm::function_ref<void()> action) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6789,6 +6789,37 @@ CxxRecordAsSwiftType::evaluate(Evaluator &evaluator,
67896789
return nullptr;
67906790
}
67916791

6792+
bool anySubobjectsSelfContained(const clang::CXXRecordDecl *decl) {
6793+
if (!decl->getDefinition())
6794+
return false;
6795+
6796+
if (hasCustomCopyOrMoveConstructor(decl) || hasOwnedValueAttr(decl))
6797+
return true;
6798+
6799+
auto checkType = [](clang::QualType t) {
6800+
if (auto recordType = dyn_cast<clang::RecordType>(t.getCanonicalType())) {
6801+
if (auto cxxRecord =
6802+
dyn_cast<clang::CXXRecordDecl>(recordType->getDecl())) {
6803+
return anySubobjectsSelfContained(cxxRecord);
6804+
}
6805+
}
6806+
6807+
return false;
6808+
};
6809+
6810+
for (auto field : decl->fields()) {
6811+
if (checkType(field->getType()))
6812+
return true;
6813+
}
6814+
6815+
for (auto base : decl->bases()) {
6816+
if (checkType(base.getType()))
6817+
return true;
6818+
}
6819+
6820+
return false;
6821+
}
6822+
67926823
bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
67936824
SafeUseOfCxxDeclDescriptor desc) const {
67946825
const clang::Decl *decl = desc.decl;
@@ -6806,10 +6837,24 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
68066837
if (isForeignReferenceType(method->getReturnType()))
68076838
return true;
68086839

6809-
// If it returns a pointer or reference, that's a projection.
6840+
// begin and end methods likely return an interator, so they're unsafe. This
6841+
// is required so that automatic the conformance to RAC works properly.
6842+
if (method->getNameAsString() == "begin" ||
6843+
method->getNameAsString() == "end")
6844+
return false;
6845+
6846+
auto parentQualType = method
6847+
->getParent()->getTypeForDecl()->getCanonicalTypeUnqualified();
6848+
6849+
bool parentIsSelfContained =
6850+
!isForeignReferenceType(parentQualType) &&
6851+
anySubobjectsSelfContained(method->getParent());
6852+
6853+
// If it returns a pointer or reference from an owned parent, that's a
6854+
// projection (unsafe).
68106855
if (method->getReturnType()->isPointerType() ||
68116856
method->getReturnType()->isReferenceType())
6812-
return false;
6857+
return !parentIsSelfContained;
68136858

68146859
// Check if it's one of the known unsafe methods we currently
68156860
// mark as safe by default.
@@ -6824,20 +6869,22 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
68246869
dyn_cast<clang::CXXRecordDecl>(returnType->getDecl())) {
68256870
if (isSwiftClassType(cxxRecordReturnType))
68266871
return true;
6872+
68276873
if (hasIteratorAPIAttr(cxxRecordReturnType) ||
6828-
isIterator(cxxRecordReturnType)) {
6874+
isIterator(cxxRecordReturnType))
68296875
return false;
6830-
}
68316876

68326877
// Mark this as safe to help our diganostics down the road.
68336878
if (!cxxRecordReturnType->getDefinition()) {
68346879
return true;
68356880
}
68366881

6837-
if (!hasCustomCopyOrMoveConstructor(cxxRecordReturnType) &&
6882+
// A projection of a view type (such as a string_view) from a self
6883+
// contained parent is a proejction (unsafe).
6884+
if (!anySubobjectsSelfContained(cxxRecordReturnType) &&
68386885
!hasOwnedValueAttr(cxxRecordReturnType) &&
68396886
hasPointerInSubobjects(cxxRecordReturnType)) {
6840-
return false;
6887+
return !parentIsSelfContained;
68416888
}
68426889
}
68436890
}

test/Interop/Cxx/class/Inputs/type-classification.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ struct __attribute__((swift_attr("import_iterator"))) Iterator {
232232
};
233233

234234
struct HasMethodThatReturnsIterator {
235+
HasMethodThatReturnsIterator(const HasMethodThatReturnsIterator&);
235236
Iterator getIterator() const;
236237
};
237238

@@ -240,6 +241,7 @@ struct IteratorBox {
240241
};
241242

242243
struct HasMethodThatReturnsIteratorBox {
244+
HasMethodThatReturnsIteratorBox(const HasMethodThatReturnsIteratorBox&);
243245
IteratorBox getIteratorBox() const;
244246
};
245247

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ module Test {
1212
struct Ptr { int *p; };
1313

1414
struct X {
15+
X(const X&);
16+
1517
int *test() { }
1618
Ptr other() { }
1719
};

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ struct Ptr { int *p; };
1313
struct __attribute__((swift_attr("import_owned"))) StirngLiteral { const char *name; };
1414

1515
struct M {
16-
int *_Nonnull test1() const;
17-
int &test2() const;
18-
Ptr test3() const;
16+
M(const M&);
17+
int *_Nonnull test1() const;
18+
int &test2() const;
19+
Ptr test3() const;
1920

20-
int *begin() const;
21+
int *begin() const;
2122

22-
StirngLiteral stringLiteral() const { return StirngLiteral{"M"}; }
23+
StirngLiteral stringLiteral() const { return StirngLiteral{"M"}; }
2324
};
2425

2526
//--- test.swift

test/Interop/Cxx/class/method/Inputs/ambiguous_methods.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ struct Unsafe {
5252
};
5353

5454
struct HasAmbiguousUnsafeMethods {
55+
HasAmbiguousUnsafeMethods(const HasAmbiguousUnsafeMethods&);
5556
Unsafe getUnsafe() const { return Unsafe(); }
5657
Unsafe getUnsafe() { return Unsafe(); }
5758
};

test/Interop/Cxx/class/method/Inputs/module.modulemap

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,11 @@ module Methods {
66
module AmbiguousMethods {
77
header "ambiguous_methods.h"
88
requires cplusplus
9-
}
9+
}
10+
11+
module UnsafeProjections {
12+
header "unsafe-projections.h"
13+
requires cplusplus
14+
15+
export *
16+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_METHOD_UNSAFE_PROJECTIONS_H
2+
#define TEST_INTEROP_CXX_CLASS_METHOD_UNSAFE_PROJECTIONS_H
3+
4+
#include <string>
5+
6+
struct NestedSelfContained;
7+
struct Empty;
8+
struct SelfContained;
9+
struct ExplicitSelfContained;
10+
struct NestedExplicitSelfContained;
11+
12+
struct View {
13+
void *ptr;
14+
15+
void *data() const;
16+
void *empty() const;
17+
std::string name() const;
18+
NestedSelfContained nested() const;
19+
ExplicitSelfContained explicitSelfContained() const;
20+
NestedExplicitSelfContained explicitNested() const;
21+
};
22+
23+
struct SelfContained {
24+
void *ptr;
25+
SelfContained(const SelfContained&);
26+
27+
std::string name() const;
28+
SelfContained selfContained() const;
29+
NestedSelfContained nested() const;
30+
Empty empty() const;
31+
int value() const;
32+
View view() const;
33+
int *pointer() const;
34+
ExplicitSelfContained explicitSelfContained() const;
35+
NestedExplicitSelfContained explicitNested() const;
36+
};
37+
38+
struct NestedSelfContained {
39+
SelfContained member;
40+
41+
std::string name() const;
42+
SelfContained selfContained() const;
43+
NestedSelfContained nested() const;
44+
Empty empty() const;
45+
int value() const;
46+
View view() const;
47+
int *pointer() const;
48+
ExplicitSelfContained explicitSelfContained() const;
49+
NestedExplicitSelfContained explicitNested() const;
50+
};
51+
52+
struct InheritSelfContained: SelfContained {
53+
std::string name() const;
54+
SelfContained selfContained() const;
55+
NestedSelfContained nested() const;
56+
Empty empty() const;
57+
int value() const;
58+
View view() const;
59+
int *pointer() const;
60+
};
61+
62+
struct __attribute__((swift_attr("import_owned"))) ExplicitSelfContained {
63+
void *ptr;
64+
65+
void *pointer() const;
66+
View view() const;
67+
NestedSelfContained nested() const;
68+
};
69+
70+
struct NestedExplicitSelfContained {
71+
ExplicitSelfContained m;
72+
73+
SelfContained selfContained() const;
74+
NestedSelfContained nested() const;
75+
int value() const;
76+
View view() const;
77+
int *pointer() const;
78+
};
79+
80+
struct Empty {
81+
Empty empty() const;
82+
void *pointer() const;
83+
SelfContained selfContained() const;
84+
};
85+
86+
struct IntPair {
87+
int a; int b;
88+
89+
int first() const;
90+
void *pointer() const;
91+
SelfContained selfContained() const;
92+
};
93+
94+
#endif // TEST_INTEROP_CXX_CLASS_METHOD_UNSAFE_PROJECTIONS_H

0 commit comments

Comments
 (0)