From cb562d26b80069c823bf8284dc5a926bbeaa3ac8 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Tue, 29 Nov 2022 14:28:17 +0000 Subject: [PATCH] [cxx-interop] Synthesize conformances to `CxxConvertibleToCollection` This extends the existing auto-conformance mechanism to synthesize the conformances to `CxxConvertibleToCollection` protocol for C++ sequence types. This means that the developer can now call `Array(myCxxSequence)` or `Set(myCxxSequence)` without adding any extensions manually. --- include/swift/AST/KnownProtocols.def | 1 + lib/AST/ASTContext.cpp | 1 + .../ClangDerivedConformances.cpp | 93 +++++++++++-------- lib/IRGen/GenMeta.cpp | 1 + .../custom-convertible-to-collection.swift | 4 - .../custom-sequence-module-interface.swift | 10 +- 6 files changed, 63 insertions(+), 47 deletions(-) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 13d2a1a1b693c..4f86cca0a36e4 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -105,6 +105,7 @@ PROTOCOL(DistributedTargetInvocationDecoder) PROTOCOL(DistributedTargetInvocationResultHandler) // C++ Standard Library Overlay: +PROTOCOL(CxxConvertibleToCollection) PROTOCOL(CxxRandomAccessCollection) PROTOCOL(CxxSequence) PROTOCOL(UnsafeCxxInputIterator) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b915f89013699..d5113a6c6ffd7 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1126,6 +1126,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::DistributedTargetInvocationResultHandler: M = getLoadedModule(Id_Distributed); break; + case KnownProtocolKind::CxxConvertibleToCollection: case KnownProtocolKind::CxxRandomAccessCollection: case KnownProtocolKind::CxxSequence: case KnownProtocolKind::UnsafeCxxInputIterator: diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index ef3c255dd80e6..2cf63e00e39ff 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -319,6 +319,8 @@ void swift::conformToCxxSequenceIfNeeded( ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator); ProtocolDecl *cxxSequenceProto = ctx.getProtocol(KnownProtocolKind::CxxSequence); + ProtocolDecl *cxxConvertibleProto = + ctx.getProtocol(KnownProtocolKind::CxxConvertibleToCollection); // If the Cxx module is missing, or does not include one of the necessary // protocols, bail. if (!cxxIteratorProto || !cxxSequenceProto) @@ -389,47 +391,62 @@ void swift::conformToCxxSequenceIfNeeded( // Try to conform to CxxRandomAccessCollection if possible. - auto cxxRAIteratorProto = - ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator); - if (!cxxRAIteratorProto || - !ctx.getProtocol(KnownProtocolKind::CxxRandomAccessCollection)) - return; - - // Check if `begin()` and `end()` are non-mutating. - if (begin->isMutating() || end->isMutating()) - return; + auto tryToConformToRandomAccessCollection = [&]() -> bool { + auto cxxRAIteratorProto = + ctx.getProtocol(KnownProtocolKind::UnsafeCxxRandomAccessIterator); + if (!cxxRAIteratorProto || + !ctx.getProtocol(KnownProtocolKind::CxxRandomAccessCollection)) + return false; - // Check if RawIterator conforms to UnsafeCxxRandomAccessIterator. - auto rawIteratorRAConformanceRef = - decl->getModuleContext()->lookupConformance(rawIteratorTy, - cxxRAIteratorProto); - if (!isConcreteAndValid(rawIteratorRAConformanceRef, module)) - return; + // Check if `begin()` and `end()` are non-mutating. + if (begin->isMutating() || end->isMutating()) + return false; - // CxxRandomAccessCollection always uses Int as an Index. - auto indexTy = ctx.getIntType(); + // Check if RawIterator conforms to UnsafeCxxRandomAccessIterator. + auto rawIteratorRAConformanceRef = + decl->getModuleContext()->lookupConformance(rawIteratorTy, + cxxRAIteratorProto); + if (!isConcreteAndValid(rawIteratorRAConformanceRef, module)) + return false; - auto sliceTy = ctx.getSliceType(); - sliceTy = sliceTy.subst( - [&](SubstitutableType *dependentType) { - if (dependentType->isEqual(cxxSequenceSelfTy)) - return declSelfTy; - return Type(dependentType); - }, - LookUpConformanceInModule(module)); + // CxxRandomAccessCollection always uses Int as an Index. + auto indexTy = ctx.getIntType(); + + auto sliceTy = ctx.getSliceType(); + sliceTy = sliceTy.subst( + [&](SubstitutableType *dependentType) { + if (dependentType->isEqual(cxxSequenceSelfTy)) + return declSelfTy; + return Type(dependentType); + }, + LookUpConformanceInModule(module)); + + auto indicesTy = ctx.getRangeType(); + indicesTy = indicesTy.subst( + [&](SubstitutableType *dependentType) { + if (dependentType->isEqual(cxxSequenceSelfTy)) + return indexTy; + return Type(dependentType); + }, + LookUpConformanceInModule(module)); + + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Index"), indexTy); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Indices"), indicesTy); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("SubSequence"), + sliceTy); + impl.addSynthesizedProtocolAttrs( + decl, {KnownProtocolKind::CxxRandomAccessCollection}); + return true; + }; - auto indicesTy = ctx.getRangeType(); - indicesTy = indicesTy.subst( - [&](SubstitutableType *dependentType) { - if (dependentType->isEqual(cxxSequenceSelfTy)) - return indexTy; - return Type(dependentType); - }, - LookUpConformanceInModule(module)); + bool conformedToRAC = tryToConformToRandomAccessCollection(); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Index"), indexTy); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Indices"), indicesTy); - impl.addSynthesizedTypealias(decl, ctx.getIdentifier("SubSequence"), sliceTy); - impl.addSynthesizedProtocolAttrs( - decl, {KnownProtocolKind::CxxRandomAccessCollection}); + // If the collection does not support random access, let's still allow the + // developer to explicitly convert a C++ sequence to a Swift Array (making a + // copy of the sequence's elements) by conforming the type to + // CxxCollectionConvertible. This enables an overload of Array.init declared + // in the Cxx module. + if (!conformedToRAC && cxxConvertibleProto) + impl.addSynthesizedProtocolAttrs( + decl, {KnownProtocolKind::CxxConvertibleToCollection}); } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index c7252d1a9da1f..0c8c4c9c3a1f6 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5884,6 +5884,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::DistributedTargetInvocationEncoder: case KnownProtocolKind::DistributedTargetInvocationDecoder: case KnownProtocolKind::DistributedTargetInvocationResultHandler: + case KnownProtocolKind::CxxConvertibleToCollection: case KnownProtocolKind::CxxRandomAccessCollection: case KnownProtocolKind::CxxSequence: case KnownProtocolKind::UnsafeCxxInputIterator: diff --git a/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift b/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift index 449699dfaeb06..d653413f3ff1a 100644 --- a/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift +++ b/test/Interop/Cxx/stdlib/overlay/custom-convertible-to-collection.swift @@ -9,8 +9,6 @@ import Cxx var CxxSequenceTestSuite = TestSuite("CxxConvertibleToCollection") -extension SimpleSequence: CxxConvertibleToCollection {} - CxxSequenceTestSuite.test("SimpleSequence to Swift.Array") { let seq = SimpleSequence() let array = Array(seq) @@ -23,8 +21,6 @@ CxxSequenceTestSuite.test("SimpleSequence to Swift.Set") { expectEqual(Set([1, 2, 3, 4] as [Int32]), set) } -extension SimpleEmptySequence: CxxConvertibleToCollection {} - CxxSequenceTestSuite.test("SimpleEmptySequence to Swift.Array") { let seq = SimpleEmptySequence() let array = Array(seq) diff --git a/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift b/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift index 01951b759c26c..3a6fa5c64ac36 100644 --- a/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift +++ b/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift @@ -1,30 +1,30 @@ // 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 -// CHECK: struct SimpleSequence { +// CHECK: struct SimpleSequence : CxxConvertibleToCollection { // CHECK: typealias Element = ConstIterator.Pointee // CHECK: typealias Iterator = CxxIterator // CHECK: typealias RawIterator = ConstIterator // CHECK: } -// CHECK: struct SimpleSequenceWithOutOfLineEqualEqual { +// CHECK: struct SimpleSequenceWithOutOfLineEqualEqual : CxxConvertibleToCollection { // CHECK: typealias Element = ConstIteratorOutOfLineEq.Pointee // CHECK: typealias Iterator = CxxIterator // CHECK: typealias RawIterator = ConstIteratorOutOfLineEq // CHECK: } -// CHECK: struct SimpleArrayWrapperNullableIterators { +// CHECK: struct SimpleArrayWrapperNullableIterators : CxxConvertibleToCollection { // CHECK: typealias Element = Optional>.Pointee // CHECK: typealias Iterator = CxxIterator // CHECK: typealias RawIterator = UnsafePointer? // CHECK: } -// CHECK: struct SimpleEmptySequence { +// CHECK: struct SimpleEmptySequence : CxxConvertibleToCollection { // CHECK: typealias Element = Optional>.Pointee // CHECK: typealias Iterator = CxxIterator // CHECK: typealias RawIterator = UnsafePointer? // CHECK: } -// CHECK: struct HasMutatingBeginEnd { +// CHECK: struct HasMutatingBeginEnd : CxxConvertibleToCollection { // CHECK: typealias Element = ConstIterator.Pointee // CHECK: typealias Iterator = CxxIterator // CHECK: typealias RawIterator = ConstIterator