Skip to content

Commit 9f54289

Browse files
authored
Merge pull request #62293 from apple/egorzhdan/synthesize-cxx-convertible
[cxx-interop] Synthesize conformances to `CxxConvertibleToCollection`
2 parents 7421c07 + cb562d2 commit 9f54289

File tree

6 files changed

+63
-47
lines changed

6 files changed

+63
-47
lines changed

include/swift/AST/KnownProtocols.def

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ PROTOCOL(DistributedTargetInvocationDecoder)
105105
PROTOCOL(DistributedTargetInvocationResultHandler)
106106

107107
// C++ Standard Library Overlay:
108+
PROTOCOL(CxxConvertibleToCollection)
108109
PROTOCOL(CxxRandomAccessCollection)
109110
PROTOCOL(CxxSequence)
110111
PROTOCOL(UnsafeCxxInputIterator)

lib/AST/ASTContext.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
11181118
case KnownProtocolKind::DistributedTargetInvocationResultHandler:
11191119
M = getLoadedModule(Id_Distributed);
11201120
break;
1121+
case KnownProtocolKind::CxxConvertibleToCollection:
11211122
case KnownProtocolKind::CxxRandomAccessCollection:
11221123
case KnownProtocolKind::CxxSequence:
11231124
case KnownProtocolKind::UnsafeCxxInputIterator:

lib/ClangImporter/ClangDerivedConformances.cpp

+55-38
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ void swift::conformToCxxSequenceIfNeeded(
319319
ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator);
320320
ProtocolDecl *cxxSequenceProto =
321321
ctx.getProtocol(KnownProtocolKind::CxxSequence);
322+
ProtocolDecl *cxxConvertibleProto =
323+
ctx.getProtocol(KnownProtocolKind::CxxConvertibleToCollection);
322324
// If the Cxx module is missing, or does not include one of the necessary
323325
// protocols, bail.
324326
if (!cxxIteratorProto || !cxxSequenceProto)
@@ -389,47 +391,62 @@ void swift::conformToCxxSequenceIfNeeded(
389391

390392
// Try to conform to CxxRandomAccessCollection if possible.
391393

392-
auto cxxRAIteratorProto =
393-
ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator);
394-
if (!cxxRAIteratorProto ||
395-
!ctx.getProtocol(KnownProtocolKind::CxxRandomAccessCollection))
396-
return;
397-
398-
// Check if `begin()` and `end()` are non-mutating.
399-
if (begin->isMutating() || end->isMutating())
400-
return;
394+
auto tryToConformToRandomAccessCollection = [&]() -> bool {
395+
auto cxxRAIteratorProto =
396+
ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator);
397+
if (!cxxRAIteratorProto ||
398+
!ctx.getProtocol(KnownProtocolKind::CxxRandomAccessCollection))
399+
return false;
401400

402-
// Check if RawIterator conforms to UnsafeCxxRandomAccessIterator.
403-
auto rawIteratorRAConformanceRef =
404-
decl->getModuleContext()->lookupConformance(rawIteratorTy,
405-
cxxRAIteratorProto);
406-
if (!isConcreteAndValid(rawIteratorRAConformanceRef, module))
407-
return;
401+
// Check if `begin()` and `end()` are non-mutating.
402+
if (begin->isMutating() || end->isMutating())
403+
return false;
408404

409-
// CxxRandomAccessCollection always uses Int as an Index.
410-
auto indexTy = ctx.getIntType();
405+
// Check if RawIterator conforms to UnsafeCxxRandomAccessIterator.
406+
auto rawIteratorRAConformanceRef =
407+
decl->getModuleContext()->lookupConformance(rawIteratorTy,
408+
cxxRAIteratorProto);
409+
if (!isConcreteAndValid(rawIteratorRAConformanceRef, module))
410+
return false;
411411

412-
auto sliceTy = ctx.getSliceType();
413-
sliceTy = sliceTy.subst(
414-
[&](SubstitutableType *dependentType) {
415-
if (dependentType->isEqual(cxxSequenceSelfTy))
416-
return declSelfTy;
417-
return Type(dependentType);
418-
},
419-
LookUpConformanceInModule(module));
412+
// CxxRandomAccessCollection always uses Int as an Index.
413+
auto indexTy = ctx.getIntType();
414+
415+
auto sliceTy = ctx.getSliceType();
416+
sliceTy = sliceTy.subst(
417+
[&](SubstitutableType *dependentType) {
418+
if (dependentType->isEqual(cxxSequenceSelfTy))
419+
return declSelfTy;
420+
return Type(dependentType);
421+
},
422+
LookUpConformanceInModule(module));
423+
424+
auto indicesTy = ctx.getRangeType();
425+
indicesTy = indicesTy.subst(
426+
[&](SubstitutableType *dependentType) {
427+
if (dependentType->isEqual(cxxSequenceSelfTy))
428+
return indexTy;
429+
return Type(dependentType);
430+
},
431+
LookUpConformanceInModule(module));
432+
433+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Index"), indexTy);
434+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Indices"), indicesTy);
435+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("SubSequence"),
436+
sliceTy);
437+
impl.addSynthesizedProtocolAttrs(
438+
decl, {KnownProtocolKind::CxxRandomAccessCollection});
439+
return true;
440+
};
420441

421-
auto indicesTy = ctx.getRangeType();
422-
indicesTy = indicesTy.subst(
423-
[&](SubstitutableType *dependentType) {
424-
if (dependentType->isEqual(cxxSequenceSelfTy))
425-
return indexTy;
426-
return Type(dependentType);
427-
},
428-
LookUpConformanceInModule(module));
442+
bool conformedToRAC = tryToConformToRandomAccessCollection();
429443

430-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Index"), indexTy);
431-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Indices"), indicesTy);
432-
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("SubSequence"), sliceTy);
433-
impl.addSynthesizedProtocolAttrs(
434-
decl, {KnownProtocolKind::CxxRandomAccessCollection});
444+
// If the collection does not support random access, let's still allow the
445+
// developer to explicitly convert a C++ sequence to a Swift Array (making a
446+
// copy of the sequence's elements) by conforming the type to
447+
// CxxCollectionConvertible. This enables an overload of Array.init declared
448+
// in the Cxx module.
449+
if (!conformedToRAC && cxxConvertibleProto)
450+
impl.addSynthesizedProtocolAttrs(
451+
decl, {KnownProtocolKind::CxxConvertibleToCollection});
435452
}

lib/IRGen/GenMeta.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -5886,6 +5886,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
58865886
case KnownProtocolKind::DistributedTargetInvocationEncoder:
58875887
case KnownProtocolKind::DistributedTargetInvocationDecoder:
58885888
case KnownProtocolKind::DistributedTargetInvocationResultHandler:
5889+
case KnownProtocolKind::CxxConvertibleToCollection:
58895890
case KnownProtocolKind::CxxRandomAccessCollection:
58905891
case KnownProtocolKind::CxxSequence:
58915892
case KnownProtocolKind::UnsafeCxxInputIterator:

test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift

-4
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import Cxx
99

1010
var CxxSequenceTestSuite = TestSuite("CxxConvertibleToCollection")
1111

12-
extension SimpleSequence: CxxConvertibleToCollection {}
13-
1412
CxxSequenceTestSuite.test("SimpleSequence to Swift.Array") {
1513
let seq = SimpleSequence()
1614
let array = Array(seq)
@@ -23,8 +21,6 @@ CxxSequenceTestSuite.test("SimpleSequence to Swift.Set") {
2321
expectEqual(Set([1, 2, 3, 4] as [Int32]), set)
2422
}
2523

26-
extension SimpleEmptySequence: CxxConvertibleToCollection {}
27-
2824
CxxSequenceTestSuite.test("SimpleEmptySequence to Swift.Array") {
2925
let seq = SimpleEmptySequence()
3026
let array = Array(seq)

test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomSequence -source-filename=x -I %S/Inputs -enable-experimental-cxx-interop -module-cache-path %t | %FileCheck %s
22

3-
// CHECK: struct SimpleSequence {
3+
// CHECK: struct SimpleSequence : CxxConvertibleToCollection {
44
// CHECK: typealias Element = ConstIterator.Pointee
55
// CHECK: typealias Iterator = CxxIterator<SimpleSequence>
66
// CHECK: typealias RawIterator = ConstIterator
77
// CHECK: }
88

9-
// CHECK: struct SimpleSequenceWithOutOfLineEqualEqual {
9+
// CHECK: struct SimpleSequenceWithOutOfLineEqualEqual : CxxConvertibleToCollection {
1010
// CHECK: typealias Element = ConstIteratorOutOfLineEq.Pointee
1111
// CHECK: typealias Iterator = CxxIterator<SimpleSequenceWithOutOfLineEqualEqual>
1212
// CHECK: typealias RawIterator = ConstIteratorOutOfLineEq
1313
// CHECK: }
1414

15-
// CHECK: struct SimpleArrayWrapperNullableIterators {
15+
// CHECK: struct SimpleArrayWrapperNullableIterators : CxxConvertibleToCollection {
1616
// CHECK: typealias Element = Optional<UnsafePointer<Int32>>.Pointee
1717
// CHECK: typealias Iterator = CxxIterator<SimpleArrayWrapperNullableIterators>
1818
// CHECK: typealias RawIterator = UnsafePointer<Int32>?
1919
// CHECK: }
2020

21-
// CHECK: struct SimpleEmptySequence {
21+
// CHECK: struct SimpleEmptySequence : CxxConvertibleToCollection {
2222
// CHECK: typealias Element = Optional<UnsafePointer<Int32>>.Pointee
2323
// CHECK: typealias Iterator = CxxIterator<SimpleEmptySequence>
2424
// CHECK: typealias RawIterator = UnsafePointer<Int32>?
2525
// CHECK: }
2626

27-
// CHECK: struct HasMutatingBeginEnd {
27+
// CHECK: struct HasMutatingBeginEnd : CxxConvertibleToCollection {
2828
// CHECK: typealias Element = ConstIterator.Pointee
2929
// CHECK: typealias Iterator = CxxIterator<HasMutatingBeginEnd>
3030
// CHECK: typealias RawIterator = ConstIterator

0 commit comments

Comments
 (0)