Skip to content

Commit f3cb88a

Browse files
authored
Merge pull request #67413 from zoecarver/pick-only-methods-on-self-contained-types-are-unsafe
2 parents 8e2b9e9 + 30c929f commit f3cb88a

File tree

13 files changed

+281
-15
lines changed

13 files changed

+281
-15
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: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,36 @@ public struct StringRef : CustomStringConvertible, NoReflectionChildren {
6464

6565
public var string: String { _bridged.string }
6666
public var description: String { string }
67-
67+
68+
public var count: Int {
69+
#if $NewCxxMethodSafetyHeuristics
70+
Int(_bridged.bytes_end() - _bridged.bytes_begin())
71+
#else
72+
Int(_bridged.__bytes_endUnsafe() - _bridged.__bytes_beginUnsafe())
73+
#endif
74+
}
75+
76+
public subscript(index: Int) -> UInt8 {
77+
#if $NewCxxMethodSafetyHeuristics
78+
let buffer = UnsafeBufferPointer<UInt8>(start: _bridged.bytes_begin(),
79+
count: count)
80+
#else
81+
let buffer = UnsafeBufferPointer<UInt8>(start: _bridged.__bytes_beginUnsafe(),
82+
count: count)
83+
#endif
84+
return buffer[index]
85+
}
86+
6887
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
6993
let lhsBuffer = UnsafeBufferPointer<UInt8>(
7094
start: lhs._bridged.__bytes_beginUnsafe(),
71-
count: Int(lhs._bridged.__bytes_endUnsafe() - lhs._bridged.__bytes_beginUnsafe()))
95+
count: lhs.count)
96+
#endif
7297
return rhs.withUTF8Buffer { (rhsBuffer: UnsafeBufferPointer<UInt8>) in
7398
if lhsBuffer.count != rhsBuffer.count { return false }
7499
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
@@ -3448,6 +3448,10 @@ static bool usesFeatureBuiltinModule(Decl *decl) {
34483448
return false;
34493449
}
34503450

3451+
static bool usesFeatureNewCxxMethodSafetyHeuristics(Decl *decl) {
3452+
return decl->hasClangNode();
3453+
}
3454+
34513455
/// Suppress the printing of a particular feature.
34523456
static void suppressingFeature(PrintOptions &options, Feature feature,
34533457
llvm::function_ref<void()> action) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6743,6 +6743,37 @@ CxxRecordAsSwiftType::evaluate(Evaluator &evaluator,
67436743
return nullptr;
67446744
}
67456745

6746+
bool anySubobjectsSelfContained(const clang::CXXRecordDecl *decl) {
6747+
if (!decl->getDefinition())
6748+
return false;
6749+
6750+
if (hasCustomCopyOrMoveConstructor(decl) || hasOwnedValueAttr(decl))
6751+
return true;
6752+
6753+
auto checkType = [](clang::QualType t) {
6754+
if (auto recordType = dyn_cast<clang::RecordType>(t.getCanonicalType())) {
6755+
if (auto cxxRecord =
6756+
dyn_cast<clang::CXXRecordDecl>(recordType->getDecl())) {
6757+
return anySubobjectsSelfContained(cxxRecord);
6758+
}
6759+
}
6760+
6761+
return false;
6762+
};
6763+
6764+
for (auto field : decl->fields()) {
6765+
if (checkType(field->getType()))
6766+
return true;
6767+
}
6768+
6769+
for (auto base : decl->bases()) {
6770+
if (checkType(base.getType()))
6771+
return true;
6772+
}
6773+
6774+
return false;
6775+
}
6776+
67466777
bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
67476778
SafeUseOfCxxDeclDescriptor desc) const {
67486779
const clang::Decl *decl = desc.decl;
@@ -6760,10 +6791,24 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
67606791
if (isForeignReferenceType(method->getReturnType()))
67616792
return true;
67626793

6763-
// If it returns a pointer or reference, that's a projection.
6794+
// begin and end methods likely return an interator, so they're unsafe. This
6795+
// is required so that automatic the conformance to RAC works properly.
6796+
if (method->getNameAsString() == "begin" ||
6797+
method->getNameAsString() == "end")
6798+
return false;
6799+
6800+
auto parentQualType = method
6801+
->getParent()->getTypeForDecl()->getCanonicalTypeUnqualified();
6802+
6803+
bool parentIsSelfContained =
6804+
!isForeignReferenceType(parentQualType) &&
6805+
anySubobjectsSelfContained(method->getParent());
6806+
6807+
// If it returns a pointer or reference from an owned parent, that's a
6808+
// projection (unsafe).
67646809
if (method->getReturnType()->isPointerType() ||
67656810
method->getReturnType()->isReferenceType())
6766-
return false;
6811+
return !parentIsSelfContained;
67676812

67686813
// Check if it's one of the known unsafe methods we currently
67696814
// mark as safe by default.
@@ -6778,20 +6823,22 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
67786823
dyn_cast<clang::CXXRecordDecl>(returnType->getDecl())) {
67796824
if (isSwiftClassType(cxxRecordReturnType))
67806825
return true;
6826+
67816827
if (hasIteratorAPIAttr(cxxRecordReturnType) ||
6782-
isIterator(cxxRecordReturnType)) {
6828+
isIterator(cxxRecordReturnType))
67836829
return false;
6784-
}
67856830

67866831
// Mark this as safe to help our diganostics down the road.
67876832
if (!cxxRecordReturnType->getDefinition()) {
67886833
return true;
67896834
}
67906835

6791-
if (!hasCustomCopyOrMoveConstructor(cxxRecordReturnType) &&
6836+
// A projection of a view type (such as a string_view) from a self
6837+
// contained parent is a projection (unsafe).
6838+
if (!anySubobjectsSelfContained(cxxRecordReturnType) &&
67926839
!hasOwnedValueAttr(cxxRecordReturnType) &&
67936840
hasPointerInSubobjects(cxxRecordReturnType)) {
6794-
return false;
6841+
return !parentIsSelfContained;
67956842
}
67966843
}
67976844
}

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)