Skip to content

Commit 288a0d6

Browse files
committed
[cxx-interop] Allow removing elements from std::vector
This adds `func remove(at index: Int)` to all instantiations of `std::vector` via an extension for `protocol CxxVector`. The original C++ method `std::vector::erase` is not visible in Swift because all of its overloads return unsafe iterators. rdar://113704853
1 parent 71c81d5 commit 288a0d6

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

lib/ClangImporter/ClangDerivedConformances.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,28 +1027,41 @@ void swift::conformToCxxVectorIfNeeded(ClangImporter::Implementation &impl,
10271027
decl, ctx.getIdentifier("value_type"));
10281028
auto iterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
10291029
decl, ctx.getIdentifier("const_iterator"));
1030-
if (!valueType || !iterType)
1030+
auto mutableIterType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
1031+
decl, ctx.getIdentifier("iterator"));
1032+
if (!valueType || !iterType || !mutableIterType)
10311033
return;
10321034

10331035
ProtocolDecl *cxxRandomAccessIteratorProto =
10341036
ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator);
1035-
if (!cxxRandomAccessIteratorProto)
1037+
ProtocolDecl *cxxMutableRandomAccessIteratorProto =
1038+
ctx.getProtocol(KnownProtocolKind::UnsafeCxxMutableRandomAccessIterator);
1039+
if (!cxxRandomAccessIteratorProto || !cxxMutableRandomAccessIteratorProto)
10361040
return;
10371041

10381042
auto rawIteratorTy = iterType->getUnderlyingType();
1043+
auto rawMutableIteratorTy = mutableIterType->getUnderlyingType();
10391044

10401045
// Check if RawIterator conforms to UnsafeCxxRandomAccessIterator.
10411046
auto rawIteratorConformanceRef =
10421047
ModuleDecl::lookupConformance(rawIteratorTy, cxxRandomAccessIteratorProto);
10431048
if (!isConcreteAndValid(rawIteratorConformanceRef))
10441049
return;
10451050

1051+
// Check if RawMutableIterator conforms to UnsafeCxxMutableInputIterator.
1052+
auto rawMutableIteratorConformanceRef = module->lookupConformance(
1053+
rawMutableIteratorTy, cxxMutableRandomAccessIteratorProto);
1054+
if (!isConcreteAndValid(rawMutableIteratorConformanceRef, module))
1055+
return;
1056+
10461057
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
10471058
valueType->getUnderlyingType());
10481059
impl.addSynthesizedTypealias(decl, ctx.Id_ArrayLiteralElement,
10491060
valueType->getUnderlyingType());
10501061
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"),
10511062
rawIteratorTy);
1063+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawMutableIterator"),
1064+
rawMutableIteratorTy);
10521065
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxVector});
10531066
}
10541067

stdlib/public/Cxx/CxxVector.swift

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@ public protocol CxxVector<Element>: ExpressibleByArrayLiteral {
1717
associatedtype Element
1818
associatedtype RawIterator: UnsafeCxxRandomAccessIterator
1919
where RawIterator.Pointee == Element
20+
associatedtype RawMutableIterator: UnsafeCxxMutableRandomAccessIterator
21+
where RawMutableIterator.Pointee == Element
2022

2123
init()
2224

25+
/// Do not implement this function manually in Swift.
26+
mutating func __beginUnsafe() -> RawIterator
27+
2328
mutating func push_back(_ element: Element)
29+
30+
/// Do not implement this function manually in Swift.
31+
@discardableResult
32+
mutating func __eraseUnsafe(_ iterator: RawIterator) -> RawMutableIterator
2433
}
2534

2635
extension CxxVector {
@@ -37,11 +46,20 @@ extension CxxVector {
3746
self.push_back(item)
3847
}
3948
}
40-
}
4149

42-
extension CxxVector {
4350
@inlinable
4451
public init(arrayLiteral elements: Element...) {
4552
self.init(elements)
4653
}
54+
55+
@discardableResult
56+
@inlinable
57+
public mutating func remove(at index: Int) -> Element {
58+
// Not using CxxIterator here to avoid making a copy of the collection.
59+
var rawIterator = self.__beginUnsafe()
60+
rawIterator += RawIterator.Distance(index)
61+
let element = rawIterator.pointee
62+
self.__eraseUnsafe(rawIterator)
63+
return element
64+
}
4765
}

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,48 @@ func fill(vector v: inout Vector) {
8585
v.push_back(CInt(3))
8686
}
8787

88+
StdVectorTestSuite.test("VectorOfInt.remove(at:)") {
89+
var v = Vector()
90+
fill(vector: &v)
91+
92+
let rm1 = v.remove(at: 1)
93+
expectEqual(rm1, 2)
94+
expectEqual(v.size(), 2)
95+
expectEqual(v[0], 1)
96+
expectEqual(v[1], 3)
97+
98+
let rm2 = v.remove(at: 0)
99+
expectEqual(rm2, 1)
100+
expectEqual(v.size(), 1)
101+
expectEqual(v[0], 3)
102+
}
103+
104+
StdVectorTestSuite.test("VectorOfString.remove(at:)") {
105+
var v = VectorOfString()
106+
v.push_back(std.string())
107+
v.push_back(std.string("123"))
108+
v.push_back(std.string("abc"))
109+
v.push_back(std.string("qwe"))
110+
111+
let rm1 = v.remove(at: 3)
112+
expectEqual(rm1, std.string("qwe"))
113+
expectEqual(v.size(), 3)
114+
expectEqual(v[0], std.string())
115+
expectEqual(v[1], std.string("123"))
116+
expectEqual(v[2], std.string("abc"))
117+
118+
let rm2 = v.remove(at: 1)
119+
expectEqual(rm2, std.string("123"))
120+
expectEqual(v.size(), 2)
121+
122+
v.remove(at: 0)
123+
expectEqual(v.size(), 1)
124+
125+
v.remove(at: 0)
126+
expectEqual(v.size(), 0)
127+
expectTrue(v.empty())
128+
}
129+
88130
StdVectorTestSuite.test("VectorOfInt for loop") {
89131
var v = Vector()
90132
fill(vector: &v)

0 commit comments

Comments
 (0)