From 23c5ae50add457c56f34ae998a6eafa24b2bf06a Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Thu, 8 Jun 2023 23:53:34 +0100 Subject: [PATCH 1/3] [cxx-interop] Add `std::set` initializer that takes a Swift Sequence rdar://107909624 (cherry picked from commit dd7e1775fc20cea2d9a6ba71172538c4100dff0f) --- .../ClangDerivedConformances.cpp | 23 +++++++++++++++++++ stdlib/public/Cxx/CxxSet.swift | 20 ++++++++++++++++ test/Interop/Cxx/stdlib/use-std-set.swift | 14 +++++++++++ 3 files changed, 57 insertions(+) diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index 89bd5d8ddd66d..59a7a60410aaa 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -586,10 +586,33 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, if (!valueType || !sizeType) return; + auto insertId = ctx.getIdentifier("__insertUnsafe"); + auto inserts = lookupDirectWithoutExtensions(decl, insertId); + FuncDecl *insert = nullptr; + for (auto candidate : inserts) { + if (auto candidateMethod = dyn_cast(candidate)) { + if (!candidateMethod->hasParameterList()) + continue; + auto params = candidateMethod->getParameters(); + if (params->size() != 1) + continue; + auto param = params->front(); + if (param->getType()->getCanonicalType() != + valueType->getUnderlyingType()->getCanonicalType()) + continue; + insert = candidateMethod; + break; + } + } + if (!insert) + return; + impl.addSynthesizedTypealias(decl, ctx.Id_Element, valueType->getUnderlyingType()); impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"), sizeType->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("InsertionResult"), + insert->getResultInterfaceType()); impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet}); } diff --git a/stdlib/public/Cxx/CxxSet.swift b/stdlib/public/Cxx/CxxSet.swift index 02dec94fb1193..e3f143da45b60 100644 --- a/stdlib/public/Cxx/CxxSet.swift +++ b/stdlib/public/Cxx/CxxSet.swift @@ -13,11 +13,31 @@ public protocol CxxSet { associatedtype Element associatedtype Size: BinaryInteger + associatedtype InsertionResult // std::pair + + init() + + @discardableResult + mutating func __insertUnsafe(_ element: Element) -> InsertionResult func count(_ element: Element) -> Size } extension CxxSet { + /// Creates a C++ set containing the elements of a Swift Sequence. + /// + /// This initializes the set by copying every element of the sequence. + /// + /// - Complexity: O(*n*), where *n* is the number of elements in the Swift + /// sequence + @inlinable + public init(_ sequence: S) where S.Element == Element { + self.init() + for item in sequence { + self.__insertUnsafe(item) + } + } + @inlinable public func contains(_ element: Element) -> Bool { return count(element) > 0 diff --git a/test/Interop/Cxx/stdlib/use-std-set.swift b/test/Interop/Cxx/stdlib/use-std-set.swift index 56f7b7672f166..95f0110908f10 100644 --- a/test/Interop/Cxx/stdlib/use-std-set.swift +++ b/test/Interop/Cxx/stdlib/use-std-set.swift @@ -47,4 +47,18 @@ StdSetTestSuite.test("MultisetOfCInt.contains") { expectFalse(s.contains(3)) } +StdSetTestSuite.test("SetOfCInt.init()") { + let s = SetOfCInt([1, 3, 5]) + expectTrue(s.contains(1)) + expectFalse(s.contains(2)) + expectTrue(s.contains(3)) +} + +StdSetTestSuite.test("UnorderedSetOfCInt.init()") { + let s = UnorderedSetOfCInt([1, 3, 5]) + expectTrue(s.contains(1)) + expectFalse(s.contains(2)) + expectTrue(s.contains(3)) +} + runAllTests() From 04668cdf67151e28efe3007910f7eb2a6c2fde32 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Tue, 13 Jun 2023 18:04:20 +0100 Subject: [PATCH 2/3] [cxx-interop] Optimize CxxSet initialization from a Swift Sequence rdar://107909624 (cherry picked from commit 042aff2a760b49fe8ff2360ce6eb443c29f6a093) --- stdlib/public/Cxx/CxxSet.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Cxx/CxxSet.swift b/stdlib/public/Cxx/CxxSet.swift index e3f143da45b60..d468f6deed52e 100644 --- a/stdlib/public/Cxx/CxxSet.swift +++ b/stdlib/public/Cxx/CxxSet.swift @@ -31,7 +31,7 @@ extension CxxSet { /// - Complexity: O(*n*), where *n* is the number of elements in the Swift /// sequence @inlinable - public init(_ sequence: S) where S.Element == Element { + public init(_ sequence: __shared S) where S.Element == Element { self.init() for item in sequence { self.__insertUnsafe(item) From 37b45da26fcab445343957921f95486a83c0a5bb Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Mon, 3 Jul 2023 13:44:17 -0700 Subject: [PATCH 3/3] [interop] ensure C++ stdlib set's insert methods are unsafe (cherry picked from commit 6c6092ebee46b1a163753424bcb28a04a1fac679) --- .../ClangDerivedConformances.cpp | 19 ++++++++++++++++++- lib/ClangImporter/ClangDerivedConformances.h | 2 ++ lib/ClangImporter/ClangImporter.cpp | 5 +++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index 59a7a60410aaa..7991e4987ff96 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -565,6 +565,23 @@ void swift::conformToCxxSequenceIfNeeded( } } +static bool isStdSetType(const clang::CXXRecordDecl *clangDecl) { + return isStdDecl(clangDecl, {"set", "unordered_set", "multiset"}); +} + +bool swift::isUnsafeStdMethod(const clang::CXXMethodDecl *methodDecl) { + auto parentDecl = + dyn_cast(methodDecl->getDeclContext()); + if (!parentDecl) + return false; + if (!isStdSetType(parentDecl)) + return false; + if (methodDecl->getDeclName().isIdentifier() && + methodDecl->getName() == "insert") + return true; + return false; +} + void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, NominalTypeDecl *decl, const clang::CXXRecordDecl *clangDecl) { @@ -576,7 +593,7 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, // Only auto-conform types from the C++ standard library. Custom user types // might have a similar interface but different semantics. - if (!isStdDecl(clangDecl, {"set", "unordered_set", "multiset"})) + if (!isStdSetType(clangDecl)) return; auto valueType = lookupDirectSingleWithoutExtensions( diff --git a/lib/ClangImporter/ClangDerivedConformances.h b/lib/ClangImporter/ClangDerivedConformances.h index 46616c677b702..3faeb3efdee05 100644 --- a/lib/ClangImporter/ClangDerivedConformances.h +++ b/lib/ClangImporter/ClangDerivedConformances.h @@ -20,6 +20,8 @@ namespace swift { bool isIterator(const clang::CXXRecordDecl *clangDecl); +bool isUnsafeStdMethod(const clang::CXXMethodDecl *methodDecl); + /// If the decl is a C++ input iterator, synthesize a conformance to the /// UnsafeCxxInputIterator protocol, which is defined in the Cxx module. void conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl, diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 3ec0611bcbe1d..d7a322ff0eeb1 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -6747,6 +6747,11 @@ bool IsSafeUseOfCxxDecl::evaluate(Evaluator &evaluator, method->getReturnType()->isReferenceType()) return false; + // Check if it's one of the known unsafe methods we currently + // mark as safe by default. + if (isUnsafeStdMethod(method)) + return false; + // Try to figure out the semantics of the return type. If it's a // pointer/iterator, it's unsafe. if (auto returnType = dyn_cast(