Skip to content

Commit 444f62f

Browse files
authored
Merge pull request #66600 from apple/egorzhdan/5.9-cxx-set-init
🍒[cxx-interop] Add `std::set` initializer that takes a Swift Sequence
2 parents 4b521e3 + 37b45da commit 444f62f

File tree

5 files changed

+82
-1
lines changed

5 files changed

+82
-1
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,23 @@ void swift::conformToCxxSequenceIfNeeded(
565565
}
566566
}
567567

568+
static bool isStdSetType(const clang::CXXRecordDecl *clangDecl) {
569+
return isStdDecl(clangDecl, {"set", "unordered_set", "multiset"});
570+
}
571+
572+
bool swift::isUnsafeStdMethod(const clang::CXXMethodDecl *methodDecl) {
573+
auto parentDecl =
574+
dyn_cast<clang::CXXRecordDecl>(methodDecl->getDeclContext());
575+
if (!parentDecl)
576+
return false;
577+
if (!isStdSetType(parentDecl))
578+
return false;
579+
if (methodDecl->getDeclName().isIdentifier() &&
580+
methodDecl->getName() == "insert")
581+
return true;
582+
return false;
583+
}
584+
568585
void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl,
569586
NominalTypeDecl *decl,
570587
const clang::CXXRecordDecl *clangDecl) {
@@ -576,7 +593,7 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl,
576593

577594
// Only auto-conform types from the C++ standard library. Custom user types
578595
// might have a similar interface but different semantics.
579-
if (!isStdDecl(clangDecl, {"set", "unordered_set", "multiset"}))
596+
if (!isStdSetType(clangDecl))
580597
return;
581598

582599
auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
@@ -586,10 +603,33 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl,
586603
if (!valueType || !sizeType)
587604
return;
588605

606+
auto insertId = ctx.getIdentifier("__insertUnsafe");
607+
auto inserts = lookupDirectWithoutExtensions(decl, insertId);
608+
FuncDecl *insert = nullptr;
609+
for (auto candidate : inserts) {
610+
if (auto candidateMethod = dyn_cast<FuncDecl>(candidate)) {
611+
if (!candidateMethod->hasParameterList())
612+
continue;
613+
auto params = candidateMethod->getParameters();
614+
if (params->size() != 1)
615+
continue;
616+
auto param = params->front();
617+
if (param->getType()->getCanonicalType() !=
618+
valueType->getUnderlyingType()->getCanonicalType())
619+
continue;
620+
insert = candidateMethod;
621+
break;
622+
}
623+
}
624+
if (!insert)
625+
return;
626+
589627
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
590628
valueType->getUnderlyingType());
591629
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"),
592630
sizeType->getUnderlyingType());
631+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"),
632+
insert->getResultInterfaceType());
593633
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet});
594634
}
595635

lib/ClangImporter/ClangDerivedConformances.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ namespace swift {
2020

2121
bool isIterator(const clang::CXXRecordDecl *clangDecl);
2222

23+
bool isUnsafeStdMethod(const clang::CXXMethodDecl *methodDecl);
24+
2325
/// If the decl is a C++ input iterator, synthesize a conformance to the
2426
/// UnsafeCxxInputIterator protocol, which is defined in the Cxx module.
2527
void conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl,

lib/ClangImporter/ClangImporter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6738,6 +6738,11 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator,
67386738
method->getReturnType()->isReferenceType())
67396739
return false;
67406740

6741+
// Check if it's one of the known unsafe methods we currently
6742+
// mark as safe by default.
6743+
if (isUnsafeStdMethod(method))
6744+
return false;
6745+
67416746
// Try to figure out the semantics of the return type. If it's a
67426747
// pointer/iterator, it's unsafe.
67436748
if (auto returnType = dyn_cast<clang::RecordType>(

stdlib/public/Cxx/CxxSet.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,31 @@
1313
public protocol CxxSet<Element> {
1414
associatedtype Element
1515
associatedtype Size: BinaryInteger
16+
associatedtype InsertionResult // std::pair<iterator, bool>
17+
18+
init()
19+
20+
@discardableResult
21+
mutating func __insertUnsafe(_ element: Element) -> InsertionResult
1622

1723
func count(_ element: Element) -> Size
1824
}
1925

2026
extension CxxSet {
27+
/// Creates a C++ set containing the elements of a Swift Sequence.
28+
///
29+
/// This initializes the set by copying every element of the sequence.
30+
///
31+
/// - Complexity: O(*n*), where *n* is the number of elements in the Swift
32+
/// sequence
33+
@inlinable
34+
public init<S: Sequence>(_ sequence: __shared S) where S.Element == Element {
35+
self.init()
36+
for item in sequence {
37+
self.__insertUnsafe(item)
38+
}
39+
}
40+
2141
@inlinable
2242
public func contains(_ element: Element) -> Bool {
2343
return count(element) > 0

test/Interop/Cxx/stdlib/use-std-set.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,18 @@ StdSetTestSuite.test("MultisetOfCInt.contains") {
4747
expectFalse(s.contains(3))
4848
}
4949

50+
StdSetTestSuite.test("SetOfCInt.init()") {
51+
let s = SetOfCInt([1, 3, 5])
52+
expectTrue(s.contains(1))
53+
expectFalse(s.contains(2))
54+
expectTrue(s.contains(3))
55+
}
56+
57+
StdSetTestSuite.test("UnorderedSetOfCInt.init()") {
58+
let s = UnorderedSetOfCInt([1, 3, 5])
59+
expectTrue(s.contains(1))
60+
expectFalse(s.contains(2))
61+
expectTrue(s.contains(3))
62+
}
63+
5064
runAllTests()

0 commit comments

Comments
 (0)